Compare commits

..

63 Commits

Author SHA1 Message Date
Nathan Rajlich
0b60467d2f Publish Stable
- @vercel/build-utils@2.7.0
 - vercel@21.1.0
 - @vercel/client@9.0.5
 - @vercel/go@1.1.7
 - @vercel/node-bridge@1.3.2
 - @vercel/node@1.9.0
 - @vercel/python@1.2.4
 - @vercel/routing-utils@1.9.2
 - @vercel/ruby@1.2.5
2021-01-13 17:18:43 -08:00
Nathan Rajlich
4384a6104f Publish Canary
- vercel@21.0.2-canary.10
2021-01-13 11:42:54 -08:00
Nathan Rajlich
f40f95ff37 [cli] Respect the directoryListing Project setting in vc dev (#5690)
Implements the ["Directory Listing" Project setting](https://vercel.com/changelog/listing-the-content-of-directories-can-now-be-toggled) in `vercel dev`.

The Dev integration test file has been updated to allow specifying custom Project settings that will be `PATCH`'d to the project document before running `vc deploy` / `vc dev`.
2021-01-13 15:36:12 +00:00
Nathan Rajlich
90c05250b0 Publish Canary
- vercel@21.0.2-canary.9
 - @vercel/go@1.1.7-canary.1
 - @vercel/node@1.8.6-canary.5
2021-01-12 17:47:24 -08:00
Nathan Rajlich
030880fe74 [node][go] Fix order of exit code in startDevServer() error reason (#5688) 2021-01-12 17:02:55 -08:00
Nathan Rajlich
02bc88f33b [cli] Use cmd() formatting for "npm not installed" message (#5687)
* [cli] Use `cmd()` formatting for "npm not installed" message

* Fix build
2021-01-12 17:02:36 -08:00
Nathan Rajlich
38ff557cad [cli] Skip "00-list-directory" and "temporary directory listing" tests temporarily (#5689)
* [cli] Skip "now-dev-directory-listing" test temporarily

* Disable correct tests

* Fix `vc env` test
2021-01-12 17:01:56 -08:00
Nathan Rajlich
47c34842d5 Publish Canary
- vercel@21.0.2-canary.8
 - @vercel/go@1.1.7-canary.0
 - @vercel/node@1.8.6-canary.4
 - @vercel/python@1.2.4-canary.0
 - @vercel/ruby@1.2.5-canary.0
2021-01-12 14:51:30 -08:00
Nathan Rajlich
d21b215ad0 [cli] Add error link when startDevServer() throws, and make npm not installed formatting consistent (#5684) 2021-01-12 14:47:11 -08:00
Nathan Rajlich
5827737fd5 [node][go] Better startDevServer() error message (#5674)
* [go] Better `startDevServer()` error message

* [node] Better `startDevServer()` error message

Co-authored-by: Steven <steven@ceriously.com>
2021-01-11 15:55:58 -08:00
Steven
a9e078d410 [all] Rename init files to vc_ prefix (#5675)
These files show up in error logs, so its confusing to see the [legacy now terminology](https://vercel.com/blog/zeit-is-now-vercel).
2021-01-11 20:38:03 +00:00
Lee Robinson
b4d9b17fb8 Update to latest Create Next App example. (#5655) 2021-01-10 19:52:18 -06:00
Steven
02c55bf634 [cli] Remove legacy example from vc ls --help (#5651)
`vc ls --help` was still listing an example using `--all` which was removed with Now 1.0 in PR #5011.

This PR removes that legacy example.
2021-01-07 20:28:48 +00:00
Nathan Rajlich
08af15055f Publish Canary
- vercel@21.0.2-canary.7
2021-01-06 18:33:16 -08:00
Nathan Rajlich
0597aaa5e4 [cli] Print better message upon ENOENT error when invoking startDevServer() (#5643)
When `spawn()` emits an "ENOENT" error, meaning that the command was not
found, the error would only be visible if `--debug` was enabled for vc
dev.

Now we will always render any error from `startDevServer()`, as well as
special-case the "ENOENT" error informing the user to double check their
installation of the command.
2021-01-06 13:48:55 -08:00
Nathan Rajlich
176856a1ea Stop publishing legacy @now scoped packages (#5641)
These `@now` scoped packages have been published and deprecated with the following warning since May 2020:

```
DEPRECATED ⚠️  - "@now/node" is deprecated and will stop receiving updates on December 31, 2020. Please use "@vercel/node" instead.
```

Since it is now 2021, the time has come to stop publishing.

### Related Issues

[[ch3378]]
2021-01-06 01:59:25 +00:00
Nathan Rajlich
375a55ebed [next][redwood][static-build] Remove frontend runtimes (#5640) 2021-01-05 14:11:55 -08:00
JJ Kasper
7d33a05581 Publish Stable
- @vercel/next@2.7.8
2021-01-05 09:58:19 -06:00
JJ Kasper
0377c8b737 [next] Add tests for optional catch-all fallback true page params (#5247)
This adds tests for optional catch-all `fallback: true` pages to ensure the params are normalized correctly. These should be failing initially until the PR with the normalizing fix in Next.js is released to canary. 

x-ref: https://github.com/vercel/next.js/issues/17220
2021-01-05 04:59:16 +00:00
Nathan Rajlich
662d546388 Publish Canary
- @vercel/node-bridge@1.3.2-canary.1
2021-01-04 16:37:52 -08:00
Nathan Rajlich
8c514b5608 [node-bridge] Publish launcher.* files to npm (#5636) 2021-01-04 16:37:22 -08:00
Nathan Rajlich
a6ec53d9d3 Publish Canary
- vercel@21.0.2-canary.6
 - @vercel/node-bridge@1.3.2-canary.0
 - @vercel/node@1.8.6-canary.3
 - @vercel/static-build@0.18.1-canary.1
 - @vercel/redwood@0.2.1-canary.0
2021-01-04 16:27:14 -08:00
Nathan Rajlich
3ad5903f70 [node-bridge] Move launcher.ts to @vercel/node-bridge (#5634)
The `launcher.ts` file is more closely aligned to the node bridge package, as it's used by other runtimes as well.
2021-01-04 16:25:27 -08:00
Nathan Rajlich
3cf155e999 [tests] Retry fetch upon "ECONNREFUSED" (#5635)
CI failed due to this error code, which can be retried.
2021-01-04 16:06:28 -08:00
JJ Kasper
d9a298d97c Publish Canary
- @vercel/next@2.7.8-canary.0
2021-01-04 15:15:21 -06:00
JJ Kasper
487d3c8554 [next] Ensure 404 route is correct for GSP/GIP 404 (#5632)
This is a follow-up to https://github.com/vercel/vercel/pull/5618 ensuring the 404 route is pointing to the static 404 output correctly when `_app.gip` and getStaticProps in `/404.js` is used

### Related Issues

Fixes: https://github.com/vercel/next.js/issues/19849

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2021-01-04 20:53:29 +00:00
JJ Kasper
9d0bfd3656 Publish Stable
- @vercel/next@2.7.7
2021-01-02 12:24:18 -06:00
JJ Kasper
ec75333569 [next] Fix next test files lint (#5619) 2020-12-31 17:10:52 -06:00
JJ Kasper
51aab912a2 Publish Canary
- @vercel/next@2.7.7-canary.2
2020-12-31 16:01:02 -06:00
JJ Kasper
670b2653c0 [next] Ensure 404 is static with _app.GIP and 404 GSP (#5618) 2020-12-31 15:47:18 -06:00
JJ Kasper
f71686fdad Publish Canary
- vercel@21.0.2-canary.5
 - @vercel/next@2.7.7-canary.1
 - @vercel/node@1.8.6-canary.2
2020-12-29 18:26:39 -06:00
Joe Haddad
ec9c8ce150 [next] share cache between nft runs (#5614)
This should result in faster Lambda tracing, as it avoids duplicate work.
2020-12-29 19:04:47 -05:00
Steven
a2048fc6d3 [node][next] Bump nft to 0.9.5 (#5581)
This PR bumps nft from version 0.9.4 to [0.9.5](https://github.com/vercel/nft/releases/tag/0.9.5).
2020-12-28 12:23:03 -05:00
Steven
09ff9cda9f [docs] Fix error message when credentials are missing (#5590) 2020-12-21 12:12:32 -08:00
Nathan Rajlich
3a4d6f7848 Publish Canary
- vercel@21.0.2-canary.4
 - @vercel/node@1.8.6-canary.1
2020-12-21 10:11:14 -08:00
Andy
9a0d676c0d Remove "Add a new framework" CONTRIBUTING.md (#5583) 2020-12-18 12:34:50 -08:00
Nathan Rajlich
25cd7b9e6e [cli] Fix vc logs -o raw and add test (#5571)
There are events (i.e. with `type: "deployment-event"`) that do not
contain a `text` property, so check that it's a string before invoking
`.replace()` on it.

Fixes this stack trace:

```
Error! An unexpected error occurred in logs: TypeError: Cannot read property 'replace' of undefined
    at Object.printLogRaw [as raw] (/home/me/nextjs-site/node_modules/vercel/dist/index.js:203803:10)
    at printEvent (/home/me/nextjs-site/node_modules/vercel/dist/index.js:203688:35)
    at Array.forEach (<anonymous>)
    at main (/home/me/nextjs-site/node_modules/vercel/dist/index.js:203690:31)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at main (/home/me/nextjs-site/node_modules/vercel/dist/index.js:207173:16)
```
2020-12-16 16:16:36 -08:00
Steven
f926d5516c [examples] Update gatsby to use @vercel/node (#5567)
[`@now/node`](https://www.npmjs.com/package/@now/node) is deprecated in favor of [`@vercel/node`](https://www.npmjs.com/package/@vercel/node)
2020-12-14 18:29:10 -05:00
Nathan Rajlich
4603383850 [node] Add Vercel prefixed TypeScript types (#5568)
The `Now` prefixed types remain as aliases for backwards-compat purposes.

#### Tests

- [x] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
2020-12-14 18:26:22 -05:00
Nathan Rajlich
c0c57889c8 Publish Canary
- @vercel/build-utils@2.6.1-canary.2
 - vercel@21.0.2-canary.3
 - @vercel/client@9.0.5-canary.2
 - @vercel/next@2.7.7-canary.0
 - @vercel/static-build@0.18.1-canary.0
2020-12-14 09:59:43 -08:00
Nathan Rajlich
85908a0524 [build-utils][static-build][next] Force YARN_NODE_LINKER="node-modules" env var when executing yarn (#5552)
### Related Issues

* https://vercel.com/knowledge/does-vercel-support-yarn-2
* https://github.com/vercel/vercel/discussions/4223
* https://github.com/vercel/vercel/discussions/4910
* https://github.com/vercel/vercel/issues/5136
* https://github.com/vercel/vercel/issues/5280

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [x] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2020-12-14 04:27:59 +00:00
Andy Bitz
503b9a2429 Publish Canary
- @vercel/build-utils@2.6.1-canary.1
 - vercel@21.0.2-canary.2
 - @vercel/client@9.0.5-canary.1
2020-12-11 19:02:19 +01:00
Andy
eac8f32ae7 [cli][build-utils] Change type to handle builds without a src property (#5553)
* [cli][build-utils] Change type to handle builds without a `src` property

* Add test

* Update unit tests

* Update test

* Undo test changes
2020-12-11 19:00:49 +01:00
dependabot[bot]
3f76fefde6 Bump ini from 1.3.5 to 1.3.7 (#5550)
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7.
<details>
<summary>Commits</summary>
<ul>
<li><a href="c74c8af35f"><code>c74c8af</code></a> 1.3.7</li>
<li><a href="024b8b55ac"><code>024b8b5</code></a> update deps, add linting</li>
<li><a href="032fbaf5f0"><code>032fbaf</code></a> Use Object.create(null) to avoid default object property hazards</li>
<li><a href="2da90391ef"><code>2da9039</code></a> 1.3.6</li>
<li><a href="cfea636f53"><code>cfea636</code></a> better git push script, before publish instead of after</li>
<li><a href="56d2805e07"><code>56d2805</code></a> do not allow invalid hazardous string as section name</li>
<li>See full diff in <a href="https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7">compare view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a href="https://www.npmjs.com/~isaacs">isaacs</a>, a new releaser for ini since your current version.</p>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ini&package-manager=npm_and_yarn&previous-version=1.3.5&new-version=1.3.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
- `@dependabot use these labels` will set the current labels as the default for future PRs for this repo and language
- `@dependabot use these reviewers` will set the current reviewers as the default for future PRs for this repo and language
- `@dependabot use these assignees` will set the current assignees as the default for future PRs for this repo and language
- `@dependabot use this milestone` will set the current milestone as the default for future PRs for this repo and language

You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/vercel/vercel/network/alerts).

</details>
2020-12-10 22:09:11 +00:00
Steven
79ddc5746b Publish Canary
- @vercel/routing-utils@1.9.2-canary.4
2020-12-10 15:14:12 -05:00
Javi Velasco
9a14615b43 [routing-utils] More accurate schema (#5477)
This PR completes the routes schema to be more accurate for the provided type.
This will allow us to generate the type dynamically from the JSON Schema
2020-12-10 20:10:57 +00:00
JJ Kasper
6d6ccbdc25 [tests] Add retrying for GitHub actions connection error (#5533)
Follow-up to https://github.com/vercel/vercel/pull/5526 this also adds retrying for fetch connection issues in GitHub Actions to prevent false test failures.

x-ref: https://github.com/vercel/vercel/runs/1518604380
2020-12-09 21:11:01 +00:00
JJ Kasper
704424ec58 Publish Stable
- @vercel/next@2.7.6
2020-12-08 10:49:48 -06:00
JJ Kasper
c471127c69 Publish Canary
- @vercel/next@2.7.6-canary.1
2020-12-08 10:33:13 -06:00
JJ Kasper
c02dc9ac49 [next] Add public dir check and update tests (#5526)
* Add public dir check and update tests

* De-dupe test checks a bit
2020-12-08 10:25:55 -05:00
JJ Kasper
dd10e8cc77 Publish Canary
- @vercel/next@2.7.6-canary.0
 - @vercel/routing-utils@1.9.2-canary.3
2020-12-01 16:11:08 -06:00
Connor Davis
cf69c2398c [next] Add Important Headers to Avoid Users Overriding Cache Headers (#5452)
We want to ensure users cannot override Next.js' cache-control headers

### Related Issues

https://app.clubhouse.io/vercel/story/14921/add-important-headers-to-routes
https://app.clubhouse.io/vercel/story/15528/add-important-to-cache-control-headers-in-next-js

#### Tests

- [x] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR


Fixes: https://github.com/vercel/vercel/pull/5452
2020-12-01 22:08:50 +00:00
JJ Kasper
e48707571f Publish Stable
- @vercel/next@2.7.5
2020-11-30 11:06:59 -06:00
JJ Kasper
6051fe6f0c Publish Canary
- @vercel/next@2.7.5-canary.1
2020-11-30 11:00:43 -06:00
JJ Kasper
2b3ba8a14f [next] Make sure to use outputDirectory when loading prerenderManifest (#5487)
* Make sure to use outputDirectory when loading prerenderManifest

* Add test case
2020-11-30 10:57:56 -06:00
Steven
25bea3f83e Publish Canary
- vercel@21.0.2-canary.1
 - @vercel/next@2.7.5-canary.0
 - @vercel/node@1.8.6-canary.0
2020-11-24 15:24:52 -05:00
Steven
b3e1828ebe [node][next] Bump nft to 0.9.4 (#5455)
This PR bumps nft to [0.9.4](https://github.com/vercel/nft/releases/tag/0.9.4)
2020-11-24 14:32:46 -05:00
Andy Bitz
e0e2a8e87e Publish Canary
- @vercel/routing-utils@1.9.2-canary.2
2020-11-24 19:52:19 +01:00
Andy
37ec89796d [routing-utils] Handle null phase at the beginning (#5470)
* [routing-utils] Handle `null` phase at the beginning

* Add fixed list

* Add tests

* Change hit and miss order

* Put null at the start

* Undo order changes

Co-authored-by: Steven <steven@ceriously.com>
2020-11-24 19:51:06 +01:00
JJ Kasper
d3e2a4c4db Publish Stable
- @vercel/next@2.7.4
2020-11-24 11:14:03 -06:00
JJ Kasper
ece3645914 Publish Canary
- vercel@21.0.2-canary.0
 - @vercel/next@2.7.4-canary.0
2020-11-24 10:53:50 -06:00
JJ Kasper
fee386493b [next] Add handle: resource to ensure 404 page (#5454)
This adds `handle: resource` to ensure the 404 page is shown instead of a directory listing when there isn't an index route for an output directory. This also removes the `.well-known` route added in https://github.com/vercel/vercel/pull/5447 in favor of adding it in Next.js itself here https://github.com/vercel/next.js/pull/19364 and revalidate tests wait times are increased in this PR due to [this thread](https://vercel.slack.com/archives/C9MMK4674/p1605821738156200)

x-ref: https://github.com/vercel/next.js/pull/19364
Fixes: https://github.com/vercel/next.js/issues/16555
2020-11-24 16:38:43 +00:00
Luc Leray
d95ed184ab [tests] Add test for secrets decryption in vc env pull and vc dev (#5461)
* add test for env with decryptable secret

* Apply suggestions from code review

Co-authored-by: Nathan Rajlich <nathan@tootallnate.net>

Co-authored-by: Nathan Rajlich <nathan@tootallnate.net>
2020-11-23 10:38:29 +01:00
1932 changed files with 875 additions and 323068 deletions

View File

@@ -94,12 +94,3 @@ Sometimes you want to test changes to a Builder against an existing project, may
4. Run `vercel *.tgz` to upload the tarball file and get a URL
5. Edit any existing `vercel.json` project and replace `use` with the URL
6. Run `vercel` or `vercel dev` to deploy with the experimental Builder
## Add a New Framework
You can add support for a new Framework by creating a Pull Request for this repository and following the steps below:
1. Add the Framework to the `@vercel/frameworks` package: The file is located in `./packages/frameworks/frameworks.json`. You can copy the structure of an existing one and adjust the required fields. Note that the `settings` property either contains a `value` or a `placeholder`. The `value` property is used when something is not configurable, the `placeholder` is used when something is configurable and can be changed with configuration. An example would be the Output Directory for Hugo, it's `public` by default but can be changed through its config file, so we use `placeholder` with an explanation of what can be used.
2. Add an example to the `./examples` directory: The name of the directory should equal the slug of the framework used in `@vercel/frameworks`. The `.github/EXAMPLE_README_TEMPLATE.md` file can be used to create a `README.md` file for the example.
3. Update the `@vercel/static-build` package: The file `./packages/now-static-build/src/frameworks.ts` has to be extended. You can add default routes that will always be applied to projects that use this Framework or specify some paths that will be cached to speed up the build process.
4. After your Pull Request has been merged and released, other users can select the example on the Vercel dashboard and deploy it.

View File

@@ -6,5 +6,5 @@ You're running Vercel CLI in a non-terminal context and there are no credentials
#### Possible Ways to Fix It
- Specify a value for the `--token` flag (this needs to be the token of the user account as which you'd like to act). You can either get the token from the `./vercel/auth.json` file located in your user directory or [from the dashboard](https://vercel.com/account/tokens).
- Ensure that both `~/vercel/auth.json` and `~/vercel/config.json` exist
- Specify a value for the `--token` flag (this needs to be the token of the user account as which you'd like to act). You can create a new token on your [Settings page](https://vercel.com/account/tokens).
- Run `vercel login` to sign in and generate a new token

View File

@@ -1,4 +1,4 @@
import { NowRequest, NowResponse } from '@now/node';
import { NowRequest, NowResponse } from '@vercel/node';
export default (_req: NowRequest, res: NowResponse) => {
const date = new Date().toString();

View File

@@ -12,7 +12,7 @@
"react-helmet": "^5.2.0"
},
"devDependencies": {
"@now/node": "^1.3.0"
"@vercel/node": "1.8.5"
},
"scripts": {
"dev": "gatsby develop",

View File

@@ -1107,15 +1107,6 @@
"@nodelib/fs.scandir" "2.1.3"
fastq "^1.6.0"
"@now/node@^1.3.0":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@now/node/-/node-1.7.1.tgz#764a0c6bcb24967f8014c4f73ad238c292996fe3"
integrity sha512-+srVKopsVTPDR3u9eOjJryZroLTrPp8XEOuIDGBdfFcJuS7qpAomctSbfyA7WNyjC0ExtUxELqBg5sAedG5+2g==
dependencies:
"@types/node" "*"
ts-node "8.9.1"
typescript "3.9.3"
"@pieh/friendly-errors-webpack-plugin@1.7.0-chalk-2":
version "1.7.0-chalk-2"
resolved "https://registry.yarnpkg.com/@pieh/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.0-chalk-2.tgz#2e9da9d3ade9d18d013333eb408c457d04eabac0"
@@ -1409,6 +1400,15 @@
dependencies:
wonka "^4.0.14"
"@vercel/node@1.8.5":
version "1.8.5"
resolved "https://registry.yarnpkg.com/@vercel/node/-/node-1.8.5.tgz#2c8b9532f1bb25734a9964c52973386ed78022d4"
integrity sha512-1iw7FSR8Oau6vZB1MWfBnA5q2a/IqRHiSZSbt8lz0dyTF599q8pc5GcSv/TvmrYaEGzh3+N0S4cbmuMCqVlwJg==
dependencies:
"@types/node" "*"
ts-node "8.9.1"
typescript "3.9.3"
"@webassemblyjs/ast@1.9.0":
version "1.9.0"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964"

View File

@@ -14,6 +14,10 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
## Learn More
To learn more about Next.js, take a look at the following resources:

View File

@@ -8,7 +8,7 @@
"start": "next start"
},
"dependencies": {
"next": "10.0.0",
"next": "10.0.5",
"react": "17.0.1",
"react-dom": "17.0.1"
}

View File

@@ -76,7 +76,6 @@
align-items: center;
justify-content: center;
flex-wrap: wrap;
max-width: 800px;
margin-top: 3rem;
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "2.6.1-canary.0",
"version": "2.7.0",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",

View File

@@ -43,9 +43,8 @@ export function sortFiles(fileA: string, fileB: string) {
export function detectApiExtensions(builders: Builder[]): Set<string> {
return new Set<string>(
builders
.filter(
b =>
b.config && b.config.zeroConfig && b.src && b.src.startsWith('api/')
.filter((b): b is Builder & { src: string } =>
Boolean(b.config && b.config.zeroConfig && b.src?.startsWith('api/'))
)
.map(b => extname(b.src))
.filter(Boolean)
@@ -56,22 +55,28 @@ export function detectApiDirectory(builders: Builder[]): string | null {
// TODO: We eventually want to save the api directory to
// builder.config.apiDirectory so it is only detected once
const found = builders.some(
b => b.config && b.config.zeroConfig && b.src.startsWith('api/')
b => b.config && b.config.zeroConfig && b.src?.startsWith('api/')
);
return found ? 'api' : null;
}
// TODO: Replace this function with `config.outputDirectory`
function getPublicBuilder(builders: Builder[]): Builder | null {
const builder = builders.find(
builder =>
function getPublicBuilder(
builders: Builder[]
): (Builder & { src: string }) | null {
for (const builder of builders) {
if (
typeof builder.src === 'string' &&
isOfficialRuntime('static', builder.use) &&
/^.*\/\*\*\/\*$/.test(builder.src) &&
builder.config &&
builder.config.zeroConfig === true
);
) {
return builder as Builder & { src: string };
}
}
return builder || null;
return null;
}
export function detectOutputDirectory(builders: Builder[]): string | null {
// TODO: We eventually want to save the output directory to
@@ -361,7 +366,7 @@ function maybeGetApiBuilder(
return null;
}
const match = apiMatches.find(({ src }) => {
const match = apiMatches.find(({ src = '**' }) => {
return src === fileName || minimatch(fileName, src);
});

View File

@@ -292,6 +292,11 @@ export async function runNpmInstall(
opts.prettyCommand = 'yarn install';
command = 'yarn';
commandArgs = ['install', ...args];
// Yarn v2 PnP mode may be activated, so force "node-modules" linker style
if (!env.YARN_NODE_LINKER) {
env.YARN_NODE_LINKER = 'node-modules';
}
}
if (process.env.NPM_ONLY_PRODUCTION) {
@@ -388,10 +393,17 @@ export async function runPackageJsonScript(
prettyCommand,
});
} else {
// Yarn v2 PnP mode may be activated, so force "node-modules" linker style
const env: typeof process.env = { ...spawnOpts?.env };
if (!env.YARN_NODE_LINKER) {
env.YARN_NODE_LINKER = 'node-modules';
}
const prettyCommand = `yarn run ${scriptName}`;
console.log(`Running "${prettyCommand}"`);
await spawnAsync('yarn', ['run', scriptName], {
...spawnOpts,
env,
cwd: destPath,
prettyCommand,
});

View File

@@ -336,7 +336,7 @@ export interface NodeVersion {
export interface Builder {
use: string;
src: string;
src?: string;
config?: Config;
}

View File

@@ -0,0 +1,5 @@
const { camelCase } = require('camel-case');
module.exports = (req, res) => {
res.end(camelCase('camel-case module is working'));
};

View File

@@ -1,5 +0,0 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@vercel/static-build" }],
"probes": [{ "path": "/", "mustContain": "Svelte app" }]
}

View File

@@ -16,6 +16,7 @@
"svelte": "^3.0.0"
},
"dependencies": {
"camel-case": "^4.1.2",
"sirv-cli": "^0.4.4"
}
}

View File

@@ -0,0 +1,11 @@
{
"version": 2,
"builds": [
{ "src": "package.json", "use": "@vercel/static-build" },
{ "src": "api/index.js", "use": "@vercel/node" }
],
"probes": [
{ "path": "/", "mustContain": "Svelte app" },
{ "path": "/api", "mustContain": "camelCaseModuleIsWorking" }
]
}

View File

@@ -270,6 +270,16 @@ __metadata:
languageName: node
linkType: hard
"camel-case@npm:^4.1.2":
version: 4.1.2
resolution: "camel-case@npm:4.1.2"
dependencies:
pascal-case: ^3.1.2
tslib: ^2.0.3
checksum: 3/0b8dcfb424c9497e45984b88ef005c66bdf8e877e36365aedfc3cf73182684fde5a14cf2c526579c0351a5f27dc39a00f1edecc25d43606075fea948c504e37f
languageName: node
linkType: hard
"caseless@npm:~0.12.0":
version: 0.12.0
resolution: "caseless@npm:0.12.0"
@@ -850,6 +860,15 @@ fsevents@~2.1.2:
languageName: node
linkType: hard
"lower-case@npm:^2.0.2":
version: 2.0.2
resolution: "lower-case@npm:2.0.2"
dependencies:
tslib: ^2.0.3
checksum: 3/aabaca9cef65f7564a1005b625664527e4d169e363101e65773f8f6ff2fdcf09884a3bc02990cd7a62cf05f3538114af25ee7bef553f1ca3208c8a77ac75cbfa
languageName: node
linkType: hard
"magic-string@npm:^0.25.2":
version: 0.25.7
resolution: "magic-string@npm:0.25.7"
@@ -944,6 +963,16 @@ fsevents@~2.1.2:
languageName: node
linkType: hard
"no-case@npm:^3.0.4":
version: 3.0.4
resolution: "no-case@npm:3.0.4"
dependencies:
lower-case: ^2.0.2
tslib: ^2.0.3
checksum: 3/84db4909caec37504c6655f995a004067f8733be8cd8d849f1578661b60a1685e086325fa4e1a5e8ce94e7416c1d0f037e2a00f635a14457183de80ab4fc7612
languageName: node
linkType: hard
"node-gyp@npm:latest":
version: 6.1.0
resolution: "node-gyp@npm:6.1.0"
@@ -1057,6 +1086,16 @@ fsevents@~2.1.2:
languageName: node
linkType: hard
"pascal-case@npm:^3.1.2":
version: 3.1.2
resolution: "pascal-case@npm:3.1.2"
dependencies:
no-case: ^3.0.4
tslib: ^2.0.3
checksum: 3/31708cecab221482edc81e2bd9b9d8282d72d4f1443b31f39725aa23768c5e42d93c4c014f1bc90f7f074e2a70d5091e4892ea370e550affc9ccf1d33c900bcd
languageName: node
linkType: hard
"path-is-absolute@npm:^1.0.0":
version: 1.0.1
resolution: "path-is-absolute@npm:1.0.1"
@@ -1447,6 +1486,7 @@ fsevents@~2.1.2:
dependencies:
"@rollup/plugin-commonjs": ^12.0.0
"@rollup/plugin-node-resolve": ^8.0.0
camel-case: ^4.1.2
rollup: ^2.3.4
rollup-plugin-livereload: ^1.0.0
rollup-plugin-svelte: ^5.0.3
@@ -1517,6 +1557,13 @@ fsevents@~2.1.2:
languageName: node
linkType: hard
"tslib@npm:^2.0.3":
version: 2.0.3
resolution: "tslib@npm:2.0.3"
checksum: 3/447bfca5deaa157806c3f77eaba74d05dd0b38b014e47ce79d98b5c77ce7d91b00a687ba13ca1b5a74d35ca1098ac7a072c0a97fad06f0266612f2a03a6c8e8f
languageName: node
linkType: hard
"tunnel-agent@npm:^0.6.0":
version: 0.6.0
resolution: "tunnel-agent@npm:0.6.0"

View File

@@ -182,7 +182,7 @@ describe('Test `detectBuilders`', () => {
const { builders } = await detectBuilders(files);
expect(builders!.length).toBe(7);
expect(builders!.some(b => b.src.endsWith('_test.go'))).toBe(false);
expect(builders!.some(b => b.src!.endsWith('_test.go'))).toBe(false);
});
it('just public', async () => {
@@ -1341,7 +1341,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
featHandleMiss,
});
expect(builders!.length).toBe(7);
expect(builders!.some(b => b.src.endsWith('_test.go'))).toBe(false);
expect(builders!.some(b => b.src!.endsWith('_test.go'))).toBe(false);
expect(errorRoutes!.length).toBe(1);
expect((errorRoutes![0] as Source).status).toBe(404);
});

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "21.0.1",
"version": "21.1.0",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -61,11 +61,11 @@
"node": ">= 10"
},
"dependencies": {
"@vercel/build-utils": "2.6.1-canary.0",
"@vercel/go": "1.1.6",
"@vercel/node": "1.8.5",
"@vercel/python": "1.2.3",
"@vercel/ruby": "1.2.4",
"@vercel/build-utils": "2.7.0",
"@vercel/go": "1.1.7",
"@vercel/node": "1.9.0",
"@vercel/python": "1.2.4",
"@vercel/ruby": "1.2.5",
"update-notifier": "4.1.0"
},
"devDependencies": {

View File

@@ -51,12 +51,6 @@ const help = () => {
${chalk.cyan(`$ ${getPkgName()} ls my-app`)}
${chalk.gray(
''
)} List all deployments and all instances for the app ${chalk.dim('`my-app`')}
${chalk.cyan(`$ ${getPkgName()} ls my-app --all`)}
${chalk.gray('')} Filter deployments by metadata
${chalk.cyan(`$ ${getPkgName()} ls -m key1=value1 -m key2=value2`)}

View File

@@ -324,7 +324,7 @@ function printLogRaw(log) {
if (log.object) {
console.log(log.object);
} else {
} else if (typeof log.text === 'string') {
console.log(
log.text
.replace(/\n$/, '')

View File

@@ -231,6 +231,7 @@ export interface ProjectSettings {
outputDirectory?: string | null;
rootDirectory?: string | null;
autoExposeSystemEnvs?: boolean;
directoryListing?: boolean;
}
export interface Project extends ProjectSettings {

View File

@@ -1,3 +1,4 @@
import chalk from 'chalk';
import execa from 'execa';
import semver from 'semver';
import npa from 'npm-package-arg';
@@ -8,9 +9,10 @@ import { mkdirp, readJSON, writeJSON } from 'fs-extra';
import { NowBuildError, PackageJson } from '@vercel/build-utils';
import cliPkg from '../pkg';
import { NoBuilderCacheError } from '../errors-ts';
import cmd from '../output/cmd';
import { Output } from '../output';
import { getDistTag } from '../get-dist-tag';
import { NoBuilderCacheError } from '../errors-ts';
import * as staticBuilder from './static-builder';
import { BuilderWithPackage } from './types';
@@ -261,7 +263,9 @@ async function npmInstall(
throw new NowBuildError({
message:
(result as any).code === 'ENOENT'
? '`npm` is not installed'
? `Command not found: ${chalk.cyan(
'npm'
)}\nPlease ensure that ${cmd('npm')} is properly installed`
: 'Failed to install `vercel dev` dependencies',
code: 'NPM_INSTALL_ERROR',
link: 'https://vercel.link/npm-install-failed-dev',

View File

@@ -402,7 +402,7 @@ export async function getBuildMatches(
const builds = nowConfig.builds || [{ src: '**', use: '@vercel/static' }];
for (const buildConfig of builds) {
let { src, use } = buildConfig;
let { src = '**', use } = buildConfig;
if (!use) {
continue;

View File

@@ -43,6 +43,7 @@ import {
} from '@vercel/build-utils';
import _frameworks, { Framework } from '@vercel/frameworks';
import cmd from '../output/cmd';
import link from '../output/link';
import { Output } from '../output';
import { relative } from '../path-helpers';
@@ -650,7 +651,7 @@ export default class DevServer {
const cloudEnv = exposeSystemEnvs(
this.projectEnvs || [],
this.systemEnvValues || [],
this.projectSettings && this.projectSettings.autoExposeSystemEnvs,
this.projectSettings?.autoExposeSystemEnvs,
new URL(this.address).host
);
@@ -667,7 +668,7 @@ export default class DevServer {
// mirror how VERCEL_REGION is injected in prod/preview
// only inject in `runEnvs`, because `allEnvs` is exposed to dev command
// and should not contain VERCEL_REGION
if (this.projectSettings && this.projectSettings.autoExposeSystemEnvs) {
if (this.projectSettings?.autoExposeSystemEnvs) {
runEnv['VERCEL_REGION'] = 'dev1';
}
@@ -886,7 +887,7 @@ export default class DevServer {
})
.catch(err => {
this.updateBuildersPromise = null;
this.output.error(`Failed to update builders: ${err.message}`);
this.output.prettyError(err);
this.output.debug(err.stack);
});
}, ms('30s'));
@@ -1681,7 +1682,16 @@ export default class DevServer {
// `startDevServer()` threw an error. Most likely this means the dev
// server process exited before sending the port information message
// (missing dependency at runtime, for example).
debug(`Error starting "${builderPkg.name}" dev server: ${err}`);
if (err.code === 'ENOENT') {
err.message = `Command not found: ${chalk.cyan(
err.path,
...err.spawnargs
)}\nPlease ensure that ${cmd(err.path)} is properly installed`;
err.link = 'https://vercel.link/command-not-found';
}
this.output.prettyError(err);
await this.sendError(
req,
res,
@@ -1894,6 +1904,12 @@ export default class DevServer {
requestPath: string,
nowRequestId: string
): boolean {
// If the "directory listing" feature is disabled in the
// Project's settings, then don't render the directory listing
if (this.projectSettings?.directoryListing === false) {
return false;
}
let prefix = requestPath;
if (prefix.length > 0 && !prefix.endsWith('/')) {
prefix += '/';

View File

@@ -50,6 +50,7 @@ export interface EnvConfigs {
export interface BuildMatch extends BuildConfig {
entrypoint: string;
src: string;
builderWithPkg: BuilderWithPackage;
buildOutput: BuilderOutputs;
buildResults: Map<string | null, BuildResult>;

View File

@@ -0,0 +1,4 @@
package.json
yarn.lock
.now
.vercel

View File

@@ -0,0 +1 @@
hello:index.txt

View File

@@ -0,0 +1,7 @@
{
"builds": [
{
"use": "@vercel/static"
}
]
}

View File

@@ -232,7 +232,7 @@ async function testFixture(directory, opts = {}, args = []) {
function testFixtureStdio(
directory,
fn,
{ expectedCode = 0, skipDeploy, isExample } = {}
{ expectedCode = 0, skipDeploy, isExample, projectSettings } = {}
) {
return async t => {
const nodeMajor = Number(process.versions.node.split('.')[0]);
@@ -249,24 +249,51 @@ function testFixtureStdio(
// Deploy fixture and link project
if (!skipDeploy) {
const project = join(cwd, '.vercel', 'project.json');
if (await fs.exists(project)) {
await fs.unlink(project);
}
const projectJsonPath = join(cwd, '.vercel', 'project.json');
await fs.remove(projectJsonPath);
const gitignore = join(cwd, '.gitignore');
const gitignoreOrig = await fs.exists(gitignore);
let { stdout, stderr, exitCode } = await execa(
binaryPath,
['-t', token, '--confirm', '--public', '--no-clipboard', '--debug'],
{ cwd, reject: false }
);
console.log({ stdout, stderr, exitCode });
if (!gitignoreOrig && (await fs.exists(gitignore))) {
await fs.unlink(gitignore);
}
t.is(exitCode, expectedCode);
if (expectedCode === 0) {
deploymentUrl = new URL(stdout).host;
const hasGitignore = await fs.exists(gitignore);
try {
// Run `vc link`
const { exitCode: linkExitCode } = await execa(
binaryPath,
['-t', token, 'link', '--confirm'],
{ cwd, stdio: 'inherit', reject: false }
);
t.is(linkExitCode, 0);
// Patch the project with any non-default properties
if (projectSettings) {
const { projectId } = await fs.readJson(projectJsonPath);
const res = await fetch(
`https://api.vercel.com/v2/projects/${projectId}`,
{
method: 'PATCH',
headers: {
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(projectSettings),
}
);
t.is(res.status, 200);
}
// Run `vc deploy`
let { exitCode, stdout } = await execa(
binaryPath,
['-t', token, 'deploy', '--public', '--no-clipboard', '--debug'],
{ cwd, stdio: ['ignore', 'pipe', 'inherit'], reject: false }
);
console.log({ exitCode, stdout });
t.is(exitCode, expectedCode);
if (expectedCode === 0) {
deploymentUrl = new URL(stdout).host;
}
} finally {
if (!hasGitignore) {
await fs.remove(gitignore);
}
}
}
@@ -622,14 +649,6 @@ test(
});
})
);
/*
test(
'[vercel dev] displays directory listing after miss',
testFixtureStdio('handle-miss-display-dir-list', async (testPath) => {
await testPath(404, '/post', /one.html/m);
})
);
*/
test(
'[vercel dev] does not display directory listing after 404',
@@ -1056,12 +1075,16 @@ test(
test(
'[vercel dev] 00-list-directory',
testFixtureStdio('00-list-directory', async testPath => {
await testPath(200, '/', /Files within/m);
await testPath(200, '/', /test[0-3]\.txt/m);
await testPath(200, '/', /\.well-known/m);
await testPath(200, '/.well-known/keybase.txt', 'proof goes here');
})
testFixtureStdio(
'00-list-directory',
async testPath => {
await testPath(200, '/', /Files within/m);
await testPath(200, '/', /test[0-3]\.txt/m);
await testPath(200, '/', /\.well-known/m);
await testPath(200, '/.well-known/keybase.txt', 'proof goes here');
},
{ projectSettings: { directoryListing: true } }
)
);
test(
@@ -1756,3 +1779,11 @@ test(
});
})
);
test(
'[vercel dev] Do not fail if `src` is missing',
testFixtureStdio('missing-src-property', async testPath => {
await testPath(200, '/', /hello:index.txt/m);
await testPath(404, '/i-do-not-exist');
})
);

View File

@@ -602,6 +602,41 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
t.regex(systemEnvs[0], /Production, Preview, Development/gm);
}
// we create a "legacy" env variable that contains a decryptable secret
// to check that vc env pull and vc dev work correctly with decryptable secrets
async function createEnvWithDecryptableSecret() {
console.log('creating an env variable with a decryptable secret');
const name = `my-secret${Math.floor(Math.random() * 10000)}`;
const res = await apiFetch('/v2/now/secrets', {
method: 'POST',
body: JSON.stringify({
name,
value: 'decryptable value',
decryptable: true,
}),
});
t.is(res.status, 200);
const json = await res.json();
const link = require(path.join(target, '.vercel/project.json'));
const resEnv = await apiFetch(`/v4/projects/${link.projectId}/env`, {
method: 'POST',
body: JSON.stringify({
key: 'MY_DECRYPTABLE_SECRET_ENV',
value: json.uid,
target: ['development'],
type: 'secret',
}),
});
t.is(resEnv.status, 200);
}
async function nowEnvPull() {
const { exitCode, stderr, stdout } = await execa(
binaryPath,
@@ -622,6 +657,7 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
t.true(lines.has('MY_PLAINTEXT_ENV_VAR="my plaintext value"'));
t.true(lines.has('MY_STDIN_VAR="{"expect":"quotes"}"'));
t.true(lines.has('NEXT_PUBLIC_VERCEL_URL=""'));
t.true(lines.has('MY_DECRYPTABLE_SECRET_ENV="decryptable value"'));
}
async function nowEnvPullOverwrite() {
@@ -711,6 +747,7 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
t.is(apiJson['MY_PLAINTEXT_ENV_VAR'], 'my plaintext value');
t.is(apiJson['NEXT_PUBLIC_VERCEL_URL'], '');
t.is(apiJson['MY_DECRYPTABLE_SECRET_ENV'], 'decryptable value');
const homeUrl = localhost[0];
@@ -718,6 +755,7 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
const homeJson = await homeRes.json();
t.is(homeJson['MY_PLAINTEXT_ENV_VAR'], 'my plaintext value');
t.is(homeJson['NEXT_PUBLIC_VERCEL_URL'], '');
t.is(homeJson['MY_DECRYPTABLE_SECRET_ENV'], 'decryptable value');
vc.kill('SIGTERM', { forceKillAfterTimeout: 2000 });
@@ -752,6 +790,7 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
t.is(apiJson['NEXT_PUBLIC_VERCEL_URL'], localhostNoProtocol);
t.is(apiJson['MY_PLAINTEXT_ENV_VAR'], 'my plaintext value');
t.is(apiJson['MY_STDIN_VAR'], '{"expect":"quotes"}');
t.is(apiJson['MY_DECRYPTABLE_SECRET_ENV'], 'decryptable value');
const homeUrl = localhost[0];
const homeRes = await fetch(homeUrl);
@@ -759,10 +798,11 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
t.is(homeJson['MY_PLAINTEXT_ENV_VAR'], 'my plaintext value');
t.is(homeJson['NEXT_PUBLIC_VERCEL_URL'], localhostNoProtocol);
t.is(homeJson['MY_STDIN_VAR'], '{"expect":"quotes"}');
t.is(homeJson['MY_DECRYPTABLE_SECRET_ENV'], 'decryptable value');
// system env vars are not automatically exposed
t.is(apiJson['VERCEL'], undefined);
t.is(homeJson['VERCEL'], undefined);
// system env vars are automatically exposed
t.is(apiJson['VERCEL'], '1');
t.is(homeJson['VERCEL'], '1');
vc.kill('SIGTERM', { forceKillAfterTimeout: 2000 });
@@ -916,6 +956,28 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
);
t.is(exitCode2, 0, formatOutput({ stderr2, stdout2 }));
const {
exitCode: exitCode3,
stderr: stderr3,
stdout: stdout3,
} = await execa(
binaryPath,
[
'env',
'rm',
'MY_DECRYPTABLE_SECRET_ENV',
'development',
'-y',
...defaultArgs,
],
{
reject: false,
cwd: target,
}
);
t.is(exitCode3, 0, formatOutput({ stderr3, stdout3 }));
}
async function nowEnvRemoveWithNameOnly() {
@@ -948,6 +1010,7 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
await nowEnvAddFromStdin();
await nowEnvAddSystemEnv();
await nowEnvLsIncludesVar();
await createEnvWithDecryptableSecret();
await nowEnvPull();
await nowEnvPullOverwrite();
await nowEnvPullConfirm();
@@ -1503,7 +1566,7 @@ test('ensure we render a warning for deployments with no files', async t => {
t.is(res.status, 404);
});
test('output logs of a 2.0 deployment', async t => {
test('output logs with "short" output', async t => {
const { stderr, stdout, exitCode } = await execa(
binaryPath,
['logs', context.deployment, ...defaultArgs],
@@ -1520,13 +1583,21 @@ test('output logs of a 2.0 deployment', async t => {
stderr.includes(`Fetched deployment "${context.deployment}"`),
formatOutput({ stderr, stdout })
);
// "short" format includes timestamps
t.truthy(
stdout.match(
/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/
)
);
t.is(exitCode, 0);
});
test('output logs of a 2.0 deployment without annotate', async t => {
test('output logs with "raw" output', async t => {
const { stderr, stdout, exitCode } = await execa(
binaryPath,
['logs', context.deployment, ...defaultArgs],
['logs', context.deployment, ...defaultArgs, '--output', 'raw'],
{
reject: false,
}
@@ -1536,12 +1607,19 @@ test('output logs of a 2.0 deployment without annotate', async t => {
console.log(stdout);
console.log(exitCode);
t.true(!stderr.includes('[now-builder-debug]'));
t.true(!stderr.includes('START RequestId'));
t.true(!stderr.includes('END RequestId'));
t.true(!stderr.includes('REPORT RequestId'));
t.true(!stderr.includes('Init Duration'));
t.true(!stderr.includes('XRAY TraceId'));
t.true(
stderr.includes(`Fetched deployment "${context.deployment}"`),
formatOutput({ stderr, stdout })
);
// "raw" format does not include timestamps
t.is(
null,
stdout.match(
/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/
)
);
t.is(exitCode, 0);
});

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "9.0.5-canary.0",
"version": "9.0.5",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -37,7 +37,7 @@
]
},
"dependencies": {
"@vercel/build-utils": "2.6.1-canary.0",
"@vercel/build-utils": "2.7.0",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",
"async-sema": "3.0.0",

View File

@@ -285,8 +285,8 @@ Learn more: https://vercel.com/docs/runtimes#official-runtimes/go
}
const mainModGoContents = modMainGoContents
.replace('__NOW_HANDLER_PACKAGE_NAME', goPackageName)
.replace('__NOW_HANDLER_FUNC_NAME', goFuncName);
.replace('__VC_HANDLER_PACKAGE_NAME', goPackageName)
.replace('__VC_HANDLER_FUNC_NAME', goFuncName);
if (isGoModExist && isGoModInRootDir) {
debug('[mod-root] Write main file to ' + downloadPath);
@@ -405,13 +405,13 @@ Learn more: https://vercel.com/docs/runtimes#official-runtimes/go
'utf8'
);
const mainGoContents = origianlMainGoContents.replace(
'__NOW_HANDLER_FUNC_NAME',
'__VC_HANDLER_FUNC_NAME',
handlerFunctionName
);
// in order to allow the user to have `main.go`,
// we need our `main.go` to be called something else
const mainGoFileName = 'main__now__go__.go';
const mainGoFileName = 'main__vc__go__.go';
// Go doesn't like to build files in different directories,
// so now we place `main.go` together with the user code
@@ -580,9 +580,9 @@ Learn more: https://vercel.com/docs/runtimes#official-runtimes/go`
};
} else if (Array.isArray(result)) {
// Got "exit" event from child process
throw new Error(
`Failed to start dev server for "${entrypointWithExt}" (code=${result[0]}, signal=${result[1]})`
);
const [exitCode, signal] = result;
const reason = signal ? `"${signal}" signal` : `exit code ${exitCode}`;
throw new Error(`\`go run ${entrypointWithExt}\` failed with ${reason}`);
} else {
throw new Error(`Unexpected result type: ${typeof result}`);
}

View File

@@ -6,5 +6,5 @@ import (
)
func main() {
vc.Start(http.HandlerFunc(__NOW_HANDLER_FUNC_NAME))
vc.Start(http.HandlerFunc(__VC_HANDLER_FUNC_NAME))
}

View File

@@ -1,12 +1,12 @@
package main
import (
"__NOW_HANDLER_PACKAGE_NAME"
"__VC_HANDLER_PACKAGE_NAME"
"net/http"
vc "github.com/vercel/go-bridge/go/bridge"
)
func main() {
vc.Start(http.HandlerFunc(__NOW_HANDLER_FUNC_NAME))
vc.Start(http.HandlerFunc(__VC_HANDLER_FUNC_NAME))
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/go",
"version": "1.1.6",
"version": "1.1.7",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",

View File

@@ -1,2 +0,0 @@
/dist
/src/now__bridge.ts

View File

@@ -1,16 +0,0 @@
#!/bin/bash
set -euo pipefail
bridge_defs="$(dirname $(pwd))/now-node-bridge/src/bridge.ts"
cp -v "$bridge_defs" src/now__bridge.ts
tsc
ncc build src/dev-server.ts -e @vercel/build-utils -e @now/build-utils -o dist/dev
mv dist/dev/index.js dist/dev-server.js
rm -rf dist/dev
ncc build src/index.ts -e @vercel/build-utils -e @now/build-utils -o dist/main
mv dist/main/index.js dist/index.js
rm -rf dist/main

View File

@@ -1,43 +0,0 @@
{
"name": "@vercel/next",
"version": "2.7.3",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
"scripts": {
"build": "./build.sh",
"test-integration-once": "jest --env node --verbose --runInBand --bail",
"prepublishOnly": "./build.sh"
},
"repository": {
"type": "git",
"url": "https://github.com/vercel/vercel.git",
"directory": "packages/now-next"
},
"files": [
"dist"
],
"devDependencies": {
"@types/aws-lambda": "8.10.19",
"@types/buffer-crc32": "0.2.0",
"@types/find-up": "4.0.0",
"@types/fs-extra": "8.0.0",
"@types/next-server": "8.0.0",
"@types/resolve-from": "5.0.1",
"@types/semver": "6.0.0",
"@types/yazl": "2.4.1",
"@vercel/nft": "0.9.2",
"async-sema": "3.0.1",
"buffer-crc32": "0.2.13",
"escape-string-regexp": "2.0.0",
"execa": "2.0.4",
"find-up": "4.1.0",
"fs-extra": "7.0.0",
"get-port": "5.0.0",
"resolve-from": "5.0.0",
"semver": "6.1.1",
"set-cookie-parser": "2.4.6",
"typescript": "3.9.3",
"yazl": "https://github.com/ijjk/yazl#70949c55b482647669ce37023017b1514c42b33c"
}
}

View File

@@ -1,10 +0,0 @@
let buildUtils: typeof import('@vercel/build-utils');
try {
buildUtils = require('@vercel/build-utils');
} catch (e) {
// Fallback for older CLI versions
buildUtils = require('@now/build-utils');
}
export default buildUtils;

View File

@@ -1,90 +0,0 @@
import fs from 'fs-extra';
import path from 'path';
import semver from 'semver';
import { ExperimentalTraceVersion } from './utils';
function getCustomData(importName: string, target: string) {
return `
module.exports = function(...args) {
let original = require('./${importName}');
const finalConfig = {};
const target = { target: '${target}' };
if (typeof original === 'function' && original.constructor.name === 'AsyncFunction') {
// AsyncFunctions will become promises
original = original(...args);
}
if (original instanceof Promise) {
// Special case for promises, as it's currently not supported
// and will just error later on
return original
.then((orignalConfig) => Object.assign(finalConfig, orignalConfig))
.then((config) => Object.assign(config, target));
} else if (typeof original === 'function') {
Object.assign(finalConfig, original(...args));
} else if (typeof original === 'object') {
Object.assign(finalConfig, original);
}
Object.assign(finalConfig, target);
return finalConfig;
}
`.trim();
}
function getDefaultData(target: string) {
return `module.exports = { target: '${target}' };`;
}
export default async function createServerlessConfig(
workPath: string,
entryPath: string,
nextVersion: string | undefined
) {
let target = 'serverless';
if (nextVersion) {
try {
if (semver.gte(nextVersion, ExperimentalTraceVersion)) {
target = 'experimental-serverless-trace';
}
} catch (
_ignored
// eslint-disable-next-line
) {}
}
const primaryConfigPath = path.join(entryPath, 'next.config.js');
const secondaryConfigPath = path.join(workPath, 'next.config.js');
const backupConfigName = `next.config.__vercel_builder_backup__.js`;
const hasPrimaryConfig = fs.existsSync(primaryConfigPath);
const hasSecondaryConfig = fs.existsSync(secondaryConfigPath);
let configPath: string;
let backupConfigPath: string;
if (hasPrimaryConfig) {
// Prefer primary path
configPath = primaryConfigPath;
backupConfigPath = path.join(entryPath, backupConfigName);
} else if (hasSecondaryConfig) {
// Work with secondary path (some monorepo setups)
configPath = secondaryConfigPath;
backupConfigPath = path.join(workPath, backupConfigName);
} else {
// Default to primary path for creation
configPath = primaryConfigPath;
backupConfigPath = path.join(entryPath, backupConfigName);
}
if (fs.existsSync(configPath)) {
await fs.rename(configPath, backupConfigPath);
await fs.writeFile(configPath, getCustomData(backupConfigName, target));
} else {
await fs.writeFile(configPath, getDefaultData(target));
}
}

View File

@@ -1,32 +0,0 @@
import resolveFrom from 'resolve-from';
import { parse } from 'url';
import getPort from 'get-port';
import { createServer } from 'http';
import { syncEnvVars } from './utils';
process.on('unhandledRejection', err => {
console.error('Exiting builder due to build error:');
console.error(err);
process.exit(1);
});
process.once('message', async ({ dir, runtimeEnv }) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const next = require(resolveFrom(dir, 'next'));
const app = next({ dev: true, dir });
const handler = app.getRequestHandler();
const [openPort] = await Promise.all([getPort(), app.prepare()]);
const url = `http://localhost:${openPort}`;
syncEnvVars(process.env, process.env, runtimeEnv);
createServer((req, res) => {
const parsedUrl = parse(req.url || '', true);
handler(req, res, parsedUrl);
}).listen(openPort, () => {
if (process.send) {
process.send(url);
}
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +0,0 @@
import { Server } from 'http';
import next from 'next-server';
import url from 'url';
import { Bridge } from './now__bridge';
if (!process.env.NODE_ENV) {
const region = process.env.VERCEL_REGION || process.env.NOW_REGION;
process.env.NODE_ENV = region === 'dev1' ? 'development' : 'production';
}
const app = next({});
const server = new Server((req, res) => {
const parsedUrl = url.parse(req.url || '', true);
app.render(req, res, 'PATHNAME_PLACEHOLDER', parsedUrl.query, parsedUrl);
});
const bridge = new Bridge(server);
bridge.listen();
exports.launcher = bridge.launcher;

View File

@@ -1,336 +0,0 @@
export default [
'0.1.0',
'0.1.1',
'0.2.0',
'0.2.1',
'0.2.2',
'0.2.3',
'0.2.4',
'0.2.5',
'0.2.6',
'0.2.7',
'0.2.8',
'0.2.9',
'0.2.10',
'0.2.11',
'0.2.12',
'0.2.13',
'0.2.14',
'0.3.0',
'0.3.1',
'0.3.2',
'0.3.3',
'0.4.0',
'0.4.1',
'0.9.9',
'0.9.10',
'0.9.11',
'1.0.0',
'1.0.1',
'1.0.2',
'1.1.0',
'1.1.1',
'1.1.2',
'1.2.0',
'1.2.1',
'1.2.2',
'1.2.3',
'2.0.0-beta.0',
'2.0.0-beta.1',
'2.0.0-beta.2',
'2.0.0-beta.3',
'2.0.0-beta.4',
'2.0.0-beta.5',
'2.0.0-beta.6',
'2.0.0-beta.7',
'2.0.0-beta.8',
'2.0.0-beta.9',
'2.0.0-beta.10',
'2.0.0-beta.11',
'2.0.0-beta.12',
'2.0.0-beta.13',
'2.0.0-beta.14',
'2.0.0-beta.15',
'2.0.0-beta.16',
'2.0.0-beta.17',
'2.0.0-beta.18',
'2.0.0-beta.19',
'2.0.0-beta.20',
'2.0.0-beta.21',
'2.0.0-beta.22',
'2.0.0-beta.23',
'2.0.0-beta.24',
'2.0.0-beta.25',
'2.0.0-beta.26',
'2.0.0-beta.27',
'2.0.0-beta.28',
'2.0.0-beta.29',
'2.0.0-beta.30',
'2.0.0-beta.31',
'2.0.0-beta.32',
'2.0.0-beta.33',
'2.0.0-beta.34',
'2.0.0-beta.35',
'2.0.0-beta.36',
'2.0.0-beta.37',
'2.0.0-beta.38',
'2.0.0-beta.39',
'2.0.0-beta.40',
'2.0.0-beta.41',
'2.0.0-beta.42',
'2.0.0',
'2.0.1',
'2.1.0',
'2.1.1',
'2.2.0',
'2.3.0-alpha1',
'2.3.0',
'2.3.1',
'2.4.0',
'2.4.1',
'2.4.2',
'2.4.3',
'2.4.4',
'2.4.5',
'2.4.6',
'2.4.7',
'2.4.8',
'2.4.9',
'3.0.0-beta1',
'3.0.0-beta10',
'3.0.0-beta11',
'3.0.0-beta12',
'3.0.0-beta13',
'3.0.0-beta14',
'3.0.0-beta15',
'3.0.0-beta16',
'3.0.0-beta2',
'3.0.0-beta3',
'3.0.0-beta4',
'3.0.0-beta5',
'3.0.0-beta6',
'3.0.0-beta7',
'3.0.0-beta8',
'3.0.0-beta9',
'3.0.1-beta.1',
'3.0.1-beta.2',
'3.0.1-beta.3',
'3.0.1-beta.4',
'3.0.1-beta.5',
'3.0.1-beta.6',
'3.0.1-beta.7',
'3.0.1-beta.8',
'3.0.1-beta.9',
'3.0.1-beta.10',
'3.0.1-beta.11',
'3.0.1-beta.12',
'3.0.1-beta.13',
'3.0.1-beta.14',
'3.0.1-beta.15',
'3.0.1-beta.16',
'3.0.1-beta.17',
'3.0.1-beta.18',
'3.0.1-beta.19',
'3.0.1-beta.20',
'3.0.1-beta.21',
'3.0.1',
'3.0.2',
'3.0.3',
'3.0.4',
'3.0.5',
'3.0.6',
'3.1.0',
'3.2.0',
'3.2.1',
'3.2.2',
'3.2.3',
'4.0.0-beta.1',
'4.0.0-beta.2',
'4.0.0-beta.3',
'4.0.0-beta.4',
'4.0.0-beta.5',
'4.0.0-beta.6',
'4.0.0',
'4.0.1',
'4.0.2',
'4.0.3',
'4.0.4',
'4.0.5',
'4.1.0',
'4.1.1',
'4.1.2',
'4.1.3',
'4.1.4-canary.1',
'4.1.4-canary.2',
'4.1.4',
'4.2.0-canary.1',
'4.2.0-zones.2',
'4.2.0',
'4.2.1',
'4.2.2',
'4.2.3',
'4.3.0-canary.1',
'4.3.0-universal-alpha.1',
'4.3.0-universal-alpha.2',
'4.3.0-universal-alpha.3',
'4.3.0-universal-alpha.4',
'4.3.0-zones.1',
'4.4.0-canary.2',
'4.4.0-canary.3',
'5.0.0-universal-alpha.1',
'5.0.0-universal-alpha.2',
'5.0.0-universal-alpha.3',
'5.0.0-universal-alpha.4',
'5.0.0-universal-alpha.5',
'5.0.0-universal-alpha.6',
'5.0.0-universal-alpha.7',
'5.0.0-universal-alpha.8',
'5.0.0-universal-alpha.9',
'5.0.0-universal-alpha.10',
'5.0.0-universal-alpha.11',
'5.0.0-universal-alpha.12',
'5.0.0-universal-alpha.13',
'5.0.0-universal-alpha.14',
'5.0.0-universal-alpha.15',
'5.0.0-universal-alpha.16',
'5.0.0-universal-alpha.17',
'5.0.0-universal-alpha.18',
'5.0.0-universal-alpha.19',
'5.0.0-universal-alpha.20',
'5.0.0-universal-alpha.21',
'5.0.0-universal-alpha.22',
'5.0.0-universal-alpha.23',
'5.0.0-zones.1',
'5.0.0',
'5.0.1-canary.1',
'5.0.1-canary.2',
'5.0.1-canary.3',
'5.0.1-canary.4',
'5.0.1-canary.5',
'5.0.1-canary.6',
'5.0.1-canary.7',
'5.0.1-canary.8',
'5.0.1-canary.9',
'5.0.1-canary.10',
'5.0.1-canary.11',
'5.0.1-canary.12',
'5.0.1-canary.13',
'5.0.1-canary.14',
'5.0.1-canary.15',
'5.0.1-canary.16',
'5.0.1-canary.17',
'5.1.0',
'6.0.0-canary.1',
'6.0.0-canary.2',
'6.0.0-canary.3',
'6.0.0-canary.4',
'6.0.0-canary.5',
'6.0.0-canary.6',
'6.0.0-canary.7',
'6.0.0',
'6.0.1-canary.0',
'6.0.1-canary.1',
'6.0.1-canary.2',
'6.0.1',
'6.0.2-canary.0',
'6.0.2',
'6.0.3-canary.0',
'6.0.3-canary.1',
'6.0.3',
'6.0.4-canary.0',
'6.0.4-canary.1',
'6.0.4-canary.2',
'6.0.4-canary.3',
'6.0.4-canary.4',
'6.0.4-canary.5',
'6.0.4-canary.6',
'6.0.4-canary.7',
'6.0.4-canary.8',
'6.0.4-canary.9',
'6.1.0-canary.0',
'6.1.0',
'6.1.1-canary.0',
'6.1.1-canary.1',
'6.1.1-canary.2',
'6.1.1-canary.3',
'6.1.1-canary.4',
'6.1.1-canary.5',
'6.1.1',
'6.1.2',
'7.0.0-canary.0',
'7.0.0-canary.1',
'7.0.0-canary.2',
'7.0.0-canary.3',
'7.0.0-canary.4',
'7.0.0-canary.5',
'7.0.0-canary.6',
'7.0.0-canary.7',
'7.0.0-canary.8',
'7.0.0-canary.9',
'7.0.0-canary.10',
'7.0.0-canary.11',
'7.0.0-canary.12',
'7.0.0-canary.13',
'7.0.0-canary.14',
'7.0.0-canary.15',
'7.0.0-canary.16',
'7.0.0-canary.18',
'7.0.0-canary.19',
'7.0.0-canary.20',
'7.0.0',
'7.0.1-canary.0',
'7.0.1-canary.1',
'7.0.1-canary.2',
'7.0.1-canary.3',
'7.0.1-canary.4',
'7.0.1-canary.5',
'7.0.1-canary.6',
'7.0.1',
'7.0.2-alpha.1',
'7.0.2-alpha.3',
'7.0.2-canary.5',
'7.0.2-canary.6',
'7.0.2-canary.7',
'7.0.2-canary.8',
'7.0.2-canary.9',
'7.0.2-canary.10',
'7.0.2-canary.11',
'7.0.2-canary.12',
'7.0.2-canary.13',
'7.0.2-canary.14',
'7.0.2-canary.15',
'7.0.2-canary.16',
'7.0.2-canary.17',
'7.0.2-canary.18',
'7.0.2-canary.19',
'7.0.2-canary.20',
'7.0.2-canary.21',
'7.0.2-canary.22',
'7.0.2-canary.23',
'7.0.2-canary.24',
'7.0.2-canary.25',
'7.0.2-canary.26',
'7.0.2-canary.27',
'7.0.2-canary.28',
'7.0.2-canary.29',
'7.0.2-canary.31',
'7.0.2-canary.33',
'7.0.2-canary.34',
'7.0.2-canary.35',
'7.0.2-canary.36',
'7.0.2-canary.37',
'7.0.2-canary.38',
'7.0.2-canary.39',
'7.0.2-canary.40',
'7.0.2-canary.41',
'7.0.2-canary.42',
'7.0.2-canary.43',
'7.0.2-canary.44',
'7.0.2-canary.45',
'7.0.2-canary.46',
'7.0.2-canary.47',
'7.0.2-canary.48',
'7.0.2-canary.49',
'7.0.2-canary.50',
'7.0.2',
];

View File

@@ -1,25 +0,0 @@
// The Next.js builder can emit the project in a subdirectory depending on how
// many folder levels of `node_modules` are traced. To ensure `process.cwd()`
// returns the proper path, we change the directory to the folder with the
// launcher. This mimics `yarn workspace run` behavior.
process.chdir(__dirname);
if (!process.env.NODE_ENV) {
const region = process.env.VERCEL_REGION || process.env.NOW_REGION;
process.env.NODE_ENV = region === 'dev1' ? 'development' : 'production';
}
import { Server } from 'http';
import { Bridge } from './now__bridge';
// eslint-disable-next-line
let page: any = {};
// __LAUNCHER_PAGE_HANDLER__
// page.render is for React rendering
// page.default is for /api rendering
// page is for module.exports in /api
const server = new Server(page.render || page.default || page);
const bridge = new Bridge(server);
bridge.listen();
exports.launcher = bridge.launcher;

View File

@@ -1,25 +0,0 @@
// The Next.js builder can emit the project in a subdirectory depending on how
// many folder levels of `node_modules` are traced. To ensure `process.cwd()`
// returns the proper path, we change the directory to the folder with the
// launcher. This mimics `yarn workspace run` behavior.
process.chdir(__dirname);
if (!process.env.NODE_ENV) {
const region = process.env.VERCEL_REGION || process.env.NOW_REGION;
process.env.NODE_ENV = region === 'dev1' ? 'development' : 'production';
}
import { Server } from 'http';
import { Bridge } from './now__bridge';
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-var-requires
const page = require(__LAUNCHER_PAGE_PATH__);
// page.render is for React rendering
// page.default is for /api rendering
// page is for module.exports in /api
const server = new Server(page.render || page.default || page);
const bridge = new Bridge(server);
bridge.listen();
exports.launcher = bridge.launcher;

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +0,0 @@
module.exports = {
generateBuildId() {
return 'testing-build-id';
},
i18n: {
localeDetection: false,
locales: ['nl-NL', 'nl-BE', 'nl', 'fr-BE', 'fr', 'en-US', 'en'],
defaultLocale: 'en-US',
// TODO: testing locale domains support, will require custom
// testing set-up as test accounts are used currently
domains: [
{
domain: 'example.be',
defaultLocale: 'nl-BE',
},
{
domain: 'example.fr',
defaultLocale: 'fr',
},
],
},
};

View File

@@ -1,517 +0,0 @@
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/next"
}
],
"probes": [
{
"path": "/",
"headers": {
"accept-language": "en;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "\"en-US\""
},
{
"path": "/",
"headers": {
"accept-language": "nl;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "\"en-US\""
},
{
"path": "/",
"headers": {
"accept-language": "nl-NL;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "\"en-US\""
},
{
"path": "/",
"headers": {
"accept-language": "fr;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "\"en-US\""
},
{
"path": "/",
"headers": {
"accept-language": "en-US;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "index page"
},
{
"path": "/en-US",
"headers": {
"accept-language": "nl;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "index page"
},
{
"path": "/",
"status": 200,
"mustContain": "index page"
},
{
"path": "/",
"status": 200,
"mustContain": ">en-US<"
},
{
"path": "/en",
"status": 200,
"mustContain": "index page"
},
{
"path": "/en",
"status": 200,
"mustContain": ">en<"
},
{
"path": "/fr",
"status": 200,
"mustContain": "index page"
},
{
"path": "/fr",
"status": 200,
"mustContain": ">fr<"
},
{
"path": "/nl",
"status": 200,
"mustContain": "index page"
},
{
"path": "/nl",
"status": 200,
"mustContain": ">nl<"
},
{
"path": "/nl-NL",
"status": 200,
"mustContain": "index page"
},
{
"path": "/nl-NL",
"status": 200,
"mustContain": ">nl-NL<"
},
{
"path": "/non-existent",
"status": 404
},
{
"path": "/fr/non-existent",
"status": 404,
"mustContain": "lang=\"fr\""
},
{
"path": "/en/non-existent",
"status": 404,
"mustContain": "lang=\"en\""
},
{
"path": "/en-US/non-existent",
"status": 404,
"mustContain": "lang=\"en-US\""
},
{
"path": "/nl/non-existent",
"status": 404,
"mustContain": "lang=\"nl\""
},
{
"path": "/nl-NL/non-existent",
"status": 404,
"mustContain": "lang=\"nl-NL\""
},
{
"path": "/hello.txt",
"status": 200,
"mustContain": "hello world!"
},
{
"path": "/dynamic/hello",
"status": 200,
"mustContain": "dynamic page"
},
{
"path": "/dynamic/hello",
"status": 200,
"mustContain": "\"en-US\""
},
{
"path": "/en/dynamic/hello",
"status": 200,
"mustContain": "dynamic page"
},
{
"path": "/en/dynamic/hello",
"status": 200,
"mustContain": "\"en\""
},
{
"path": "/nl/dynamic/hello",
"status": 200,
"mustContain": "dynamic page"
},
{
"path": "/nl/dynamic/hello",
"status": 200,
"mustContain": "\"nl\""
},
{
"path": "/fr/dynamic/hello",
"status": 200,
"mustContain": "dynamic page"
},
{
"path": "/fr/dynamic/hello",
"status": 200,
"mustContain": "\"fr\""
},
{
"path": "/gsp",
"status": 200,
"mustContain": "gsp page"
},
{
"path": "/gsp",
"status": 200,
"mustContain": ">en-US<"
},
{
"path": "/en/gsp",
"status": 200,
"mustContain": "gsp page"
},
{
"path": "/en/gsp",
"status": 200,
"mustContain": ">en<"
},
{
"path": "/nl/gsp",
"status": 200,
"mustContain": "gsp page"
},
{
"path": "/nl/gsp",
"status": 200,
"mustContain": ">nl<"
},
{
"path": "/fr/gsp",
"status": 200,
"mustContain": "gsp page"
},
{
"path": "/fr/gsp",
"status": 200,
"mustContain": ">fr<"
},
{
"path": "/gssp",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/gssp",
"status": 200,
"mustContain": ">en-US<"
},
{
"path": "/en/gssp",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/en/gssp",
"status": 200,
"mustContain": ">en<"
},
{
"path": "/nl/gssp",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/nl/gssp",
"status": 200,
"mustContain": ">nl<"
},
{
"path": "/fr/gssp",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/fr/gssp",
"status": 200,
"mustContain": ">fr<"
},
{
"path": "/gssp/first",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/gssp/first",
"status": 200,
"mustContain": ">en-US<"
},
{
"path": "/gssp/first",
"status": 200,
"mustContain": "slug\":\"first\""
},
{
"path": "/en/gssp/first",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/en/gssp/first",
"status": 200,
"mustContain": ">en<"
},
{
"path": "/en/gssp/first",
"status": 200,
"mustContain": "slug\":\"first\""
},
{
"path": "/nl/gssp/first",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/nl/gssp/first",
"status": 200,
"mustContain": ">nl<"
},
{
"path": "/nl/gssp/first",
"status": 200,
"mustContain": "slug\":\"first\""
},
{
"path": "/fr/gssp/first",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/fr/gssp/first",
"status": 200,
"mustContain": ">fr<"
},
{
"path": "/fr/gssp/first",
"status": 200,
"mustContain": "slug\":\"first\""
},
// TODO: update when directory listing is disabled
// and these are proper 404s
{
"path": "/en/not-found",
"status": 200,
"mustContain": "Index of"
},
{
"path": "/nl/not-found",
"status": 200,
"mustContain": "Index of"
},
{
"path": "/en-US/not-found",
"status": 200,
"mustContain": "lang=\"en-US\""
},
{
"path": "/nl-NL/not-found",
"status": 200,
"mustContain": "lang=\"nl-NL\""
},
{
"path": "/fr/not-found",
"status": 200,
"mustContain": "lang=\"fr\""
},
// this will always be a 200 unless fallback: blocking is used
// since the static fallback page is served before the 404
// page is rendered
{
"path": "/en/not-found/fallback/first",
"status": 200,
"mustContain": "lang=\"en\""
},
{
"delay": 2000
},
{
"path": "/en/not-found/fallback/first",
"status": 200,
"mustNotContain": "gsp page"
},
{
"path": "/_next/data/testing-build-id/en/not-found/fallback/first.json",
"status": 404
},
{
"path": "/en/not-found/fallback/first",
"status": 200,
"mustContain": "lang=\"en\""
},
{
"path": "/en/not-found/fallback/first",
"status": 200,
"mustNotContain": "gsp page"
},
{
"path": "/fr/not-found/fallback/first",
"status": 200,
"mustContain": "lang=\"fr\""
},
{
"path": "/_next/data/testing-build-id/fr/not-found/fallback/first.json",
"status": 200
},
{
"path": "/fr/not-found/fallback/first",
"status": 200,
"mustContain": "lang=\"fr\""
},
{
"delay": 2000
},
{
"path": "/fr/not-found/fallback/first",
"status": 200,
"mustContain": "gsp page"
},
{
"path": "/_next/data/testing-build-id/en-US.json",
"status": 200,
"mustContain": "\"locale\":\"en-US\""
},
{
"path": "/_next/data/testing-build-id/en.json",
"status": 200,
"mustContain": "\"locale\":\"en\""
},
{
"path": "/_next/data/testing-build-id/fr.json",
"status": 200,
"mustContain": "\"locale\":\"fr\""
},
{
"path": "/_next/data/testing-build-id/nl.json",
"status": 200,
"mustContain": "\"locale\":\"nl\""
},
{
"path": "/_next/data/testing-build-id/en-US/gsp.json",
"status": 200,
"mustContain": "\"locale\":\"en-US\""
},
{
"path": "/_next/data/testing-build-id/en/gsp.json",
"status": 200,
"mustContain": "\"locale\":\"en\""
},
{
"path": "/_next/data/testing-build-id/fr/gsp.json",
"status": 200,
"mustContain": "\"locale\":\"fr\""
},
{
"path": "/_next/data/testing-build-id/nl/gsp.json",
"status": 200,
"mustContain": "\"locale\":\"nl\""
},
{
"path": "/gsp/blocking/first",
"status": 200,
"mustContain": "catchall"
},
{
"path": "/gsp/blocking/first",
"status": 200,
"mustContain": "lang=\"en-US\""
},
{
"path": "/_next/data/testing-build-id/en-US/gsp/blocking/first.json",
"status": 200,
"mustContain": "\"catchall\":\"yes\""
},
{
"path": "/nl-NL/gsp/blocking/first",
"status": 200,
"mustContain": "catchall"
},
{
"path": "/nl-NL/gsp/blocking/first",
"status": 200,
"mustContain": "lang=\"nl-NL\""
},
{
"path": "/_next/data/testing-build-id/nl-NL/gsp/blocking/first.json",
"status": 200,
"mustContain": "\"catchall\":\"yes\""
},
{
"path": "/fr/gsp/blocking/first",
"status": 200,
"mustContain": "catchall"
},
{
"path": "/fr/gsp/blocking/first",
"status": 200,
"mustContain": "lang=\"fr\""
},
{
"path": "/_next/data/testing-build-id/fr/gsp/blocking/first.json",
"status": 200,
"mustContain": "\"catchall\":\"yes\""
}
]
}

View File

@@ -1,7 +0,0 @@
{
"dependencies": {
"next": "canary",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}

View File

@@ -1,31 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="another">another page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getServerSideProps = ({ locale, locales }) => {
return {
props: {
locale,
locales,
},
};
};

View File

@@ -1,21 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="auto-export">auto-export page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
</>
);
}

View File

@@ -1,12 +0,0 @@
import { useRouter } from 'next/router';
export default function Dynamic(props) {
const router = useRouter();
return (
<>
<p>dynamic page</p>
<p id="query">{JSON.stringify(router.query)}</p>
</>
);
}

View File

@@ -1,43 +0,0 @@
import Link from 'next/link';
const Slug = props => {
return (
<div>
<p id="props">{JSON.stringify(props)}</p>
<Link href="/gsp/blocking/hallo-wereld" locale={'nl-NL'}>
<a>/nl-NL/gsp/blocking/hallo-wereld</a>
</Link>
<br />
<Link href="/gsp/blocking/42" locale={'nl-NL'}>
<a>/nl-NL/gsp/blocking/42</a>
</Link>
<br />
<Link href="/gsp/blocking/hallo-welt" locale={'fr'}>
<a>/fr/gsp/blocking/hallo-welt</a>
</Link>
<br />
<Link href="/gsp/blocking/42" locale={'fr'}>
<a>/fr/gsp/blocking/42</a>
</Link>
</div>
);
};
export const getStaticProps = () => {
return {
props: {
random: Math.random(),
catchall: 'yes',
},
revalidate: 1,
};
};
export const getStaticPaths = () => {
return {
paths: [],
fallback: 'blocking',
};
};
export default Slug;

View File

@@ -1,51 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
if (router.isFallback) return 'Loading...';
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getStaticProps = ({ params, locale, locales }) => {
return {
props: {
random: Math.random(),
params,
locale,
locales,
},
revalidate: 1,
};
};
export const getStaticPaths = ({ locales }) => {
const paths = [];
for (const locale of locales) {
paths.push({ params: { slug: 'first' }, locale });
paths.push({ params: { slug: 'second' }, locale });
}
return {
// the default locale will be used since one isn't defined here
paths,
fallback: true,
};
};

View File

@@ -1,32 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
// TODO: should non-dynamic GSP pages pre-render for each locale?
export const getStaticProps = ({ locale, locales }) => {
return {
props: {
locale,
locales,
},
};
};

View File

@@ -1,49 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
if (router.isFallback) return 'Loading...';
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getStaticProps = ({ params, locale, locales }) => {
return {
props: {
random: Math.random(),
params,
locale,
locales,
},
revalidate: 1,
};
};
export const getStaticPaths = () => {
return {
paths: [
{ params: { slug: 'first' } },
'/gsp/no-fallback/second',
{ params: { slug: 'first' }, locale: 'en-US' },
'/nl-NL/gsp/no-fallback/second',
'/fr/gsp/no-fallback/first',
],
fallback: false,
};
};

View File

@@ -1,32 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="gssp">gssp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getServerSideProps = ({ params, locale, locales }) => {
return {
props: {
params,
locale,
locales,
},
};
};

View File

@@ -1,31 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="gssp">gssp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getServerSideProps = ({ locale, locales }) => {
return {
props: {
locale,
locales,
},
};
};

View File

@@ -1,57 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="index">index page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/another">
<a id="to-another">to /another</a>
</Link>
<br />
<Link href="/gsp">
<a id="to-gsp">to /gsp</a>
</Link>
<br />
<Link href="/gsp/fallback/first">
<a id="to-fallback-first">to /gsp/fallback/first</a>
</Link>
<br />
<Link href="/gsp/fallback/hello">
<a id="to-fallback-hello">to /gsp/fallback/hello</a>
</Link>
<br />
<Link href="/gsp/no-fallback/first">
<a id="to-no-fallback-first">to /gsp/no-fallback/first</a>
</Link>
<br />
<Link href="/gssp">
<a id="to-gssp">to /gssp</a>
</Link>
<br />
<Link href="/gssp/first">
<a id="to-gssp-slug">to /gssp/first</a>
</Link>
<br />
</>
);
}
export const getStaticProps = ({ locale, locales }) => {
return {
props: {
random: Math.random(),
locale,
locales,
},
revalidate: 1,
};
};

View File

@@ -1,54 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
const { nextLocale } = router.query;
return (
<>
<p id="links">links page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/another" locale={nextLocale}>
<a id="to-another">to /another</a>
</Link>
<br />
<Link href="/gsp" locale={nextLocale}>
<a id="to-gsp">to /gsp</a>
</Link>
<br />
<Link href="/gsp/fallback/first" locale={nextLocale}>
<a id="to-fallback-first">to /gsp/fallback/first</a>
</Link>
<br />
<Link href="/gsp/fallback/hello" locale={nextLocale}>
<a id="to-fallback-hello">to /gsp/fallback/hello</a>
</Link>
<br />
<Link href="/gsp/no-fallback/first" locale={nextLocale}>
<a id="to-no-fallback-first">to /gsp/no-fallback/first</a>
</Link>
<br />
<Link href="/gssp" locale={nextLocale}>
<a id="to-gssp">to /gssp</a>
</Link>
<br />
<Link href="/gssp/first" locale={nextLocale}>
<a id="to-gssp-slug">to /gssp/first</a>
</Link>
<br />
</>
);
}
// make SSR page so we have query values immediately
export const getServerSideProps = () => {
return {
props: {},
};
};

View File

@@ -1,50 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
if (router.isFallback) return 'Loading...';
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getStaticProps = ({ params, locale, locales }) => {
if (locale === 'en' || locale === 'nl') {
return {
notFound: true,
};
}
return {
props: {
params,
locale,
locales,
},
};
};
export const getStaticPaths = () => {
return {
// the default locale will be used since one isn't defined here
paths: ['first', 'second'].map(slug => ({
params: { slug },
})),
fallback: true,
};
};

View File

@@ -1,37 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getStaticProps = ({ locale, locales }) => {
if (locale === 'en' || locale === 'nl') {
return {
notFound: true,
};
}
return {
props: {
locale,
locales,
},
};
};

View File

@@ -1,21 +0,0 @@
module.exports = {
generateBuildId() {
return 'testing-build-id';
},
i18n: {
locales: ['nl-NL', 'nl-BE', 'nl', 'fr-BE', 'fr', 'en-US', 'en'],
defaultLocale: 'en-US',
// TODO: testing locale domains support, will require custom
// testing set-up as test accounts are used currently
domains: [
{
domain: 'example.be',
defaultLocale: 'nl-BE',
},
{
domain: 'example.fr',
defaultLocale: 'fr',
},
],
},
};

View File

@@ -1,359 +0,0 @@
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/next",
"config": {
"sharedLambdas": false
}
}
],
"probes": [
{
"path": "/",
"headers": {
"accept-language": "en;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//en/"
}
},
{
"path": "/",
"headers": {
"accept-language": "nl;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//nl/"
}
},
{
"path": "/",
"headers": {
"accept-language": "nl-NL;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//nl-NL/"
}
},
{
"path": "/",
"headers": {
"accept-language": "fr;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//fr/"
}
},
{
"path": "/",
"headers": {
"accept-language": "en-US;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "index page"
},
{
"path": "/en-US",
"headers": {
"accept-language": "nl;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "index page"
},
{
"path": "/",
"status": 200,
"mustContain": "index page"
},
{
"path": "/",
"status": 200,
"mustContain": ">en-US<"
},
{
"path": "/en",
"status": 200,
"mustContain": "index page"
},
{
"path": "/en",
"status": 200,
"mustContain": ">en<"
},
{
"path": "/fr",
"status": 200,
"mustContain": "index page"
},
{
"path": "/fr",
"status": 200,
"mustContain": ">fr<"
},
{
"path": "/nl",
"status": 200,
"mustContain": "index page"
},
{
"path": "/nl",
"status": 200,
"mustContain": ">nl<"
},
{
"path": "/nl-NL",
"status": 200,
"mustContain": "index page"
},
{
"path": "/nl-NL",
"status": 200,
"mustContain": ">nl-NL<"
},
{
"path": "/non-existent",
"status": 404
},
{
"path": "/fr/non-existent",
"status": 404,
"mustContain": "lang=\"fr\""
},
{
"path": "/en/non-existent",
"status": 404,
"mustContain": "lang=\"en\""
},
{
"path": "/en-US/non-existent",
"status": 404,
"mustContain": "lang=\"en-US\""
},
{
"path": "/nl/non-existent",
"status": 404,
"mustContain": "lang=\"nl\""
},
{
"path": "/nl-NL/non-existent",
"status": 404,
"mustContain": "lang=\"nl-NL\""
},
{
"path": "/hello.txt",
"status": 200,
"mustContain": "hello world!"
},
{
"path": "/dynamic/hello",
"status": 200,
"mustContain": "dynamic page"
},
{
"path": "/dynamic/hello",
"status": 200,
"mustContain": "\"en-US\""
},
{
"path": "/en/dynamic/hello",
"status": 200,
"mustContain": "dynamic page"
},
{
"path": "/en/dynamic/hello",
"status": 200,
"mustContain": "\"en\""
},
{
"path": "/nl/dynamic/hello",
"status": 200,
"mustContain": "dynamic page"
},
{
"path": "/nl/dynamic/hello",
"status": 200,
"mustContain": "\"nl\""
},
{
"path": "/fr/dynamic/hello",
"status": 200,
"mustContain": "dynamic page"
},
{
"path": "/fr/dynamic/hello",
"status": 200,
"mustContain": "\"fr\""
},
{
"path": "/gsp",
"status": 200,
"mustContain": "gsp page"
},
{
"path": "/gsp",
"status": 200,
"mustContain": ">en-US<"
},
{
"path": "/en/gsp",
"status": 200,
"mustContain": "gsp page"
},
{
"path": "/en/gsp",
"status": 200,
"mustContain": ">en<"
},
{
"path": "/nl/gsp",
"status": 200,
"mustContain": "gsp page"
},
{
"path": "/nl/gsp",
"status": 200,
"mustContain": ">nl<"
},
{
"path": "/fr/gsp",
"status": 200,
"mustContain": "gsp page"
},
{
"path": "/fr/gsp",
"status": 200,
"mustContain": ">fr<"
},
{
"path": "/gssp",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/gssp",
"status": 200,
"mustContain": ">en-US<"
},
{
"path": "/en/gssp",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/en/gssp",
"status": 200,
"mustContain": ">en<"
},
{
"path": "/nl/gssp",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/nl/gssp",
"status": 200,
"mustContain": ">nl<"
},
{
"path": "/fr/gssp",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/fr/gssp",
"status": 200,
"mustContain": ">fr<"
},
{
"path": "/gssp/first",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/gssp/first",
"status": 200,
"mustContain": ">en-US<"
},
{
"path": "/gssp/first",
"status": 200,
"mustContain": "slug\":\"first\""
},
{
"path": "/en/gssp/first",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/en/gssp/first",
"status": 200,
"mustContain": ">en<"
},
{
"path": "/en/gssp/first",
"status": 200,
"mustContain": "slug\":\"first\""
},
{
"path": "/nl/gssp/first",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/nl/gssp/first",
"status": 200,
"mustContain": ">nl<"
},
{
"path": "/nl/gssp/first",
"status": 200,
"mustContain": "slug\":\"first\""
},
{
"path": "/fr/gssp/first",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/fr/gssp/first",
"status": 200,
"mustContain": ">fr<"
},
{
"path": "/fr/gssp/first",
"status": 200,
"mustContain": "slug\":\"first\""
}
]
}

View File

@@ -1,7 +0,0 @@
{
"dependencies": {
"next": "canary",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}

View File

@@ -1,31 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="another">another page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getServerSideProps = ({ locale, locales }) => {
return {
props: {
locale,
locales,
},
};
};

View File

@@ -1,21 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="auto-export">auto-export page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
</>
);
}

View File

@@ -1,12 +0,0 @@
import { useRouter } from 'next/router';
export default function Dynamic(props) {
const router = useRouter();
return (
<>
<p>dynamic page</p>
<p id="query">{JSON.stringify(router.query)}</p>
</>
);
}

View File

@@ -1,44 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
if (router.isFallback) return 'Loading...';
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getStaticProps = ({ params, locale, locales }) => {
return {
props: {
params,
locale,
locales,
},
};
};
export const getStaticPaths = () => {
return {
// the default locale will be used since one isn't defined here
paths: ['first', 'second'].map(slug => ({
params: { slug },
})),
fallback: true,
};
};

View File

@@ -1,32 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
// TODO: should non-dynamic GSP pages pre-render for each locale?
export const getStaticProps = ({ locale, locales }) => {
return {
props: {
locale,
locales,
},
};
};

View File

@@ -1,46 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
if (router.isFallback) return 'Loading...';
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getStaticProps = ({ params, locale, locales }) => {
return {
props: {
params,
locale,
locales,
},
};
};
export const getStaticPaths = () => {
return {
paths: [
{ params: { slug: 'first' } },
'/gsp/no-fallback/second',
{ params: { slug: 'first' }, locale: 'en-US' },
'/nl-NL/gsp/no-fallback/second',
],
fallback: false,
};
};

View File

@@ -1,32 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="gssp">gssp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getServerSideProps = ({ params, locale, locales }) => {
return {
props: {
params,
locale,
locales,
},
};
};

View File

@@ -1,31 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="gssp">gssp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getServerSideProps = ({ locale, locales }) => {
return {
props: {
locale,
locales,
},
};
};

View File

@@ -1,46 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="index">index page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/another">
<a id="to-another">to /another</a>
</Link>
<br />
<Link href="/gsp">
<a id="to-gsp">to /gsp</a>
</Link>
<br />
<Link href="/gsp/fallback/first">
<a id="to-fallback-first">to /gsp/fallback/first</a>
</Link>
<br />
<Link href="/gsp/fallback/hello">
<a id="to-fallback-hello">to /gsp/fallback/hello</a>
</Link>
<br />
<Link href="/gsp/no-fallback/first">
<a id="to-no-fallback-first">to /gsp/no-fallback/first</a>
</Link>
<br />
<Link href="/gssp">
<a id="to-gssp">to /gssp</a>
</Link>
<br />
<Link href="/gssp/first">
<a id="to-gssp-slug">to /gssp/first</a>
</Link>
<br />
</>
);
}

View File

@@ -1,54 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
const { nextLocale } = router.query;
return (
<>
<p id="links">links page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/another" locale={nextLocale}>
<a id="to-another">to /another</a>
</Link>
<br />
<Link href="/gsp" locale={nextLocale}>
<a id="to-gsp">to /gsp</a>
</Link>
<br />
<Link href="/gsp/fallback/first" locale={nextLocale}>
<a id="to-fallback-first">to /gsp/fallback/first</a>
</Link>
<br />
<Link href="/gsp/fallback/hello" locale={nextLocale}>
<a id="to-fallback-hello">to /gsp/fallback/hello</a>
</Link>
<br />
<Link href="/gsp/no-fallback/first" locale={nextLocale}>
<a id="to-no-fallback-first">to /gsp/no-fallback/first</a>
</Link>
<br />
<Link href="/gssp" locale={nextLocale}>
<a id="to-gssp">to /gssp</a>
</Link>
<br />
<Link href="/gssp/first" locale={nextLocale}>
<a id="to-gssp-slug">to /gssp/first</a>
</Link>
<br />
</>
);
}
// make SSR page so we have query values immediately
export const getServerSideProps = () => {
return {
props: {},
};
};

View File

@@ -1,50 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
if (router.isFallback) return 'Loading...';
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getStaticProps = ({ params, locale, locales }) => {
if (locale === 'en' || locale === 'nl') {
return {
notFound: true,
};
}
return {
props: {
params,
locale,
locales,
},
};
};
export const getStaticPaths = () => {
return {
// the default locale will be used since one isn't defined here
paths: ['first', 'second'].map(slug => ({
params: { slug },
})),
fallback: true,
};
};

View File

@@ -1,37 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getStaticProps = ({ locale, locales }) => {
if (locale === 'en' || locale === 'nl') {
return {
notFound: true,
};
}
return {
props: {
locale,
locales,
},
};
};

View File

@@ -1,182 +0,0 @@
/* eslint-env jest */
const fetch = require('node-fetch');
const cheerio = require('cheerio');
module.exports = function (ctx) {
it('should revalidate content properly from /', async () => {
// we have to hit the _next/data URL first
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/en-US.json`
);
expect(dataRes.status).toBe(200);
await dataRes.json();
const res = await fetch(`${ctx.deploymentUrl}/`);
expect(res.status).toBe(200);
let $ = cheerio.load(await res.text());
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('en-US');
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(`${ctx.deploymentUrl}/`);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('en-US');
});
it('should revalidate content properly from /fr', async () => {
// we have to hit the _next/data URL first
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/fr.json`
);
expect(dataRes.status).toBe(200);
await dataRes.json();
const res = await fetch(`${ctx.deploymentUrl}/fr`);
expect(res.status).toBe(200);
let $ = cheerio.load(await res.text());
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('fr');
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(`${ctx.deploymentUrl}/fr`);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('fr');
});
it('should revalidate content properly from /nl-NL', async () => {
// we have to hit the _next/data URL first
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/nl-NL/index.json`
);
expect(dataRes.status).toBe(200);
await dataRes.json();
const res = await fetch(`${ctx.deploymentUrl}/nl-NL`);
expect(res.status).toBe(200);
let $ = cheerio.load(await res.text());
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('nl-NL');
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(`${ctx.deploymentUrl}/nl-NL`);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('nl-NL');
});
it('should revalidate content properly from /second', async () => {
// we have to hit the _next/data URL first
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/en-US/second.json`
);
expect(dataRes.status).toBe(200);
await dataRes.json();
await new Promise(resolve => setTimeout(resolve, 2000));
const res = await fetch(`${ctx.deploymentUrl}/second`);
expect(res.status).toBe(200);
const html = await res.text();
let $ = cheerio.load(html);
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('en-US');
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(`${ctx.deploymentUrl}/second`);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('en-US');
});
it('should revalidate content properly from /fr/second', async () => {
// we have to hit the _next/data URL first
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/fr/second.json`
);
expect(dataRes.status).toBe(200);
await dataRes.json();
await new Promise(resolve => setTimeout(resolve, 2000));
const res = await fetch(`${ctx.deploymentUrl}/fr/second`);
expect(res.status).toBe(200);
const html = await res.text();
let $ = cheerio.load(html);
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('fr');
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(`${ctx.deploymentUrl}/fr/second`);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('fr');
});
it('should revalidate content properly from /nl-NL/second', async () => {
// we have to hit the _next/data URL first
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/nl-NL/second.json`
);
expect(dataRes.status).toBe(200);
await dataRes.json();
await new Promise(resolve => setTimeout(resolve, 2000));
const res = await fetch(`${ctx.deploymentUrl}/nl-NL/second`);
expect(res.status).toBe(200);
const html = await res.text();
let $ = cheerio.load(html);
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('nl-NL');
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(`${ctx.deploymentUrl}/nl-NL/second`);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('nl-NL');
});
};

View File

@@ -1,21 +0,0 @@
module.exports = {
generateBuildId() {
return 'testing-build-id';
},
i18n: {
locales: ['nl-NL', 'nl-BE', 'nl', 'fr-BE', 'fr', 'en-US', 'en'],
defaultLocale: 'en-US',
// TODO: testing locale domains support, will require custom
// testing set-up as test accounts are used currently
domains: [
{
domain: 'example.be',
defaultLocale: 'nl-BE',
},
{
domain: 'example.fr',
defaultLocale: 'fr',
},
],
},
};

View File

@@ -1,192 +0,0 @@
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/next"
}
],
"probes": [
{
"path": "/hello.txt",
"status": 200,
"mustContain": "hello world!"
},
{
"path": "/",
"headers": {
"accept-language": "en;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//en/"
}
},
{
"path": "/",
"headers": {
"accept-language": "nl;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//nl/"
}
},
{
"path": "/",
"headers": {
"accept-language": "nl-NL;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//nl-NL/"
}
},
{
"path": "/",
"headers": {
"accept-language": "fr;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//fr/"
}
},
{
"path": "/",
"headers": {
"accept-language": "en-US;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "catchall page"
},
{
"path": "/en-US",
"headers": {
"accept-language": "nl;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "catchall page"
},
{
"path": "/",
"status": 200,
"mustContain": "catchall page"
},
{
"path": "/",
"status": 200,
"mustContain": ">en-US<"
},
{
"path": "/en",
"status": 200,
"mustContain": "catchall page"
},
{
"path": "/en",
"status": 200,
"mustContain": ">en<"
},
{
"path": "/fr",
"status": 200,
"mustContain": "catchall page"
},
{
"path": "/fr",
"status": 200,
"mustContain": ">fr<"
},
{
"path": "/nl",
"status": 200,
"mustContain": "catchall page"
},
{
"path": "/nl",
"status": 200,
"mustContain": ">nl<"
},
{
"path": "/nl-NL",
"status": 200,
"mustContain": "catchall page"
},
{
"path": "/nl-NL",
"status": 200,
"mustContain": ">nl-NL<"
},
{
"path": "/first",
"status": 200,
"mustContain": "catchall page"
},
{
"path": "/first",
"status": 200,
"mustContain": ">en-US<"
},
{
"path": "/en/first",
"status": 200,
"mustContain": "catchall page"
},
{
"path": "/en/first",
"status": 200,
"mustContain": ">en<"
},
{
"path": "/fr/first",
"status": 200,
"mustContain": "catchall page"
},
{
"path": "/fr/first",
"status": 200,
"mustContain": ">fr<"
},
{
"path": "/nl/first",
"status": 200,
"mustContain": "catchall page"
},
{
"path": "/nl/first",
"status": 200,
"mustContain": ">nl<"
},
{
"path": "/nl-NL/first",
"status": 200,
"mustContain": "catchall page"
},
{
"path": "/nl-NL/first",
"status": 200,
"mustContain": ">nl-NL<"
}
]
}

View File

@@ -1,7 +0,0 @@
{
"dependencies": {
"next": "canary",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}

View File

@@ -1,69 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
const Slug = props => {
const router = useRouter();
// invariant ensuring fallback is never accidentally flipped
if (router.isFallback) {
return 'Loading...';
}
return (
<div>
<p>catchall page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/gsp/blocking/hallo-wereld" locale={'nl-NL'}>
<a>/nl-NL/gsp/blocking/hallo-wereld</a>
</Link>
<br />
<Link href="/gsp/blocking/42" locale={'nl-NL'}>
<a>/nl-NL/gsp/blocking/42</a>
</Link>
<br />
<Link href="/gsp/blocking/hallo-welt" locale={'fr'}>
<a>/fr/gsp/blocking/hallo-welt</a>
</Link>
<br />
<Link href="/gsp/blocking/42" locale={'fr'}>
<a>/fr/gsp/blocking/42</a>
</Link>
<br />
<Link href="/">
<a>/</a>
</Link>
</div>
);
};
export const getStaticProps = ({ params }) => {
return {
props: {
params,
random: Math.random(),
catchall: 'yes',
},
revalidate: 1,
};
};
export const getStaticPaths = ({ locales }) => {
const paths = [];
for (const locale of locales) {
paths.push({ params: { slug: ['first'] }, locale });
paths.push({ params: { slug: ['first'] }, locale });
}
return {
paths,
fallback: 'blocking',
};
};
export default Slug;

View File

@@ -1,412 +0,0 @@
/* eslint-env jest */
const fetch = require('node-fetch');
const cheerio = require('cheerio');
module.exports = function (ctx) {
it('should revalidate content properly from /', async () => {
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/en-US.json`
);
expect(dataRes.status).toBe(200);
await dataRes.json();
await new Promise(resolve => setTimeout(resolve, 2000));
const res = await fetch(`${ctx.deploymentUrl}/`);
expect(res.status).toBe(200);
let $ = cheerio.load(await res.text());
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('en-US');
expect(JSON.parse($('#router-query').text())).toEqual({});
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(`${ctx.deploymentUrl}/`);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('en-US');
expect(JSON.parse($('#router-query').text())).toEqual({});
});
it('should revalidate content properly from /fr', async () => {
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/fr.json`
);
expect(dataRes.status).toBe(200);
await dataRes.json();
await new Promise(resolve => setTimeout(resolve, 2000));
const res = await fetch(`${ctx.deploymentUrl}/fr`);
expect(res.status).toBe(200);
let $ = cheerio.load(await res.text());
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('fr');
expect(JSON.parse($('#router-query').text())).toEqual({});
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(`${ctx.deploymentUrl}/fr`);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('fr');
expect(JSON.parse($('#router-query').text())).toEqual({});
});
it('should revalidate content properly from /nl-NL', async () => {
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/nl-NL.json`
);
expect(dataRes.status).toBe(200);
await dataRes.json();
await new Promise(resolve => setTimeout(resolve, 2000));
const res = await fetch(`${ctx.deploymentUrl}/nl-NL`);
expect(res.status).toBe(200);
let $ = cheerio.load(await res.text());
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('nl-NL');
expect(JSON.parse($('#router-query').text())).toEqual({});
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(`${ctx.deploymentUrl}/nl-NL`);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('nl-NL');
expect(JSON.parse($('#router-query').text())).toEqual({});
});
it('should revalidate content properly from /gsp/fallback/first', async () => {
// check the _next/data URL first
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/en-US/gsp/fallback/first.json`
);
expect(dataRes.status).toBe(200);
await dataRes.json();
await new Promise(resolve => setTimeout(resolve, 2000));
const res = await fetch(`${ctx.deploymentUrl}/gsp/fallback/first`);
expect(res.status).toBe(200);
const html = await res.text();
let $ = cheerio.load(html);
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('en-US');
expect(props.params).toEqual({ slug: 'first' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(`${ctx.deploymentUrl}/gsp/fallback/first`);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('en-US');
expect(props2.params).toEqual({ slug: 'first' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
});
it('should revalidate content properly from /fr/gsp/fallback/first', async () => {
// check the _next/data URL first
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/fr/gsp/fallback/first.json`
);
expect(dataRes.status).toBe(200);
await dataRes.json();
await new Promise(resolve => setTimeout(resolve, 2000));
const res = await fetch(`${ctx.deploymentUrl}/fr/gsp/fallback/first`);
expect(res.status).toBe(200);
const html = await res.text();
let $ = cheerio.load(html);
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('fr');
expect(props.params).toEqual({ slug: 'first' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(`${ctx.deploymentUrl}/fr/gsp/fallback/first`);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('fr');
expect(props2.params).toEqual({ slug: 'first' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
});
it('should revalidate content properly from /nl-NL/gsp/fallback/first', async () => {
// check the _next/data URL first
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/nl-NL/gsp/fallback/first.json`
);
expect(dataRes.status).toBe(200);
await dataRes.json();
await new Promise(resolve => setTimeout(resolve, 2000));
const res = await fetch(`${ctx.deploymentUrl}/nl-NL/gsp/fallback/first`);
expect(res.status).toBe(200);
const html = await res.text();
let $ = cheerio.load(html);
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('nl-NL');
expect(props.params).toEqual({ slug: 'first' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(`${ctx.deploymentUrl}/nl-NL/gsp/fallback/first`);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('nl-NL');
expect(props2.params).toEqual({ slug: 'first' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
});
//
it('should revalidate content properly from /gsp/fallback/new-page', async () => {
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/en-US/gsp/fallback/new-page.json`
);
expect(dataRes.status).toBe(200);
await dataRes.json();
const initRes = await fetch(`${ctx.deploymentUrl}/gsp/fallback/new-page`);
expect(initRes.status).toBe(200);
await new Promise(resolve => setTimeout(resolve, 2000));
const res = await fetch(`${ctx.deploymentUrl}/gsp/fallback/new-page`);
expect(res.status).toBe(200);
const html = await res.text();
let $ = cheerio.load(html);
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('en-US');
expect(props.params).toEqual({ slug: 'new-page' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'new-page' });
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(`${ctx.deploymentUrl}/gsp/fallback/new-page`);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('en-US');
expect(props2.params).toEqual({ slug: 'new-page' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'new-page' });
});
it('should revalidate content properly from /fr/gsp/fallback/new-page', async () => {
// we have to hit the _next/data URL first
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/fr/gsp/fallback/new-page.json`
);
expect(dataRes.status).toBe(200);
await new Promise(resolve => setTimeout(resolve, 2000));
const res = await fetch(`${ctx.deploymentUrl}/fr/gsp/fallback/new-page`);
expect(res.status).toBe(200);
const html = await res.text();
let $ = cheerio.load(html);
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('fr');
expect(props.params).toEqual({ slug: 'new-page' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'new-page' });
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(`${ctx.deploymentUrl}/fr/gsp/fallback/new-page`);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('fr');
});
it('should revalidate content properly from /nl-NL/gsp/fallback/new-page', async () => {
// we have to hit the _next/data URL first
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/nl-NL/gsp/fallback/new-page.json`
);
expect(dataRes.status).toBe(200);
await new Promise(resolve => setTimeout(resolve, 2000));
const res = await fetch(`${ctx.deploymentUrl}/nl-NL/gsp/fallback/new-page`);
expect(res.status).toBe(200);
const html = await res.text();
let $ = cheerio.load(html);
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('nl-NL');
expect(props.params).toEqual({ slug: 'new-page' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'new-page' });
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(
`${ctx.deploymentUrl}/nl-NL/gsp/fallback/new-page`
);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('nl-NL');
expect(props2.params).toEqual({ slug: 'new-page' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'new-page' });
});
it('should revalidate content properly from /gsp/no-fallback/first', async () => {
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/en-US/gsp/no-fallback/first.json`
);
expect(dataRes.status).toBe(200);
await dataRes.json();
await new Promise(resolve => setTimeout(resolve, 2000));
const res = await fetch(`${ctx.deploymentUrl}/gsp/no-fallback/first`);
expect(res.status).toBe(200);
let $ = cheerio.load(await res.text());
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('en-US');
expect(props.params).toEqual({ slug: 'first' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(`${ctx.deploymentUrl}/gsp/no-fallback/first`);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('en-US');
expect(props2.params).toEqual({ slug: 'first' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
});
it('should revalidate content properly from /fr/gsp/no-fallback/first', async () => {
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/fr/gsp/no-fallback/first.json`
);
expect(dataRes.status).toBe(200);
await dataRes.json();
await new Promise(resolve => setTimeout(resolve, 2000));
const res = await fetch(`${ctx.deploymentUrl}/fr/gsp/no-fallback/first`);
expect(res.status).toBe(200);
let $ = cheerio.load(await res.text());
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('fr');
expect(props.params).toEqual({ slug: 'first' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(`${ctx.deploymentUrl}/fr/gsp/no-fallback/first`);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('fr');
expect(props2.params).toEqual({ slug: 'first' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
});
it('should revalidate content properly from /nl-NL/gsp/no-fallback/second', async () => {
const dataRes = await fetch(
`${ctx.deploymentUrl}/_next/data/testing-build-id/nl-NL/gsp/no-fallback/second.json`
);
expect(dataRes.status).toBe(200);
await dataRes.json();
await new Promise(resolve => setTimeout(resolve, 2000));
const res = await fetch(
`${ctx.deploymentUrl}/nl-NL/gsp/no-fallback/second`
);
expect(res.status).toBe(200);
let $ = cheerio.load(await res.text());
const props = JSON.parse($('#props').text());
const initialRandom = props.random;
expect($('#router-locale').text()).toBe('nl-NL');
expect(props.params).toEqual({ slug: 'second' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'second' });
// wait for revalidation to occur
await new Promise(resolve => setTimeout(resolve, 2000));
const res2 = await fetch(
`${ctx.deploymentUrl}/nl-NL/gsp/no-fallback/second`
);
expect(res2.status).toBe(200);
$ = cheerio.load(await res2.text());
const props2 = JSON.parse($('#props').text());
expect(initialRandom).not.toBe(props2.random);
expect($('#router-locale').text()).toBe('nl-NL');
expect(props2.params).toEqual({ slug: 'second' });
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'second' });
});
};

View File

@@ -1,98 +0,0 @@
module.exports = {
generateBuildId() {
return 'testing-build-id';
},
i18n: {
locales: ['nl-NL', 'nl-BE', 'nl', 'fr-BE', 'fr', 'en-US', 'en'],
defaultLocale: 'en-US',
// TODO: testing locale domains support, will require custom
// testing set-up as test accounts are used currently
domains: [
{
domain: 'example.be',
defaultLocale: 'nl-BE',
},
{
domain: 'example.fr',
defaultLocale: 'fr',
},
],
},
async redirects() {
return [
{
source: '/en-US/redirect-1',
destination: '/somewhere-else',
permanent: false,
locale: false,
},
{
source: '/nl/redirect-2',
destination: '/somewhere-else',
permanent: false,
locale: false,
},
{
source: '/redirect-3',
destination: '/somewhere-else',
permanent: false,
},
];
},
async rewrites() {
return [
{
source: '/en-US/rewrite-1',
destination: '/another',
locale: false,
},
{
source: '/nl/rewrite-2',
destination: '/nl/another',
locale: false,
},
{
source: '/fr/rewrite-3',
destination: '/nl/another',
locale: false,
},
{
source: '/rewrite-4',
destination: '/another',
},
];
},
async headers() {
return [
{
source: '/en-US/add-header-1',
locale: false,
headers: [
{
key: 'x-hello',
value: 'world',
},
],
},
{
source: '/nl/add-header-2',
locale: false,
headers: [
{
key: 'x-hello',
value: 'world',
},
],
},
{
source: '/add-header-3',
headers: [
{
key: 'x-hello',
value: 'world',
},
],
},
];
},
};

View File

@@ -1,799 +0,0 @@
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/next"
}
],
"probes": [
{
"path": "/",
"headers": {
"accept-language": "en;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//en/"
}
},
{
"path": "/",
"headers": {
"accept-language": "nl;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//nl/"
}
},
{
"path": "/",
"headers": {
"accept-language": "nl-NL;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//nl-NL/"
}
},
{
"path": "/",
"headers": {
"accept-language": "fr;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//fr/"
}
},
{
"path": "/",
"headers": {
"accept-language": "en-US;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "index page"
},
{
"path": "/en-US",
"headers": {
"accept-language": "nl;q=0.9"
},
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "index page"
},
{
"path": "/",
"status": 200,
"mustContain": "index page"
},
{
"path": "/",
"status": 200,
"mustContain": ">en-US<"
},
{
"path": "/en",
"status": 200,
"mustContain": "index page"
},
{
"path": "/en",
"status": 200,
"mustContain": ">en<"
},
{
"path": "/fr",
"status": 200,
"mustContain": "index page"
},
{
"path": "/fr",
"status": 200,
"mustContain": ">fr<"
},
{
"path": "/nl",
"status": 200,
"mustContain": "index page"
},
{
"path": "/nl",
"status": 200,
"mustContain": ">nl<"
},
{
"path": "/nl-NL",
"status": 200,
"mustContain": "index page"
},
{
"path": "/nl-NL",
"status": 200,
"mustContain": ">nl-NL<"
},
{
"path": "/non-existent",
"status": 404
},
{
"path": "/fr/non-existent",
"status": 404,
"mustContain": "lang=\"fr\""
},
{
"path": "/en/non-existent",
"status": 404,
"mustContain": "lang=\"en\""
},
{
"path": "/en-US/non-existent",
"status": 404,
"mustContain": "lang=\"en-US\""
},
{
"path": "/nl/non-existent",
"status": 404,
"mustContain": "lang=\"nl\""
},
{
"path": "/nl-NL/non-existent",
"status": 404,
"mustContain": "lang=\"nl-NL\""
},
{
"path": "/hello.txt",
"status": 200,
"mustContain": "hello world!"
},
{
"path": "/dynamic/hello",
"status": 200,
"mustContain": "dynamic page"
},
{
"path": "/dynamic/hello",
"status": 200,
"mustContain": "\"en-US\""
},
{
"path": "/en/dynamic/hello",
"status": 200,
"mustContain": "dynamic page"
},
{
"path": "/en/dynamic/hello",
"status": 200,
"mustContain": "\"en\""
},
{
"path": "/nl/dynamic/hello",
"status": 200,
"mustContain": "dynamic page"
},
{
"path": "/nl/dynamic/hello",
"status": 200,
"mustContain": "\"nl\""
},
{
"path": "/fr/dynamic/hello",
"status": 200,
"mustContain": "dynamic page"
},
{
"path": "/fr/dynamic/hello",
"status": 200,
"mustContain": "\"fr\""
},
{
"path": "/gsp",
"status": 200,
"mustContain": "gsp page"
},
{
"path": "/gsp",
"status": 200,
"mustContain": ">en-US<"
},
{
"path": "/en/gsp",
"status": 200,
"mustContain": "gsp page"
},
{
"path": "/en/gsp",
"status": 200,
"mustContain": ">en<"
},
{
"path": "/nl/gsp",
"status": 200,
"mustContain": "gsp page"
},
{
"path": "/nl/gsp",
"status": 200,
"mustContain": ">nl<"
},
{
"path": "/fr/gsp",
"status": 200,
"mustContain": "gsp page"
},
{
"path": "/fr/gsp",
"status": 200,
"mustContain": ">fr<"
},
{
"path": "/gssp",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/gssp",
"status": 200,
"mustContain": ">en-US<"
},
{
"path": "/en/gssp",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/en/gssp",
"status": 200,
"mustContain": ">en<"
},
{
"path": "/nl/gssp",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/nl/gssp",
"status": 200,
"mustContain": ">nl<"
},
{
"path": "/fr/gssp",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/fr/gssp",
"status": 200,
"mustContain": ">fr<"
},
{
"path": "/gssp/first",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/gssp/first",
"status": 200,
"mustContain": ">en-US<"
},
{
"path": "/gssp/first",
"status": 200,
"mustContain": "slug\":\"first\""
},
{
"path": "/en/gssp/first",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/en/gssp/first",
"status": 200,
"mustContain": ">en<"
},
{
"path": "/en/gssp/first",
"status": 200,
"mustContain": "slug\":\"first\""
},
{
"path": "/nl/gssp/first",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/nl/gssp/first",
"status": 200,
"mustContain": ">nl<"
},
{
"path": "/nl/gssp/first",
"status": 200,
"mustContain": "slug\":\"first\""
},
{
"path": "/fr/gssp/first",
"status": 200,
"mustContain": "gssp page"
},
{
"path": "/fr/gssp/first",
"status": 200,
"mustContain": ">fr<"
},
{
"path": "/fr/gssp/first",
"status": 200,
"mustContain": "slug\":\"first\""
},
// TODO: update when directory listing is disabled
// and these are proper 404s
{
"path": "/en/not-found",
"status": 200,
"mustContain": "Index of"
},
{
"path": "/nl/not-found",
"status": 200,
"mustContain": "Index of"
},
{
"path": "/en-US/not-found",
"status": 200,
"mustContain": "lang=\"en-US\""
},
{
"path": "/nl-NL/not-found",
"status": 200,
"mustContain": "lang=\"nl-NL\""
},
{
"path": "/fr/not-found",
"status": 200,
"mustContain": "lang=\"fr\""
},
// this will always be a 200 unless fallback: blocking is used
// since the static fallback page is served before the 404
// page is rendered
{
"path": "/en/not-found/fallback/first",
"status": 200,
"mustContain": "lang=\"en\""
},
{
"delay": 2000
},
{
"path": "/en/not-found/fallback/first",
"status": 200,
"mustNotContain": "gsp page"
},
{
"path": "/_next/data/testing-build-id/en/not-found/fallback/first.json",
"status": 404
},
{
"path": "/en/not-found/fallback/first",
"status": 200,
"mustContain": "lang=\"en\""
},
{
"path": "/en/not-found/fallback/first",
"status": 200,
"mustNotContain": "gsp page"
},
{
"path": "/fr/not-found/fallback/first",
"status": 200,
"mustContain": "lang=\"fr\""
},
{
"path": "/_next/data/testing-build-id/fr/not-found/fallback/first.json",
"status": 200
},
{
"path": "/fr/not-found/fallback/first",
"status": 200,
"mustContain": "lang=\"fr\""
},
{
"delay": 2000
},
{
"path": "/fr/not-found/fallback/first",
"status": 200,
"mustContain": "gsp page"
},
{
"path": "/_next/data/testing-build-id/en-US.json",
"status": 200,
"mustContain": "\"locale\":\"en-US\""
},
{
"path": "/_next/data/testing-build-id/en.json",
"status": 200,
"mustContain": "\"locale\":\"en\""
},
{
"path": "/_next/data/testing-build-id/fr.json",
"status": 200,
"mustContain": "\"locale\":\"fr\""
},
{
"path": "/_next/data/testing-build-id/nl.json",
"status": 200,
"mustContain": "\"locale\":\"nl\""
},
{
"path": "/_next/data/testing-build-id/en-US/gsp.json",
"status": 200,
"mustContain": "\"locale\":\"en-US\""
},
{
"path": "/_next/data/testing-build-id/en/gsp.json",
"status": 200,
"mustContain": "\"locale\":\"en\""
},
{
"path": "/_next/data/testing-build-id/fr/gsp.json",
"status": 200,
"mustContain": "\"locale\":\"fr\""
},
{
"path": "/_next/data/testing-build-id/nl/gsp.json",
"status": 200,
"mustContain": "\"locale\":\"nl\""
},
{
"path": "/gsp/blocking/first",
"status": 200,
"mustContain": "catchall"
},
{
"path": "/gsp/blocking/first",
"status": 200,
"mustContain": "lang=\"en-US\""
},
{
"path": "/_next/data/testing-build-id/en-US/gsp/blocking/first.json",
"status": 200,
"mustContain": "\"catchall\":\"yes\""
},
{
"path": "/nl-NL/gsp/blocking/first",
"status": 200,
"mustContain": "catchall"
},
{
"path": "/nl-NL/gsp/blocking/first",
"status": 200,
"mustContain": "lang=\"nl-NL\""
},
{
"path": "/_next/data/testing-build-id/nl-NL/gsp/blocking/first.json",
"status": 200,
"mustContain": "\"catchall\":\"yes\""
},
{
"path": "/fr/gsp/blocking/first",
"status": 200,
"mustContain": "catchall"
},
{
"path": "/fr/gsp/blocking/first",
"status": 200,
"mustContain": "lang=\"fr\""
},
{
"path": "/_next/data/testing-build-id/fr/gsp/blocking/first.json",
"status": 200,
"mustContain": "\"catchall\":\"yes\""
},
{
"path": "/en-US/redirect-1",
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//somewhere-else/"
}
},
{
"path": "/en/redirect-1",
"fetchOptions": {
"redirect": "manual"
},
"status": 404
},
{
"path": "/nl/redirect-2",
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//somewhere-else/"
}
},
{
"path": "/en-US/redirect-2",
"fetchOptions": {
"redirect": "manual"
},
"status": 404
},
{
"path": "/redirect-3",
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//somewhere-else/"
}
},
{
"path": "/en-US/redirect-3",
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//somewhere-else/"
}
},
{
"path": "/fr/redirect-3",
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//somewhere-else/"
}
},
{
"path": "/nl-NL/redirect-3",
"fetchOptions": {
"redirect": "manual"
},
"status": 307,
"responseHeaders": {
"location": "//somewhere-else/"
}
},
{
"path": "/en-US/rewrite-1",
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "another page"
},
{
"path": "/en-US/rewrite-1",
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "lang=\"en-US\""
},
{
"path": "/nl/rewrite-1",
"fetchOptions": {
"redirect": "manual"
},
"status": 404
},
{
"path": "/nl/rewrite-2",
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "another page"
},
{
"path": "/nl/rewrite-2",
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "lang=\"nl\""
},
{
"path": "/rewrite-2",
"fetchOptions": {
"redirect": "manual"
},
"status": 404
},
{
"path": "/fr/rewrite-3",
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "another page"
},
{
"path": "/fr/rewrite-3",
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "lang=\"nl\""
},
{
"path": "/rewrite-3",
"fetchOptions": {
"redirect": "manual"
},
"status": 404
},
{
"path": "/rewrite-4",
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "another page"
},
{
"path": "/rewrite-4",
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "lang=\"en-US\""
},
{
"path": "/en/rewrite-4",
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "another page"
},
{
"path": "/en/rewrite-4",
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "lang=\"en\""
},
{
"path": "/fr/rewrite-4",
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "another page"
},
{
"path": "/fr/rewrite-4",
"fetchOptions": {
"redirect": "manual"
},
"status": 200,
"mustContain": "lang=\"fr\""
},
{
"path": "/en-US/add-header-1",
"fetchOptions": {
"redirect": "manual"
},
"status": 404,
"responseHeaders": {
"x-hello": "world"
}
},
{
"path": "/en/add-header-1",
"fetchOptions": {
"redirect": "manual"
},
"status": 404,
"responseHeaders": {
"x-hello": null
}
},
{
"path": "/nl/add-header-2",
"fetchOptions": {
"redirect": "manual"
},
"status": 404,
"responseHeaders": {
"x-hello": "world"
}
},
{
"path": "/en-US/add-header-2",
"fetchOptions": {
"redirect": "manual"
},
"status": 404,
"responseHeaders": {
"x-hello": null
}
},
{
"path": "/add-header-3",
"fetchOptions": {
"redirect": "manual"
},
"status": 404,
"responseHeaders": {
"x-hello": "world"
}
},
{
"path": "/en-US/add-header-3",
"fetchOptions": {
"redirect": "manual"
},
"status": 404,
"responseHeaders": {
"x-hello": "world"
}
},
{
"path": "/fr/add-header-3",
"fetchOptions": {
"redirect": "manual"
},
"status": 404,
"responseHeaders": {
"x-hello": "world"
}
},
{
"path": "/nl-NL/add-header-3",
"fetchOptions": {
"redirect": "manual"
},
"status": 404,
"responseHeaders": {
"x-hello": "world"
}
}
]
}

View File

@@ -1,7 +0,0 @@
{
"dependencies": {
"next": "canary",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}

View File

@@ -1,31 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="another">another page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getServerSideProps = ({ locale, locales }) => {
return {
props: {
locale,
locales,
},
};
};

View File

@@ -1,21 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="auto-export">auto-export page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
</>
);
}

View File

@@ -1,12 +0,0 @@
import { useRouter } from 'next/router';
export default function Dynamic(props) {
const router = useRouter();
return (
<>
<p>dynamic page</p>
<p id="query">{JSON.stringify(router.query)}</p>
</>
);
}

View File

@@ -1,43 +0,0 @@
import Link from 'next/link';
const Slug = props => {
return (
<div>
<p id="props">{JSON.stringify(props)}</p>
<Link href="/gsp/blocking/hallo-wereld" locale={'nl-NL'}>
<a>/nl-NL/gsp/blocking/hallo-wereld</a>
</Link>
<br />
<Link href="/gsp/blocking/42" locale={'nl-NL'}>
<a>/nl-NL/gsp/blocking/42</a>
</Link>
<br />
<Link href="/gsp/blocking/hallo-welt" locale={'fr'}>
<a>/fr/gsp/blocking/hallo-welt</a>
</Link>
<br />
<Link href="/gsp/blocking/42" locale={'fr'}>
<a>/fr/gsp/blocking/42</a>
</Link>
</div>
);
};
export const getStaticProps = () => {
return {
props: {
random: Math.random(),
catchall: 'yes',
},
revalidate: 1,
};
};
export const getStaticPaths = () => {
return {
paths: [],
fallback: 'blocking',
};
};
export default Slug;

View File

@@ -1,51 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
if (router.isFallback) return 'Loading...';
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getStaticProps = ({ params, locale, locales }) => {
return {
props: {
random: Math.random(),
params,
locale,
locales,
},
revalidate: 1,
};
};
export const getStaticPaths = ({ locales }) => {
const paths = [];
for (const locale of locales) {
paths.push({ params: { slug: 'first' }, locale });
paths.push({ params: { slug: 'second' }, locale });
}
return {
// the default locale will be used since one isn't defined here
paths,
fallback: true,
};
};

View File

@@ -1,32 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
// TODO: should non-dynamic GSP pages pre-render for each locale?
export const getStaticProps = ({ locale, locales }) => {
return {
props: {
locale,
locales,
},
};
};

View File

@@ -1,49 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
if (router.isFallback) return 'Loading...';
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getStaticProps = ({ params, locale, locales }) => {
return {
props: {
random: Math.random(),
params,
locale,
locales,
},
revalidate: 1,
};
};
export const getStaticPaths = () => {
return {
paths: [
{ params: { slug: 'first' } },
'/gsp/no-fallback/second',
{ params: { slug: 'first' }, locale: 'en-US' },
'/nl-NL/gsp/no-fallback/second',
'/fr/gsp/no-fallback/first',
],
fallback: false,
};
};

View File

@@ -1,32 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="gssp">gssp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getServerSideProps = ({ params, locale, locales }) => {
return {
props: {
params,
locale,
locales,
},
};
};

View File

@@ -1,31 +0,0 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Page(props) {
const router = useRouter();
return (
<>
<p id="gssp">gssp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
);
}
export const getServerSideProps = ({ locale, locales }) => {
return {
props: {
locale,
locales,
},
};
};

Some files were not shown because too many files have changed in this diff Show More