mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-27 19:00:11 +00:00
Compare commits
38 Commits
@vercel/ne
...
@vercel/no
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6ec53d9d3 | ||
|
|
3ad5903f70 | ||
|
|
3cf155e999 | ||
|
|
d9a298d97c | ||
|
|
487d3c8554 | ||
|
|
9d0bfd3656 | ||
|
|
ec75333569 | ||
|
|
51aab912a2 | ||
|
|
670b2653c0 | ||
|
|
f71686fdad | ||
|
|
ec9c8ce150 | ||
|
|
a2048fc6d3 | ||
|
|
09ff9cda9f | ||
|
|
3a4d6f7848 | ||
|
|
9a0d676c0d | ||
|
|
25cd7b9e6e | ||
|
|
f926d5516c | ||
|
|
4603383850 | ||
|
|
c0c57889c8 | ||
|
|
85908a0524 | ||
|
|
503b9a2429 | ||
|
|
eac8f32ae7 | ||
|
|
3f76fefde6 | ||
|
|
79ddc5746b | ||
|
|
9a14615b43 | ||
|
|
6d6ccbdc25 | ||
|
|
704424ec58 | ||
|
|
c471127c69 | ||
|
|
c02dc9ac49 | ||
|
|
dd10e8cc77 | ||
|
|
cf69c2398c | ||
|
|
e48707571f | ||
|
|
6051fe6f0c | ||
|
|
2b3ba8a14f | ||
|
|
25bea3f83e | ||
|
|
b3e1828ebe | ||
|
|
e0e2a8e87e | ||
|
|
37ec89796d |
9
.github/CONTRIBUTING.md
vendored
9
.github/CONTRIBUTING.md
vendored
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"react-helmet": "^5.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@now/node": "^1.3.0"
|
||||
"@vercel/node": "1.8.5"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "gatsby develop",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "2.6.1-canary.0",
|
||||
"version": "2.6.1-canary.2",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -336,7 +336,7 @@ export interface NodeVersion {
|
||||
|
||||
export interface Builder {
|
||||
use: string;
|
||||
src: string;
|
||||
src?: string;
|
||||
config?: Config;
|
||||
}
|
||||
|
||||
|
||||
5
packages/now-build-utils/test/fixtures/19-yarn-v2/api/index.js
vendored
Normal file
5
packages/now-build-utils/test/fixtures/19-yarn-v2/api/index.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
const { camelCase } = require('camel-case');
|
||||
|
||||
module.exports = (req, res) => {
|
||||
res.end(camelCase('camel-case module is working'));
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "package.json", "use": "@vercel/static-build" }],
|
||||
"probes": [{ "path": "/", "mustContain": "Svelte app" }]
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
"svelte": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"camel-case": "^4.1.2",
|
||||
"sirv-cli": "^0.4.4"
|
||||
}
|
||||
}
|
||||
|
||||
11
packages/now-build-utils/test/fixtures/19-yarn-v2/vercel.json
vendored
Normal file
11
packages/now-build-utils/test/fixtures/19-yarn-v2/vercel.json
vendored
Normal 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" }
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "21.0.2-canary.0",
|
||||
"version": "21.0.2-canary.6",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -61,9 +61,9 @@
|
||||
"node": ">= 10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.6.1-canary.0",
|
||||
"@vercel/build-utils": "2.6.1-canary.2",
|
||||
"@vercel/go": "1.1.6",
|
||||
"@vercel/node": "1.8.5",
|
||||
"@vercel/node": "1.8.6-canary.3",
|
||||
"@vercel/python": "1.2.3",
|
||||
"@vercel/ruby": "1.2.4",
|
||||
"update-notifier": "4.1.0"
|
||||
|
||||
@@ -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$/, '')
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>;
|
||||
|
||||
4
packages/now-cli/test/dev/fixtures/missing-src-property/.gitignore
vendored
Normal file
4
packages/now-cli/test/dev/fixtures/missing-src-property/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
package.json
|
||||
yarn.lock
|
||||
.now
|
||||
.vercel
|
||||
@@ -0,0 +1 @@
|
||||
hello:index.txt
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"builds": [
|
||||
{
|
||||
"use": "@vercel/static"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1756,3 +1756,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');
|
||||
})
|
||||
);
|
||||
|
||||
33
packages/now-cli/test/integration.js
vendored
33
packages/now-cli/test/integration.js
vendored
@@ -1566,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],
|
||||
@@ -1583,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,
|
||||
}
|
||||
@@ -1599,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);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "9.0.5-canary.0",
|
||||
"version": "9.0.5-canary.2",
|
||||
"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.6.1-canary.2",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "3.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/next",
|
||||
"version": "2.7.4",
|
||||
"version": "2.7.8-canary.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
|
||||
@@ -26,7 +26,7 @@
|
||||
"@types/resolve-from": "5.0.1",
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/yazl": "2.4.1",
|
||||
"@vercel/nft": "0.9.2",
|
||||
"@vercel/nft": "0.9.5",
|
||||
"async-sema": "3.0.1",
|
||||
"buffer-crc32": "0.2.13",
|
||||
"escape-string-regexp": "2.0.0",
|
||||
|
||||
@@ -381,6 +381,14 @@ export async function build({
|
||||
console.log(`Running "install" command: \`${installCommand}\`...`);
|
||||
await execCommand(installCommand, {
|
||||
...spawnOpts,
|
||||
|
||||
// Yarn v2 PnP mode may be activated, so force
|
||||
// "node-modules" linker style
|
||||
env: {
|
||||
YARN_NODE_LINKER: 'node-modules',
|
||||
...spawnOpts.env,
|
||||
},
|
||||
|
||||
cwd: entryPath,
|
||||
});
|
||||
} else {
|
||||
@@ -410,7 +418,7 @@ export async function build({
|
||||
}
|
||||
|
||||
const memoryToConsume = Math.floor(os.totalmem() / 1024 ** 2) - 128;
|
||||
const env: { [key: string]: string | undefined } = { ...spawnOpts.env };
|
||||
const env: typeof process.env = { ...spawnOpts.env };
|
||||
env.NODE_OPTIONS = `--max_old_space_size=${memoryToConsume}`;
|
||||
|
||||
if (buildCommand) {
|
||||
@@ -418,6 +426,11 @@ export async function build({
|
||||
const nodeBinPath = await getNodeBinPath({ cwd: entryPath });
|
||||
env.PATH = `${nodeBinPath}${path.delimiter}${env.PATH}`;
|
||||
|
||||
// Yarn v2 PnP mode may be activated, so force "node-modules" linker style
|
||||
if (!env.YARN_NODE_LINKER) {
|
||||
env.YARN_NODE_LINKER = 'node-modules';
|
||||
}
|
||||
|
||||
debug(
|
||||
`Added "${nodeBinPath}" to PATH env because a build command was used.`
|
||||
);
|
||||
@@ -445,7 +458,10 @@ export async function build({
|
||||
nextVersion
|
||||
);
|
||||
const imagesManifest = await getImagesManifest(entryPath, outputDirectory);
|
||||
const prerenderManifest = await getPrerenderManifest(entryPath);
|
||||
const prerenderManifest = await getPrerenderManifest(
|
||||
entryPath,
|
||||
outputDirectory
|
||||
);
|
||||
const headers: Route[] = [];
|
||||
const rewrites: Route[] = [];
|
||||
let redirects: Route[] = [];
|
||||
@@ -759,6 +775,7 @@ export async function build({
|
||||
'cache-control': `public,max-age=${MAX_AGE_ONE_YEAR},immutable`,
|
||||
},
|
||||
continue: true,
|
||||
important: true,
|
||||
},
|
||||
|
||||
// error handling
|
||||
@@ -1020,7 +1037,7 @@ export async function build({
|
||||
|
||||
if (
|
||||
initialRevalidate === false &&
|
||||
!canUsePreviewMode &&
|
||||
(!canUsePreviewMode || (hasPages404 && routeKey === '/404')) &&
|
||||
!prerenderManifest.fallbackRoutes[route] &&
|
||||
!prerenderManifest.blockingFallbackRoutes[route]
|
||||
) {
|
||||
@@ -1072,19 +1089,25 @@ export async function build({
|
||||
console.time(tracingLabel);
|
||||
}
|
||||
|
||||
const nftCache = Object.create(null);
|
||||
|
||||
const {
|
||||
fileList: apiFileList,
|
||||
reasons: apiReasons,
|
||||
} = await nodeFileTrace(apiPages, {
|
||||
base: baseDir,
|
||||
processCwd: entryPath,
|
||||
cache: nftCache,
|
||||
});
|
||||
|
||||
debug(`node-file-trace result for api routes: ${apiFileList}`);
|
||||
|
||||
const { fileList, reasons: nonApiReasons } = await nodeFileTrace(
|
||||
nonApiPages,
|
||||
{
|
||||
base: baseDir,
|
||||
processCwd: entryPath,
|
||||
cache: nftCache,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1808,7 +1831,11 @@ export async function build({
|
||||
});
|
||||
}
|
||||
|
||||
if (!canUsePreviewMode) {
|
||||
// If revalidate isn't enabled we force the /404 route to be static
|
||||
// to match next start behavior otherwise getStaticProps would be
|
||||
// recalled for each 404 URL path since Prerender is cached based
|
||||
// on the URL path
|
||||
if (!canUsePreviewMode || (hasPages404 && routeKey === '/404')) {
|
||||
htmlFsRef.contentType = htmlContentType;
|
||||
prerenders[outputPathPage] = htmlFsRef;
|
||||
prerenders[outputPathData] = jsonFsRef;
|
||||
@@ -1929,7 +1956,25 @@ export async function build({
|
||||
path.join(entryPath, outputDirectory, 'static')
|
||||
);
|
||||
const staticFolderFiles = await glob('**', path.join(entryPath, 'static'));
|
||||
const publicFolderFiles = await glob('**', path.join(entryPath, 'public'));
|
||||
|
||||
let publicFolderFiles: Files = {};
|
||||
let publicFolderPath: string | undefined;
|
||||
|
||||
if (await pathExists(path.join(entryPath, 'public'))) {
|
||||
publicFolderPath = path.join(entryPath, 'public');
|
||||
} else if (
|
||||
// check at the same level as the output directory also
|
||||
await pathExists(path.join(entryPath, outputDirectory, '../public'))
|
||||
) {
|
||||
publicFolderPath = path.join(entryPath, outputDirectory, '../public');
|
||||
}
|
||||
|
||||
if (publicFolderPath) {
|
||||
debug(`Using public folder at ${publicFolderPath}`);
|
||||
publicFolderFiles = await glob('**/*', publicFolderPath);
|
||||
} else {
|
||||
debug('No public folder found');
|
||||
}
|
||||
|
||||
const staticFiles = Object.keys(nextStaticFiles).reduce(
|
||||
(mappedFiles, file) => ({
|
||||
@@ -1950,10 +1995,7 @@ export async function build({
|
||||
const publicDirectoryFiles = Object.keys(publicFolderFiles).reduce(
|
||||
(mappedFiles, file) => ({
|
||||
...mappedFiles,
|
||||
[path.join(
|
||||
entryDirectory,
|
||||
file.replace(/^public[/\\]+/, '')
|
||||
)]: publicFolderFiles[file],
|
||||
[path.join(entryDirectory, file)]: publicFolderFiles[file],
|
||||
}),
|
||||
{}
|
||||
);
|
||||
@@ -2329,6 +2371,7 @@ export async function build({
|
||||
'cache-control': `public,max-age=${MAX_AGE_ONE_YEAR},immutable`,
|
||||
},
|
||||
continue: true,
|
||||
important: true,
|
||||
},
|
||||
|
||||
// error handling
|
||||
@@ -2371,9 +2414,13 @@ export async function build({
|
||||
),
|
||||
|
||||
status: 404,
|
||||
headers: {
|
||||
'x-nextjs-page': page404Path,
|
||||
},
|
||||
...(static404Page
|
||||
? {}
|
||||
: {
|
||||
headers: {
|
||||
'x-nextjs-page': page404Path,
|
||||
},
|
||||
}),
|
||||
}
|
||||
: {
|
||||
src: path.join('/', entryDirectory, '.*'),
|
||||
|
||||
@@ -829,11 +829,12 @@ export async function getExportStatus(
|
||||
}
|
||||
|
||||
export async function getPrerenderManifest(
|
||||
entryPath: string
|
||||
entryPath: string,
|
||||
outputDirectory: string
|
||||
): Promise<NextPrerenderedRoutes> {
|
||||
const pathPrerenderManifest = path.join(
|
||||
entryPath,
|
||||
'.next',
|
||||
outputDirectory,
|
||||
'prerender-manifest.json'
|
||||
);
|
||||
|
||||
|
||||
@@ -1,6 +1,29 @@
|
||||
/* eslint-env jest */
|
||||
const fetch = require('node-fetch');
|
||||
const cheerio = require('cheerio');
|
||||
const { check, waitFor } = require('../../utils');
|
||||
const fetch = require('../../../../../test/lib/deployment/fetch-retry');
|
||||
|
||||
async function checkForChange(url, initialValue, hardError) {
|
||||
return check(
|
||||
async () => {
|
||||
const res = await fetch(url);
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error(`Invalid status code ${res.status}`);
|
||||
}
|
||||
const $ = cheerio.load(await res.text());
|
||||
const props = JSON.parse($('#props').text());
|
||||
|
||||
if (isNaN(props.random)) {
|
||||
throw new Error(`Invalid random value ${props.random}`);
|
||||
}
|
||||
const newValue = props.random;
|
||||
return initialValue !== newValue ? 'success' : 'fail';
|
||||
},
|
||||
'success',
|
||||
hardError
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = function (ctx) {
|
||||
it('should revalidate content properly from /', async () => {
|
||||
@@ -13,15 +36,15 @@ module.exports = function (ctx) {
|
||||
expect($('#router-locale').text()).toBe('en-US');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(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');
|
||||
|
||||
await checkForChange(`${ctx.deploymentUrl}/`, initialRandom);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /fr', async () => {
|
||||
@@ -34,15 +57,15 @@ module.exports = function (ctx) {
|
||||
expect($('#router-locale').text()).toBe('fr');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(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');
|
||||
|
||||
await checkForChange(`${ctx.deploymentUrl}/fr`, initialRandom);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /nl-NL', async () => {
|
||||
@@ -55,15 +78,15 @@ module.exports = function (ctx) {
|
||||
expect($('#router-locale').text()).toBe('nl-NL');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(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');
|
||||
|
||||
await checkForChange(`${ctx.deploymentUrl}/nl-NL`, initialRandom);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /second', async () => {
|
||||
@@ -77,15 +100,15 @@ module.exports = function (ctx) {
|
||||
expect($('#router-locale').text()).toBe('en-US');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(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');
|
||||
|
||||
await checkForChange(`${ctx.deploymentUrl}/second`, initialRandom);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /fr/second', async () => {
|
||||
@@ -99,15 +122,15 @@ module.exports = function (ctx) {
|
||||
expect($('#router-locale').text()).toBe('fr');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(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');
|
||||
|
||||
await checkForChange(`${ctx.deploymentUrl}/fr/second`, initialRandom);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /nl-NL/second', async () => {
|
||||
@@ -121,14 +144,14 @@ module.exports = function (ctx) {
|
||||
expect($('#router-locale').text()).toBe('nl-NL');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(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');
|
||||
|
||||
await checkForChange(`${ctx.deploymentUrl}/nl-NL/second`, initialRandom);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,6 +1,29 @@
|
||||
/* eslint-env jest */
|
||||
const fetch = require('node-fetch');
|
||||
const cheerio = require('cheerio');
|
||||
const { check, waitFor } = require('../../utils');
|
||||
const fetch = require('../../../../../test/lib/deployment/fetch-retry');
|
||||
|
||||
async function checkForChange(url, initialValue, hardError) {
|
||||
return check(
|
||||
async () => {
|
||||
const res = await fetch(url);
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error(`Invalid status code ${res.status}`);
|
||||
}
|
||||
const $ = cheerio.load(await res.text());
|
||||
const props = JSON.parse($('#props').text());
|
||||
|
||||
if (isNaN(props.random)) {
|
||||
throw new Error(`Invalid random value ${props.random}`);
|
||||
}
|
||||
const newValue = props.random;
|
||||
return initialValue !== newValue ? 'success' : 'fail';
|
||||
},
|
||||
'success',
|
||||
hardError
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = function (ctx) {
|
||||
it('should revalidate content properly from /', async () => {
|
||||
@@ -10,7 +33,7 @@ module.exports = function (ctx) {
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(2000);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -22,16 +45,17 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({});
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(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({});
|
||||
|
||||
await checkForChange(`${ctx.deploymentUrl}/`, initialRandom);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /fr', async () => {
|
||||
@@ -41,7 +65,7 @@ module.exports = function (ctx) {
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(2000);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/fr`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -53,16 +77,16 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({});
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(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({});
|
||||
|
||||
await checkForChange(`${ctx.deploymentUrl}/fr`, initialRandom);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /nl-NL', async () => {
|
||||
@@ -72,7 +96,7 @@ module.exports = function (ctx) {
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(2000);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/nl-NL`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -84,16 +108,16 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({});
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(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({});
|
||||
|
||||
await checkForChange(`${ctx.deploymentUrl}/nl-NL`, initialRandom);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /gsp/fallback/first', async () => {
|
||||
@@ -104,7 +128,7 @@ module.exports = function (ctx) {
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(2000);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/gsp/fallback/first`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -118,17 +142,21 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(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' });
|
||||
|
||||
await checkForChange(
|
||||
`${ctx.deploymentUrl}/gsp/fallback/first`,
|
||||
initialRandom
|
||||
);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /fr/gsp/fallback/first', async () => {
|
||||
@@ -139,7 +167,7 @@ module.exports = function (ctx) {
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(2000);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/fr/gsp/fallback/first`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -153,17 +181,21 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(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' });
|
||||
|
||||
await checkForChange(
|
||||
`${ctx.deploymentUrl}/fr/gsp/fallback/first`,
|
||||
initialRandom
|
||||
);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /nl-NL/gsp/fallback/first', async () => {
|
||||
@@ -174,7 +206,7 @@ module.exports = function (ctx) {
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(2000);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/nl-NL/gsp/fallback/first`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -188,17 +220,21 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(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' });
|
||||
|
||||
await checkForChange(
|
||||
`${ctx.deploymentUrl}/nl-NL/gsp/fallback/first`,
|
||||
initialRandom
|
||||
);
|
||||
});
|
||||
//
|
||||
|
||||
@@ -212,7 +248,7 @@ module.exports = function (ctx) {
|
||||
const initRes = await fetch(`${ctx.deploymentUrl}/gsp/fallback/new-page`);
|
||||
expect(initRes.status).toBe(200);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(2000);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/gsp/fallback/new-page`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -226,17 +262,21 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'new-page' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(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' });
|
||||
|
||||
await checkForChange(
|
||||
`${ctx.deploymentUrl}/gsp/fallback/new-page`,
|
||||
initialRandom
|
||||
);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /fr/gsp/fallback/new-page', async () => {
|
||||
@@ -246,7 +286,7 @@ module.exports = function (ctx) {
|
||||
);
|
||||
expect(dataRes.status).toBe(200);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(2000);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/fr/gsp/fallback/new-page`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -260,15 +300,18 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'new-page' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(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');
|
||||
|
||||
await checkForChange(
|
||||
`${ctx.deploymentUrl}/fr/gsp/fallback/new-page`,
|
||||
initialRandom
|
||||
);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /nl-NL/gsp/fallback/new-page', async () => {
|
||||
@@ -278,7 +321,7 @@ module.exports = function (ctx) {
|
||||
);
|
||||
expect(dataRes.status).toBe(200);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(2000);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/nl-NL/gsp/fallback/new-page`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -292,7 +335,7 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'new-page' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(2000);
|
||||
|
||||
const res2 = await fetch(
|
||||
`${ctx.deploymentUrl}/nl-NL/gsp/fallback/new-page`
|
||||
@@ -301,10 +344,14 @@ module.exports = function (ctx) {
|
||||
|
||||
$ = 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' });
|
||||
|
||||
await checkForChange(
|
||||
`${ctx.deploymentUrl}/nl-NL/gsp/fallback/new-page`,
|
||||
initialRandom
|
||||
);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /gsp/no-fallback/first', async () => {
|
||||
@@ -314,7 +361,7 @@ module.exports = function (ctx) {
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(2000);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/gsp/no-fallback/first`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -327,17 +374,21 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(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' });
|
||||
|
||||
await checkForChange(
|
||||
`${ctx.deploymentUrl}/gsp/no-fallback/first`,
|
||||
initialRandom
|
||||
);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /fr/gsp/no-fallback/first', async () => {
|
||||
@@ -347,7 +398,7 @@ module.exports = function (ctx) {
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(2000);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/fr/gsp/no-fallback/first`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -360,17 +411,21 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(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' });
|
||||
|
||||
await checkForChange(
|
||||
`${ctx.deploymentUrl}/fr/gsp/no-fallback/first`,
|
||||
initialRandom
|
||||
);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /nl-NL/gsp/no-fallback/second', async () => {
|
||||
@@ -380,7 +435,7 @@ module.exports = function (ctx) {
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(2000);
|
||||
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}/nl-NL/gsp/no-fallback/second`
|
||||
@@ -395,7 +450,7 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'second' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await waitFor(2000);
|
||||
|
||||
const res2 = await fetch(
|
||||
`${ctx.deploymentUrl}/nl-NL/gsp/no-fallback/second`
|
||||
@@ -404,9 +459,13 @@ module.exports = function (ctx) {
|
||||
|
||||
$ = 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' });
|
||||
|
||||
await checkForChange(
|
||||
`${ctx.deploymentUrl}/nl-NL/gsp/no-fallback/second`,
|
||||
initialRandom
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
39
packages/now-next/test/fixtures/00-public-dir-output-dir/now.json
vendored
Normal file
39
packages/now-next/test/fixtures/00-public-dir-output-dir/now.json
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next",
|
||||
"config": {
|
||||
"outputDirectory": "web/.next"
|
||||
}
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/",
|
||||
"status": 200,
|
||||
"mustContain": "Index page"
|
||||
},
|
||||
{
|
||||
"path": "/dynamic/first",
|
||||
"status": 200,
|
||||
"mustContain": "Dynamic Page"
|
||||
},
|
||||
{
|
||||
"path": "/dynamic-ssr/second",
|
||||
"status": 200,
|
||||
"mustContain": "Dynamic SSR Page"
|
||||
},
|
||||
{
|
||||
"path": "/hello.txt",
|
||||
"status": 200,
|
||||
"mustContain": "hello world!"
|
||||
},
|
||||
{
|
||||
"path": "/public/data.txt",
|
||||
"status": 200,
|
||||
"mustContain": "data!!"
|
||||
}
|
||||
]
|
||||
}
|
||||
10
packages/now-next/test/fixtures/00-public-dir-output-dir/package.json
vendored
Normal file
10
packages/now-next/test/fixtures/00-public-dir-output-dir/package.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "next build web"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
12
packages/now-next/test/fixtures/00-public-dir-output-dir/web/pages/dynamic-ssr/[slug].js
vendored
Normal file
12
packages/now-next/test/fixtures/00-public-dir-output-dir/web/pages/dynamic-ssr/[slug].js
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
export function getServerSideProps() {
|
||||
return {
|
||||
props: {
|
||||
hello: 'world',
|
||||
random: Math.random(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default function Dynamic() {
|
||||
return <p>Dynamic SSR Page</p>;
|
||||
}
|
||||
3
packages/now-next/test/fixtures/00-public-dir-output-dir/web/pages/dynamic/[slug].js
vendored
Normal file
3
packages/now-next/test/fixtures/00-public-dir-output-dir/web/pages/dynamic/[slug].js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Dynamic() {
|
||||
return <p>Dynamic Page</p>;
|
||||
}
|
||||
3
packages/now-next/test/fixtures/00-public-dir-output-dir/web/pages/index.js
vendored
Normal file
3
packages/now-next/test/fixtures/00-public-dir-output-dir/web/pages/index.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Index() {
|
||||
return <p>Index page</p>;
|
||||
}
|
||||
1
packages/now-next/test/fixtures/00-public-dir-output-dir/web/public/hello.txt
vendored
Normal file
1
packages/now-next/test/fixtures/00-public-dir-output-dir/web/public/hello.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
hello world!
|
||||
1
packages/now-next/test/fixtures/00-public-dir-output-dir/web/public/public/data.txt
vendored
Normal file
1
packages/now-next/test/fixtures/00-public-dir-output-dir/web/public/public/data.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
data!!
|
||||
@@ -1,4 +1,17 @@
|
||||
module.exports = {
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/_next/static/testing-build-id/_buildManifest.js',
|
||||
headers: [
|
||||
{
|
||||
key: 'cache-control',
|
||||
value: 'no-cache',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-env jest */
|
||||
const fetch = require('node-fetch');
|
||||
const cheerio = require('cheerio');
|
||||
const fetch = require('../../../../../test/lib/deployment/fetch-retry');
|
||||
|
||||
module.exports = function (ctx) {
|
||||
it('should revalidate content properly from pathname', async () => {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
module.exports = {
|
||||
distDir: 'the-output-directory',
|
||||
target: 'serverless',
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
};
|
||||
|
||||
@@ -14,6 +14,26 @@
|
||||
"path": "/",
|
||||
"status": 200,
|
||||
"mustContain": "hello world"
|
||||
},
|
||||
{
|
||||
"path": "/ssg/first",
|
||||
"status": 200,
|
||||
"mustContain": "first"
|
||||
},
|
||||
{
|
||||
"path": "/ssg/second",
|
||||
"status": 200,
|
||||
"mustContain": "Loading..."
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/ssg/second.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"slug\":\"second\""
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/ssg/third.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"slug\":\"third\""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
32
packages/now-next/test/fixtures/24-custom-output-dir/pages/ssg/[slug].js
vendored
Normal file
32
packages/now-next/test/fixtures/24-custom-output-dir/pages/ssg/[slug].js
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export const getStaticProps = ({ params }) => {
|
||||
return {
|
||||
props: {
|
||||
params,
|
||||
hello: 'world',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticPaths = () => {
|
||||
return {
|
||||
paths: [{ params: { slug: 'first' } }],
|
||||
fallback: true,
|
||||
};
|
||||
};
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
if (router.isFallback) {
|
||||
return 'Loading...';
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>slug: {props.params?.slug}</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-env jest */
|
||||
const cheerio = require('cheerio');
|
||||
const fetch = require('node-fetch');
|
||||
const setCookieParser = require('set-cookie-parser');
|
||||
const fetch = require('../../../../../test/lib/deployment/fetch-retry');
|
||||
|
||||
module.exports = function (ctx) {
|
||||
let previewCookie;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-env jest */
|
||||
const fetch = require('node-fetch');
|
||||
const cheerio = require('cheerio');
|
||||
const fetch = require('../../../../../test/lib/deployment/fetch-retry');
|
||||
|
||||
module.exports = function (ctx) {
|
||||
it('should revalidate content properly from dynamic pathname', async () => {
|
||||
|
||||
55
packages/now-next/test/fixtures/32-custom-install-command/.yarn/releases/yarn-berry.cjs
vendored
Executable file
55
packages/now-next/test/fixtures/32-custom-install-command/.yarn/releases/yarn-berry.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
1
packages/now-next/test/fixtures/32-custom-install-command/.yarnrc.yml
vendored
Normal file
1
packages/now-next/test/fixtures/32-custom-install-command/.yarnrc.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
yarnPath: ".yarn/releases/yarn-berry.cjs"
|
||||
5686
packages/now-next/test/fixtures/32-custom-install-command/yarn.lock
vendored
Normal file
5686
packages/now-next/test/fixtures/32-custom-install-command/yarn.lock
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
};
|
||||
4
packages/now-next/test/integration/gip-gsp-404/now.json
Normal file
4
packages/now-next/test/integration/gip-gsp-404/now.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "package.json", "use": "@vercel/next" }]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "latest",
|
||||
"react-dom": "latest"
|
||||
}
|
||||
}
|
||||
13
packages/now-next/test/integration/gip-gsp-404/pages/404.js
Normal file
13
packages/now-next/test/integration/gip-gsp-404/pages/404.js
Normal file
@@ -0,0 +1,13 @@
|
||||
export default function MyApp() {
|
||||
return '404 page';
|
||||
}
|
||||
|
||||
export const getStaticProps = () => {
|
||||
console.log('/404 getStaticProps');
|
||||
return {
|
||||
props: {
|
||||
random: Math.random(),
|
||||
is404: true,
|
||||
},
|
||||
};
|
||||
};
|
||||
15
packages/now-next/test/integration/gip-gsp-404/pages/_app.js
Normal file
15
packages/now-next/test/integration/gip-gsp-404/pages/_app.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
function MyApp({ Component, pageProps }) {
|
||||
return React.createElement(Component, pageProps);
|
||||
}
|
||||
|
||||
MyApp.getInitialProps = () => {
|
||||
console.log('App.getInitialProps');
|
||||
return {
|
||||
random: Math.random(),
|
||||
hello: 'world',
|
||||
};
|
||||
};
|
||||
|
||||
export default MyApp;
|
||||
@@ -0,0 +1,6 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
|
||||
export default (req, res) => {
|
||||
res.statusCode = 200;
|
||||
res.json({ name: 'John Doe' });
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function Home() {
|
||||
return 'index page';
|
||||
}
|
||||
@@ -34,6 +34,42 @@ it(
|
||||
FOUR_MINUTES
|
||||
);
|
||||
|
||||
it(
|
||||
'Should build the gip-gsp-404 example',
|
||||
async () => {
|
||||
const { buildResult } = await runBuildLambda(
|
||||
path.join(__dirname, 'gip-gsp-404')
|
||||
);
|
||||
const { output, routes } = buildResult;
|
||||
|
||||
const handleErrorIdx = (routes || []).findIndex(r => r.handle === 'error')
|
||||
expect(routes[handleErrorIdx + 1].dest).toBe('/404');
|
||||
expect(routes[handleErrorIdx + 1].headers).toBe(undefined);
|
||||
expect(output.goodbye).not.toBeDefined();
|
||||
expect(output.__NEXT_PAGE_LAMBDA_0).toBeDefined();
|
||||
expect(output['404']).toBeDefined();
|
||||
expect(output['404'].type).toBe('FileFsRef');
|
||||
expect(output['_next/data/testing-build-id/404.json']).toBeDefined();
|
||||
expect(output['_next/data/testing-build-id/404.json'].type).toBe(
|
||||
'FileFsRef'
|
||||
);
|
||||
const filePaths = Object.keys(output);
|
||||
const serverlessError = filePaths.some(filePath =>
|
||||
filePath.match(/_error/)
|
||||
);
|
||||
const hasUnderScoreAppStaticFile = filePaths.some(filePath =>
|
||||
filePath.match(/static.*\/pages\/_app-.*\.js$/)
|
||||
);
|
||||
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
||||
filePath.match(/static.*\/pages\/_error-.*\.js$/)
|
||||
);
|
||||
expect(hasUnderScoreAppStaticFile).toBeTruthy();
|
||||
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
||||
expect(serverlessError).toBeTruthy();
|
||||
},
|
||||
FOUR_MINUTES
|
||||
);
|
||||
|
||||
it(
|
||||
'Should not deploy preview lambdas for static site',
|
||||
async () => {
|
||||
|
||||
39
packages/now-next/test/utils.js
vendored
Normal file
39
packages/now-next/test/utils.js
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
async function waitFor(milliseconds) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, milliseconds);
|
||||
});
|
||||
}
|
||||
|
||||
async function check(contentFn, regex, hardError = true) {
|
||||
let content;
|
||||
let lastErr;
|
||||
|
||||
for (let tries = 0; tries < 30; tries++) {
|
||||
try {
|
||||
content = await contentFn();
|
||||
if (typeof regex === 'string') {
|
||||
if (regex === content) {
|
||||
return true;
|
||||
}
|
||||
} else if (regex.test(content)) {
|
||||
// found the content
|
||||
return true;
|
||||
}
|
||||
await waitFor(1000);
|
||||
} catch (err) {
|
||||
await waitFor(1000);
|
||||
lastErr = err;
|
||||
}
|
||||
}
|
||||
console.error('TIMED OUT CHECK: ', { regex, content, lastErr });
|
||||
|
||||
if (hardError) {
|
||||
throw new Error('TIMED OUT: ' + regex + '\n\n' + content);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
check,
|
||||
waitFor,
|
||||
};
|
||||
1
packages/now-node-bridge/.gitignore
vendored
1
packages/now-node-bridge/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
/bridge.*
|
||||
/launcher.*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/node-bridge",
|
||||
"version": "1.3.1",
|
||||
"version": "1.3.2-canary.0",
|
||||
"license": "MIT",
|
||||
"main": "./index.js",
|
||||
"repository": {
|
||||
|
||||
@@ -41,7 +41,7 @@ export function getNowLauncher({
|
||||
helpersPath,
|
||||
shouldAddHelpers = false,
|
||||
}: LauncherConfiguration) {
|
||||
return function(): Bridge {
|
||||
return function (): Bridge {
|
||||
let bridge = new Bridge();
|
||||
let isServerListening = false;
|
||||
|
||||
@@ -133,7 +133,7 @@ export function getAwsLauncher({
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return function(e, context, callback) {
|
||||
return function (e, context, callback) {
|
||||
const { path, method: httpMethod, body, headers } = JSON.parse(e.body);
|
||||
const { query } = parse(path, true);
|
||||
const queryStringParameters: { [i: string]: string } = {};
|
||||
1
packages/now-node/.gitignore
vendored
1
packages/now-node/.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
/dist
|
||||
/src/bridge.ts
|
||||
/src/launcher.ts
|
||||
/test/fixtures/**/types.d.ts
|
||||
/test/fixtures/11-symlinks/symlink
|
||||
|
||||
@@ -1,31 +1,34 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
bridge_defs="$(dirname $(pwd))/now-node-bridge/src/bridge.ts"
|
||||
# Copy shared dependencies
|
||||
bridge_dir="$(dirname $(pwd))/now-node-bridge"
|
||||
cp -v "$bridge_dir/src/bridge.ts" "$bridge_dir/src/launcher.ts" src
|
||||
|
||||
cp -v "$bridge_defs" src
|
||||
# Start fresh
|
||||
rm -rf dist
|
||||
|
||||
# build ts files
|
||||
# Build TypeScript files
|
||||
tsc
|
||||
|
||||
# todo: improve
|
||||
# copy type file for ts test
|
||||
# TODO: improve
|
||||
# Copy type file for ts test
|
||||
cp dist/types.d.ts test/fixtures/15-helpers/ts/types.d.ts
|
||||
# setup symlink for symlink test
|
||||
ln -sf symlinked-asset test/fixtures/11-symlinks/symlink
|
||||
|
||||
# use types.d.ts as the main types export
|
||||
# Use types.d.ts as the main types export
|
||||
mv dist/types.d.ts dist/types
|
||||
rm dist/*.d.ts
|
||||
mv dist/types dist/index.d.ts
|
||||
|
||||
# bundle helpers.ts with ncc
|
||||
# Bundle helpers.ts with ncc
|
||||
rm dist/helpers.js
|
||||
ncc build src/helpers.ts -e @vercel/build-utils -e @now/build-utils -o dist/helpers
|
||||
mv dist/helpers/index.js dist/helpers.js
|
||||
rm -rf dist/helpers
|
||||
|
||||
# build source-map-support/register for source maps
|
||||
# Build source-map-support/register for source maps
|
||||
ncc build ../../node_modules/source-map-support/register -e @vercel/build-utils -e @now/build-utils -o dist/source-map-support
|
||||
mv dist/source-map-support/index.js dist/source-map-support.js
|
||||
rm -rf dist/source-map-support
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/node",
|
||||
"version": "1.8.5",
|
||||
"version": "1.8.6-canary.3",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
||||
@@ -33,7 +33,7 @@
|
||||
"@types/etag": "1.8.0",
|
||||
"@types/test-listen": "1.1.0",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/nft": "0.9.2",
|
||||
"@vercel/nft": "0.9.5",
|
||||
"content-type": "1.0.4",
|
||||
"cookie": "0.4.0",
|
||||
"etag": "1.8.1",
|
||||
|
||||
@@ -1,20 +1,31 @@
|
||||
import { ServerResponse, IncomingMessage } from 'http';
|
||||
|
||||
export type NowRequestCookies = { [key: string]: string };
|
||||
export type NowRequestQuery = { [key: string]: string | string[] };
|
||||
export type VercelRequestCookies = { [key: string]: string };
|
||||
export type VercelRequestQuery = { [key: string]: string | string[] };
|
||||
export type VercelRequestBody = any;
|
||||
|
||||
export type VercelRequest = IncomingMessage & {
|
||||
query: VercelRequestQuery;
|
||||
cookies: VercelRequestCookies;
|
||||
body: VercelRequestBody;
|
||||
};
|
||||
|
||||
export type VercelResponse = ServerResponse & {
|
||||
send: (body: any) => VercelResponse;
|
||||
json: (jsonBody: any) => VercelResponse;
|
||||
status: (statusCode: number) => VercelResponse;
|
||||
redirect: (statusOrUrl: string | number, url?: string) => VercelResponse;
|
||||
};
|
||||
|
||||
export type VercelApiHandler = (
|
||||
req: VercelRequest,
|
||||
res: VercelResponse
|
||||
) => void;
|
||||
|
||||
// Backwards-compat
|
||||
export type NowRequestCookies = VercelRequestCookies;
|
||||
export type NowRequestQuery = VercelRequestQuery;
|
||||
export type NowRequestBody = any;
|
||||
|
||||
export type NowRequest = IncomingMessage & {
|
||||
query: NowRequestQuery;
|
||||
cookies: NowRequestCookies;
|
||||
body: NowRequestBody;
|
||||
};
|
||||
|
||||
export type NowResponse = ServerResponse & {
|
||||
send: (body: any) => NowResponse;
|
||||
json: (jsonBody: any) => NowResponse;
|
||||
status: (statusCode: number) => NowResponse;
|
||||
redirect: (statusOrUrl: string | number, url?: string) => NowResponse;
|
||||
};
|
||||
|
||||
export type NowApiHandler = (req: NowRequest, res: NowResponse) => void;
|
||||
export type NowRequest = VercelRequest;
|
||||
export type NowResponse = VercelResponse;
|
||||
export type NowApiHandler = VercelApiHandler;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"builds": [
|
||||
{ "src": "index.js", "use": "@vercel/node" },
|
||||
{ "src": "ts/index.ts", "use": "@vercel/node" },
|
||||
{ "src": "ts-legacy/index.ts", "use": "@vercel/node" },
|
||||
{ "src": "micro-compat/index.js", "use": "@vercel/node" },
|
||||
{
|
||||
"src": "no-helpers/index.js",
|
||||
@@ -40,6 +41,10 @@
|
||||
"path": "/ts",
|
||||
"mustContain": "hello:RANDOMNESS_PLACEHOLDER"
|
||||
},
|
||||
{
|
||||
"path": "/ts-legacy",
|
||||
"mustContain": "hello legacy:RANDOMNESS_PLACEHOLDER"
|
||||
},
|
||||
{
|
||||
"path": "/micro-compat",
|
||||
"method": "POST",
|
||||
|
||||
8
packages/now-node/test/fixtures/15-helpers/ts-legacy/handler.ts
vendored
Normal file
8
packages/now-node/test/fixtures/15-helpers/ts-legacy/handler.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import { NowApiHandler } from './types';
|
||||
|
||||
const listener: NowApiHandler = (req, res) => {
|
||||
res.status(200);
|
||||
res.send('hello legacy:RANDOMNESS_PLACEHOLDER');
|
||||
};
|
||||
|
||||
export default listener;
|
||||
6
packages/now-node/test/fixtures/15-helpers/ts-legacy/index.ts
vendored
Normal file
6
packages/now-node/test/fixtures/15-helpers/ts-legacy/index.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import { NowRequest, NowResponse } from './types';
|
||||
|
||||
export default function listener(req: NowRequest, res: NowResponse) {
|
||||
res.status(200);
|
||||
res.send('hello legacy:RANDOMNESS_PLACEHOLDER');
|
||||
}
|
||||
11
packages/now-node/test/fixtures/15-helpers/ts-legacy/tsconfig.json
vendored
Normal file
11
packages/now-node/test/fixtures/15-helpers/ts-legacy/tsconfig.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"sourceMap": true,
|
||||
"lib": ["esnext"],
|
||||
"target": "esnext",
|
||||
"module": "commonjs"
|
||||
},
|
||||
"include": ["index.ts", "handler.ts"]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NowApiHandler } from './types';
|
||||
import { VercelApiHandler } from './types';
|
||||
|
||||
const listener: NowApiHandler = (req, res) => {
|
||||
const listener: VercelApiHandler = (req, res) => {
|
||||
res.status(200);
|
||||
res.send('hello:RANDOMNESS_PLACEHOLDER');
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NowRequest, NowResponse } from './types';
|
||||
import { VercelRequest, VercelResponse } from './types';
|
||||
|
||||
export default function listener(req: NowRequest, res: NowResponse) {
|
||||
export default function listener(req: VercelRequest, res: VercelResponse) {
|
||||
res.status(200);
|
||||
res.send('hello:RANDOMNESS_PLACEHOLDER');
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"target": "esnext",
|
||||
"module": "commonjs"
|
||||
},
|
||||
"include": ["index.ts"]
|
||||
"include": ["index.ts", "handler.ts"]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NowRequest, NowResponse } from '@now/node';
|
||||
import { hello } from './dep';
|
||||
|
||||
export default function(req: NowRequest, res: NowResponse) {
|
||||
export default function (req: NowRequest, res: NowResponse) {
|
||||
if (req) {
|
||||
res.end(hello.toString());
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/routing-utils",
|
||||
"version": "1.9.2-canary.1",
|
||||
"version": "1.9.2-canary.4",
|
||||
"description": "Vercel routing utilities",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -89,6 +89,7 @@ export function mergeRoutes({ userRoutes, builds }: MergeRoutesProps): Route[] {
|
||||
|
||||
const outputRoutes: Route[] = [];
|
||||
const uniqueHandleValues = new Set([
|
||||
null,
|
||||
...userHandleMap.keys(),
|
||||
...builderHandleMap.keys(),
|
||||
]);
|
||||
|
||||
@@ -5,42 +5,21 @@ export const routesSchema = {
|
||||
type: 'array',
|
||||
maxItems: 1024,
|
||||
items: {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
src: {
|
||||
type: 'string',
|
||||
maxLength: 4096,
|
||||
},
|
||||
dest: {
|
||||
type: 'string',
|
||||
maxLength: 4096,
|
||||
},
|
||||
methods: {
|
||||
type: 'array',
|
||||
maxItems: 10,
|
||||
items: {
|
||||
type: 'string',
|
||||
maxLength: 32,
|
||||
},
|
||||
},
|
||||
headers: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'object',
|
||||
required: ['src'],
|
||||
additionalProperties: false,
|
||||
minProperties: 1,
|
||||
maxProperties: 100,
|
||||
patternProperties: {
|
||||
'^.{1,256}$': {
|
||||
properties: {
|
||||
src: {
|
||||
type: 'string',
|
||||
maxLength: 4096,
|
||||
},
|
||||
},
|
||||
},
|
||||
locale: {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
redirect: {
|
||||
dest: {
|
||||
type: 'string',
|
||||
maxLength: 4096,
|
||||
},
|
||||
headers: {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
minProperties: 1,
|
||||
@@ -52,43 +31,78 @@ export const routesSchema = {
|
||||
},
|
||||
},
|
||||
},
|
||||
value: {
|
||||
type: 'string',
|
||||
maxLength: 4096,
|
||||
methods: {
|
||||
type: 'array',
|
||||
maxItems: 10,
|
||||
items: {
|
||||
type: 'string',
|
||||
maxLength: 32,
|
||||
},
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
maxLength: 4096,
|
||||
important: {
|
||||
type: 'boolean',
|
||||
},
|
||||
cookie: {
|
||||
type: 'string',
|
||||
maxLength: 4096,
|
||||
continue: {
|
||||
type: 'boolean',
|
||||
},
|
||||
default: {
|
||||
type: 'string',
|
||||
maxLength: 4096,
|
||||
check: {
|
||||
type: 'boolean',
|
||||
},
|
||||
status: {
|
||||
type: 'integer',
|
||||
minimum: 100,
|
||||
maximum: 999,
|
||||
},
|
||||
locale: {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
minProperties: 1,
|
||||
properties: {
|
||||
redirect: {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
minProperties: 1,
|
||||
maxProperties: 100,
|
||||
patternProperties: {
|
||||
'^.{1,256}$': {
|
||||
type: 'string',
|
||||
maxLength: 4096,
|
||||
},
|
||||
},
|
||||
},
|
||||
value: {
|
||||
type: 'string',
|
||||
maxLength: 4096,
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
maxLength: 4096,
|
||||
},
|
||||
cookie: {
|
||||
type: 'string',
|
||||
maxLength: 4096,
|
||||
},
|
||||
default: {
|
||||
type: 'string',
|
||||
maxLength: 4096,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
handle: {
|
||||
type: 'string',
|
||||
maxLength: 32,
|
||||
{
|
||||
type: 'object',
|
||||
required: ['handle'],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
handle: {
|
||||
type: 'string',
|
||||
maxLength: 32,
|
||||
enum: ['error', 'filesystem', 'hit', 'miss', 'resource', 'rewrite'],
|
||||
},
|
||||
},
|
||||
},
|
||||
continue: {
|
||||
type: 'boolean',
|
||||
},
|
||||
check: {
|
||||
type: 'boolean',
|
||||
},
|
||||
important: {
|
||||
type: 'boolean',
|
||||
},
|
||||
status: {
|
||||
type: 'integer',
|
||||
minimum: 100,
|
||||
maximum: 999,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -16,10 +16,11 @@ export type Source = {
|
||||
methods?: string[];
|
||||
continue?: boolean;
|
||||
check?: boolean;
|
||||
important?: boolean;
|
||||
status?: number;
|
||||
locale?: {
|
||||
redirect: Record<string, string>;
|
||||
cookie: string;
|
||||
redirect?: Record<string, string>;
|
||||
cookie?: string;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
275
packages/now-routing-utils/test/index.spec.js
vendored
275
packages/now-routing-utils/test/index.spec.js
vendored
@@ -236,13 +236,25 @@ describe('normalizeRoutes', () => {
|
||||
],
|
||||
[
|
||||
{
|
||||
dataPath: '[0].src',
|
||||
keyword: 'type',
|
||||
dataPath: '[0].src',
|
||||
schemaPath: '#/items/anyOf/0/properties/src/type',
|
||||
params: { type: 'string' },
|
||||
message: 'should be string',
|
||||
params: {
|
||||
type: 'string',
|
||||
},
|
||||
schemaPath: '#/items/properties/src/type',
|
||||
},
|
||||
{
|
||||
keyword: 'additionalProperties',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/1/additionalProperties',
|
||||
params: { additionalProperty: 'src' },
|
||||
message: 'should NOT have additional properties',
|
||||
},
|
||||
{
|
||||
keyword: 'anyOf',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf',
|
||||
params: {},
|
||||
message: 'should match some schema in anyOf',
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -258,13 +270,25 @@ describe('normalizeRoutes', () => {
|
||||
],
|
||||
[
|
||||
{
|
||||
dataPath: '[0].dest',
|
||||
keyword: 'type',
|
||||
message: 'should be string',
|
||||
params: {
|
||||
type: 'string',
|
||||
},
|
||||
schemaPath: '#/items/properties/dest/type',
|
||||
keyword: 'required',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/0/required',
|
||||
params: { missingProperty: 'src' },
|
||||
message: "should have required property 'src'",
|
||||
},
|
||||
{
|
||||
keyword: 'additionalProperties',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/1/additionalProperties',
|
||||
params: { additionalProperty: 'dest' },
|
||||
message: 'should NOT have additional properties',
|
||||
},
|
||||
{
|
||||
keyword: 'anyOf',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf',
|
||||
params: {},
|
||||
message: 'should match some schema in anyOf',
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -280,13 +304,25 @@ describe('normalizeRoutes', () => {
|
||||
],
|
||||
[
|
||||
{
|
||||
dataPath: '[0].methods',
|
||||
keyword: 'type',
|
||||
message: 'should be array',
|
||||
params: {
|
||||
type: 'array',
|
||||
},
|
||||
schemaPath: '#/items/properties/methods/type',
|
||||
keyword: 'required',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/0/required',
|
||||
params: { missingProperty: 'src' },
|
||||
message: "should have required property 'src'",
|
||||
},
|
||||
{
|
||||
keyword: 'additionalProperties',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/1/additionalProperties',
|
||||
params: { additionalProperty: 'methods' },
|
||||
message: 'should NOT have additional properties',
|
||||
},
|
||||
{
|
||||
keyword: 'anyOf',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf',
|
||||
params: {},
|
||||
message: 'should match some schema in anyOf',
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -302,13 +338,25 @@ describe('normalizeRoutes', () => {
|
||||
],
|
||||
[
|
||||
{
|
||||
dataPath: '[0].methods[0]',
|
||||
keyword: 'type',
|
||||
message: 'should be string',
|
||||
params: {
|
||||
type: 'string',
|
||||
},
|
||||
schemaPath: '#/items/properties/methods/items/type',
|
||||
keyword: 'required',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/0/required',
|
||||
params: { missingProperty: 'src' },
|
||||
message: "should have required property 'src'",
|
||||
},
|
||||
{
|
||||
keyword: 'additionalProperties',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/1/additionalProperties',
|
||||
params: { additionalProperty: 'methods' },
|
||||
message: 'should NOT have additional properties',
|
||||
},
|
||||
{
|
||||
keyword: 'anyOf',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf',
|
||||
params: {},
|
||||
message: 'should match some schema in anyOf',
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -324,13 +372,25 @@ describe('normalizeRoutes', () => {
|
||||
],
|
||||
[
|
||||
{
|
||||
dataPath: '[0].headers',
|
||||
keyword: 'type',
|
||||
message: 'should be object',
|
||||
params: {
|
||||
type: 'object',
|
||||
},
|
||||
schemaPath: '#/items/properties/headers/type',
|
||||
keyword: 'required',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/0/required',
|
||||
params: { missingProperty: 'src' },
|
||||
message: "should have required property 'src'",
|
||||
},
|
||||
{
|
||||
keyword: 'additionalProperties',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/1/additionalProperties',
|
||||
params: { additionalProperty: 'headers' },
|
||||
message: 'should NOT have additional properties',
|
||||
},
|
||||
{
|
||||
keyword: 'anyOf',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf',
|
||||
params: {},
|
||||
message: 'should match some schema in anyOf',
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -348,14 +408,25 @@ describe('normalizeRoutes', () => {
|
||||
],
|
||||
[
|
||||
{
|
||||
dataPath: "[0].headers['test']",
|
||||
keyword: 'type',
|
||||
message: 'should be string',
|
||||
params: {
|
||||
type: 'string',
|
||||
},
|
||||
schemaPath:
|
||||
'#/items/properties/headers/patternProperties/%5E.%7B1%2C256%7D%24/type',
|
||||
keyword: 'required',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/0/required',
|
||||
params: { missingProperty: 'src' },
|
||||
message: "should have required property 'src'",
|
||||
},
|
||||
{
|
||||
keyword: 'additionalProperties',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/1/additionalProperties',
|
||||
params: { additionalProperty: 'headers' },
|
||||
message: 'should NOT have additional properties',
|
||||
},
|
||||
{
|
||||
keyword: 'anyOf',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf',
|
||||
params: {},
|
||||
message: 'should match some schema in anyOf',
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -371,13 +442,25 @@ describe('normalizeRoutes', () => {
|
||||
],
|
||||
[
|
||||
{
|
||||
dataPath: '[0].handle',
|
||||
keyword: 'additionalProperties',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/0/additionalProperties',
|
||||
params: { additionalProperty: 'handle' },
|
||||
message: 'should NOT have additional properties',
|
||||
},
|
||||
{
|
||||
keyword: 'type',
|
||||
dataPath: '[0].handle',
|
||||
schemaPath: '#/items/anyOf/1/properties/handle/type',
|
||||
params: { type: 'string' },
|
||||
message: 'should be string',
|
||||
params: {
|
||||
type: 'string',
|
||||
},
|
||||
schemaPath: '#/items/properties/handle/type',
|
||||
},
|
||||
{
|
||||
keyword: 'anyOf',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf',
|
||||
params: {},
|
||||
message: 'should match some schema in anyOf',
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -393,13 +476,25 @@ describe('normalizeRoutes', () => {
|
||||
],
|
||||
[
|
||||
{
|
||||
dataPath: '[0].continue',
|
||||
keyword: 'type',
|
||||
message: 'should be boolean',
|
||||
params: {
|
||||
type: 'boolean',
|
||||
},
|
||||
schemaPath: '#/items/properties/continue/type',
|
||||
keyword: 'required',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/0/required',
|
||||
params: { missingProperty: 'src' },
|
||||
message: "should have required property 'src'",
|
||||
},
|
||||
{
|
||||
keyword: 'additionalProperties',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/1/additionalProperties',
|
||||
params: { additionalProperty: 'continue' },
|
||||
message: 'should NOT have additional properties',
|
||||
},
|
||||
{
|
||||
keyword: 'anyOf',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf',
|
||||
params: {},
|
||||
message: 'should match some schema in anyOf',
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -415,13 +510,25 @@ describe('normalizeRoutes', () => {
|
||||
],
|
||||
[
|
||||
{
|
||||
dataPath: '[0].check',
|
||||
keyword: 'type',
|
||||
message: 'should be boolean',
|
||||
params: {
|
||||
type: 'boolean',
|
||||
},
|
||||
schemaPath: '#/items/properties/check/type',
|
||||
keyword: 'required',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/0/required',
|
||||
params: { missingProperty: 'src' },
|
||||
message: "should have required property 'src'",
|
||||
},
|
||||
{
|
||||
keyword: 'additionalProperties',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/1/additionalProperties',
|
||||
params: { additionalProperty: 'check' },
|
||||
message: 'should NOT have additional properties',
|
||||
},
|
||||
{
|
||||
keyword: 'anyOf',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf',
|
||||
params: {},
|
||||
message: 'should match some schema in anyOf',
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -437,13 +544,25 @@ describe('normalizeRoutes', () => {
|
||||
],
|
||||
[
|
||||
{
|
||||
dataPath: '[0].status',
|
||||
keyword: 'type',
|
||||
message: 'should be integer',
|
||||
params: {
|
||||
type: 'integer',
|
||||
},
|
||||
schemaPath: '#/items/properties/status/type',
|
||||
keyword: 'required',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/0/required',
|
||||
params: { missingProperty: 'src' },
|
||||
message: "should have required property 'src'",
|
||||
},
|
||||
{
|
||||
keyword: 'additionalProperties',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/1/additionalProperties',
|
||||
params: { additionalProperty: 'status' },
|
||||
message: 'should NOT have additional properties',
|
||||
},
|
||||
{
|
||||
keyword: 'anyOf',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf',
|
||||
params: {},
|
||||
message: 'should match some schema in anyOf',
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -459,13 +578,25 @@ describe('normalizeRoutes', () => {
|
||||
],
|
||||
[
|
||||
{
|
||||
dataPath: '[0]',
|
||||
keyword: 'additionalProperties',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/0/additionalProperties',
|
||||
params: { additionalProperty: 'doesNotExist' },
|
||||
message: 'should NOT have additional properties',
|
||||
params: {
|
||||
additionalProperty: 'doesNotExist',
|
||||
},
|
||||
schemaPath: '#/items/additionalProperties',
|
||||
},
|
||||
{
|
||||
keyword: 'additionalProperties',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf/1/additionalProperties',
|
||||
params: { additionalProperty: 'doesNotExist' },
|
||||
message: 'should NOT have additional properties',
|
||||
},
|
||||
{
|
||||
keyword: 'anyOf',
|
||||
dataPath: '[0]',
|
||||
schemaPath: '#/items/anyOf',
|
||||
params: {},
|
||||
message: 'should match some schema in anyOf',
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
82
packages/now-routing-utils/test/merge.spec.js
vendored
82
packages/now-routing-utils/test/merge.spec.js
vendored
@@ -1,4 +1,4 @@
|
||||
const { deepEqual } = require('assert');
|
||||
const { deepStrictEqual } = require('assert');
|
||||
const { mergeRoutes } = require('../dist/merge');
|
||||
|
||||
test('mergeRoutes simple', () => {
|
||||
@@ -10,7 +10,10 @@ test('mergeRoutes simple', () => {
|
||||
{
|
||||
use: '@now/node',
|
||||
entrypoint: 'api/home.js',
|
||||
routes: [{ src: '/node1', dest: '/n1' }, { src: '/node2', dest: '/n2' }],
|
||||
routes: [
|
||||
{ src: '/node1', dest: '/n1' },
|
||||
{ src: '/node2', dest: '/n2' },
|
||||
],
|
||||
},
|
||||
{
|
||||
use: '@now/python',
|
||||
@@ -30,7 +33,7 @@ test('mergeRoutes simple', () => {
|
||||
{ dest: '/py1', src: '/python1' },
|
||||
{ dest: '/py2', src: '/python2' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes handle filesystem user routes', () => {
|
||||
@@ -43,7 +46,10 @@ test('mergeRoutes handle filesystem user routes', () => {
|
||||
{
|
||||
use: '@now/node',
|
||||
entrypoint: 'api/home.js',
|
||||
routes: [{ src: '/node1', dest: '/n1' }, { src: '/node2', dest: '/n2' }],
|
||||
routes: [
|
||||
{ src: '/node1', dest: '/n1' },
|
||||
{ src: '/node2', dest: '/n2' },
|
||||
],
|
||||
},
|
||||
{
|
||||
use: '@now/python',
|
||||
@@ -64,7 +70,7 @@ test('mergeRoutes handle filesystem user routes', () => {
|
||||
{ handle: 'filesystem' },
|
||||
{ dest: '/u2', src: '/user2' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes handle filesystem build routes', () => {
|
||||
@@ -102,7 +108,7 @@ test('mergeRoutes handle filesystem build routes', () => {
|
||||
{ dest: '/n2', src: '/node2' },
|
||||
{ dest: '/py2', src: '/python2' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes handle filesystem both user and builds', () => {
|
||||
@@ -141,7 +147,7 @@ test('mergeRoutes handle filesystem both user and builds', () => {
|
||||
{ dest: '/n2', src: '/node2' },
|
||||
{ dest: '/py2', src: '/python2' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes continue true', () => {
|
||||
@@ -182,7 +188,7 @@ test('mergeRoutes continue true', () => {
|
||||
{ dest: '/py1', src: '/python1' },
|
||||
{ dest: '/py3', src: '/python3' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes check true', () => {
|
||||
@@ -223,7 +229,7 @@ test('mergeRoutes check true', () => {
|
||||
{ dest: '/py1', src: '/python1' },
|
||||
{ dest: '/py3', src: '/python3' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes check true, continue true, handle filesystem middle', () => {
|
||||
@@ -268,7 +274,7 @@ test('mergeRoutes check true, continue true, handle filesystem middle', () => {
|
||||
{ dest: '/n3', src: '/node3' },
|
||||
{ dest: '/py3', src: '/python3' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes check true, continue true, handle filesystem top', () => {
|
||||
@@ -306,7 +312,7 @@ test('mergeRoutes check true, continue true, handle filesystem top', () => {
|
||||
{ dest: '/n1', src: '/node1' },
|
||||
{ dest: '/py1', src: '/python1' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes multiple handle values', () => {
|
||||
@@ -359,5 +365,57 @@ test('mergeRoutes multiple handle values', () => {
|
||||
{ dest: '/u3', src: '/user3' },
|
||||
{ check: true, dest: '/py2', src: '/python2' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes ensure `handle: error` comes last', () => {
|
||||
const userRoutes = [];
|
||||
const builds = [
|
||||
{
|
||||
use: '@vercel/static-build',
|
||||
entrypoint: 'packge.json',
|
||||
routes: [
|
||||
{
|
||||
src: '^/home$',
|
||||
status: 301,
|
||||
headers: {
|
||||
Location: '/',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
use: '@vercel/zero-config-routes',
|
||||
entrypoint: '/',
|
||||
routes: [
|
||||
{
|
||||
handle: 'error',
|
||||
},
|
||||
{
|
||||
status: 404,
|
||||
src: '^/(?!.*api).*$',
|
||||
dest: '404.html',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const actual = mergeRoutes({ userRoutes, builds });
|
||||
const expected = [
|
||||
{
|
||||
status: 301,
|
||||
src: '^/home$',
|
||||
headers: {
|
||||
Location: '/',
|
||||
},
|
||||
},
|
||||
{
|
||||
handle: 'error',
|
||||
},
|
||||
{
|
||||
status: 404,
|
||||
src: '^/(?!.*api).*$',
|
||||
dest: '404.html',
|
||||
},
|
||||
];
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
set -euo pipefail
|
||||
|
||||
# Copy shared dependencies
|
||||
bridge_defs="$(dirname $(pwd))/now-node-bridge/src/bridge.ts"
|
||||
launcher_defs="$(dirname $(pwd))/now-node/src/launcher.ts"
|
||||
|
||||
cp -v "$bridge_defs" src
|
||||
cp -v "$launcher_defs" src
|
||||
bridge_dir="$(dirname $(pwd))/now-node-bridge"
|
||||
cp -v "$bridge_dir/src/bridge.ts" "$bridge_dir/src/launcher.ts" src
|
||||
|
||||
# Start fresh
|
||||
rm -rf dist
|
||||
|
||||
# Build TypeScript files
|
||||
tsc
|
||||
|
||||
# Build with `ncc`
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/static-build",
|
||||
"version": "0.18.0",
|
||||
"version": "0.18.1-canary.1",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/build-step",
|
||||
|
||||
@@ -366,6 +366,14 @@ export async function build({
|
||||
console.log(`Running "install" command: \`${installCommand}\`...`);
|
||||
await execCommand(installCommand, {
|
||||
...spawnOpts,
|
||||
|
||||
// Yarn v2 PnP mode may be activated, so force
|
||||
// "node-modules" linker style
|
||||
env: {
|
||||
YARN_NODE_LINKER: 'node-modules',
|
||||
...spawnOpts.env,
|
||||
},
|
||||
|
||||
cwd: entrypointDir,
|
||||
});
|
||||
} else {
|
||||
@@ -468,6 +476,14 @@ export async function build({
|
||||
typeof buildCommand === 'string'
|
||||
? await execCommand(buildCommand, {
|
||||
...spawnOpts,
|
||||
|
||||
// Yarn v2 PnP mode may be activated, so force
|
||||
// "node-modules" linker style
|
||||
env: {
|
||||
YARN_NODE_LINKER: 'node-modules',
|
||||
...spawnOpts.env,
|
||||
},
|
||||
|
||||
cwd: entrypointDir,
|
||||
})
|
||||
: await runPackageJsonScript(
|
||||
|
||||
@@ -2,17 +2,11 @@
|
||||
set -euo pipefail
|
||||
|
||||
# Copy shared dependencies
|
||||
bridge_defs="$(dirname $(pwd))/now-node-bridge/src/bridge.ts"
|
||||
launcher_defs="$(dirname $(pwd))/now-node/src/launcher.ts"
|
||||
|
||||
cp -v "$bridge_defs" src
|
||||
cp -v "$launcher_defs" src
|
||||
bridge_dir="$(dirname $(pwd))/now-node-bridge"
|
||||
cp -v "$bridge_dir/src/bridge.ts" "$bridge_dir/src/launcher.ts" src
|
||||
|
||||
# Start fresh
|
||||
rm -rf dist
|
||||
|
||||
## Build ts files
|
||||
# Build TypeScript files
|
||||
tsc
|
||||
|
||||
# Build with `ncc`
|
||||
#ncc build src/index.ts -e @vercel/build-utils -e @now/build-utils -o dist
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/redwood",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1-canary.0",
|
||||
"main": "./dist/index.js",
|
||||
"license": "MIT",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const fetch = require('node-fetch');
|
||||
const retryBailByDefault = require('./retry-bail-by-default.js');
|
||||
|
||||
async function fetchRetry (...args) {
|
||||
async function fetchRetry(...args) {
|
||||
return await retryBailByDefault(
|
||||
async (canRetry) => {
|
||||
async canRetry => {
|
||||
try {
|
||||
return await fetch(...args);
|
||||
} catch (error) {
|
||||
@@ -14,6 +14,12 @@ async function fetchRetry (...args) {
|
||||
// request to https://api-gru1.vercel.com/v3/now/deployments/dpl_FBWWhpQomjgwjJLu396snLrGZYCm failed, reason:
|
||||
// connect ETIMEDOUT 18.228.143.224:443
|
||||
throw canRetry(error);
|
||||
} else if (error.code === 'ECONNREFUSED') {
|
||||
// request to https://test2020-dhdy1xrfa.vercel.app/blog/post-3 failed, reason:
|
||||
// connect ECONNREFUSED 76.76.21.21:443
|
||||
throw canRetry(error);
|
||||
} else if (error.code === 'ECONNRESET') {
|
||||
throw canRetry(error);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const fs = require('fs-extra');
|
||||
const { glob, getWriteableDirectory } = require('@vercel/build-utils');
|
||||
|
||||
function runAnalyze(wrapper, context) {
|
||||
@@ -11,13 +12,21 @@ function runAnalyze(wrapper, context) {
|
||||
async function runBuildLambda(inputPath) {
|
||||
const inputFiles = await glob('**', inputPath);
|
||||
const nowJsonRef = inputFiles['vercel.json'] || inputFiles['now.json'];
|
||||
expect(nowJsonRef).toBeDefined();
|
||||
|
||||
if (typeof expect !== 'undefined') {
|
||||
expect(nowJsonRef).toBeDefined();
|
||||
}
|
||||
const nowJson = require(nowJsonRef.fsPath);
|
||||
expect(nowJson.builds.length).toBe(1);
|
||||
const build = nowJson.builds[0];
|
||||
expect(build.src.includes('*')).toBeFalsy();
|
||||
|
||||
if (typeof expect !== 'undefined') {
|
||||
expect(build.src.includes('*')).toBeFalsy();
|
||||
}
|
||||
const entrypoint = build.src.replace(/^\//, ''); // strip leftmost slash
|
||||
expect(inputFiles[entrypoint]).toBeDefined();
|
||||
|
||||
if (typeof expect !== 'undefined') {
|
||||
expect(inputFiles[entrypoint]).toBeDefined();
|
||||
}
|
||||
inputFiles[entrypoint].digest =
|
||||
'this-is-a-fake-digest-for-non-default-analyze';
|
||||
const wrapper = require(build.use);
|
||||
@@ -28,7 +37,7 @@ async function runBuildLambda(inputPath) {
|
||||
config: build.config,
|
||||
});
|
||||
|
||||
const workPath = await getWriteableDirectory();
|
||||
const workPath = await fs.realpath(await getWriteableDirectory());
|
||||
const buildResult = await wrapper.build({
|
||||
files: inputFiles,
|
||||
entrypoint,
|
||||
|
||||
14
yarn.lock
14
yarn.lock
@@ -2226,10 +2226,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@vercel/ncc/-/ncc-0.24.0.tgz#a2e8783a185caa99b5d8961a57dfc9665de16296"
|
||||
integrity sha512-crqItMcIwCkvdXY/V3/TzrHJQx6nbIaRqE1cOopJhgGX6izvNov40SmD//nS5flfEvdK54YGjwVVq+zG6crjOg==
|
||||
|
||||
"@vercel/nft@0.9.2":
|
||||
version "0.9.2"
|
||||
resolved "https://registry.yarnpkg.com/@vercel/nft/-/nft-0.9.2.tgz#677ecefb0bd618143281c62c719baca57a36ac4d"
|
||||
integrity sha512-Dr2yJlCnfkQEt4QHKcPJKTxCyoBX0YCzHDzozd8upBFm8kKbh2yMSu5wp+1btevQXOMkOUtxntovwwPHDIU51w==
|
||||
"@vercel/nft@0.9.5":
|
||||
version "0.9.5"
|
||||
resolved "https://registry.yarnpkg.com/@vercel/nft/-/nft-0.9.5.tgz#bf795944a4764ca49ca1a642f17ab32f9ac701d2"
|
||||
integrity sha512-EhSFOYwqvH3KZyK1pKyFj/DRoCZ2KFu8sRaVaJ+KGlU4kroAWm8okeA2EtIY11+/fMX3YQkNno7kf5H4FZrDvg==
|
||||
dependencies:
|
||||
acorn "^7.1.1"
|
||||
acorn-class-fields "^0.3.2"
|
||||
@@ -6266,9 +6266,9 @@ inherits@2.0.3:
|
||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||
|
||||
ini@^1.3.2, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
||||
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
|
||||
version "1.3.7"
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84"
|
||||
integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==
|
||||
|
||||
init-package-json@^1.10.3:
|
||||
version "1.10.3"
|
||||
|
||||
Reference in New Issue
Block a user