mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-25 19:00:01 +00:00
Compare commits
12 Commits
@vercel/ru
...
@vercel/ne
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff2a22023d | ||
|
|
c6efc028aa | ||
|
|
96565da1cf | ||
|
|
afb5e7fc85 | ||
|
|
34cc987be8 | ||
|
|
55c60d30e6 | ||
|
|
eb993d47ac | ||
|
|
d2184628d1 | ||
|
|
65f0cc6797 | ||
|
|
c628c7b58e | ||
|
|
4e005274f9 | ||
|
|
482373f711 |
14
errors/next-functions-config-optimized-lambdas.md
Normal file
14
errors/next-functions-config-optimized-lambdas.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# `@vercel/next` Functions Config Optimized Lambdas Opt-out
|
||||
|
||||
#### Why This Warning Occurred
|
||||
|
||||
`@vercel/next` by default now bundles pages into optimized functions, minimizing bootup time and increasing overall application throughput.
|
||||
When the `functions` config is added in `now.json` or `vercel.json`, it causes conflicts with this optimization, so it is opted-out.
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
Remove the `functions` config from your `now.json` or `vercel.json` to take advantage of this optimization.
|
||||
|
||||
### Useful Links
|
||||
|
||||
- [Functions Config Documentation](https://vercel.com/docs/configuration?query=functions#project/functions)
|
||||
16
errors/next-legacy-routes-optimized-lambdas.md
Normal file
16
errors/next-legacy-routes-optimized-lambdas.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# `@vercel/next` Legacy Routes Optimized Lambdas Opt-out
|
||||
|
||||
#### Why This Warning Occurred
|
||||
|
||||
`@vercel/next` by default now bundles pages into optimized functions, minimizing bootup time and increasing overall application throughput.
|
||||
When legacy `routes` are added in `now.json` or `vercel.json`, they cause conflicts with this optimization, so it is opted-out.
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
Migrate from using legacy `routes` to the new `rewrites`, `redirects`, and `headers` configurations in your `now.json` or `vercel.json` file or leverage them directly in your `next.config.js` with the built-in [custom routes support](https://github.com/zeit/next.js/issues/9081)
|
||||
|
||||
### Useful Links
|
||||
|
||||
- [Rewrites Documentation](https://vercel.com/docs/configuration?query=rewrites#project/rewrites)
|
||||
- [Redirects Documentation](https://vercel.com/docs/configuration?query=rewrites#project/redirects)
|
||||
- [Headers Documentation](https://vercel.com/docs/configuration?query=rewrites#project/headers)
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "2.3.2-canary.3",
|
||||
"version": "2.3.2-canary.4",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -82,6 +82,7 @@ export async function detectBuilders(
|
||||
defaultRoutes: Route[] | null;
|
||||
redirectRoutes: Route[] | null;
|
||||
rewriteRoutes: Route[] | null;
|
||||
errorRoutes: Route[] | null;
|
||||
}> {
|
||||
const errors: ErrorResponse[] = [];
|
||||
const warnings: ErrorResponse[] = [];
|
||||
@@ -99,6 +100,7 @@ export async function detectBuilders(
|
||||
defaultRoutes: null,
|
||||
redirectRoutes: null,
|
||||
rewriteRoutes: null,
|
||||
errorRoutes: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -154,6 +156,7 @@ export async function detectBuilders(
|
||||
defaultRoutes: null,
|
||||
redirectRoutes: null,
|
||||
rewriteRoutes: null,
|
||||
errorRoutes: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -231,6 +234,7 @@ export async function detectBuilders(
|
||||
redirectRoutes: null,
|
||||
defaultRoutes: null,
|
||||
rewriteRoutes: null,
|
||||
errorRoutes: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -272,6 +276,7 @@ export async function detectBuilders(
|
||||
redirectRoutes: null,
|
||||
defaultRoutes: null,
|
||||
rewriteRoutes: null,
|
||||
errorRoutes: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -309,6 +314,7 @@ export async function detectBuilders(
|
||||
redirectRoutes: routesResult.redirectRoutes,
|
||||
defaultRoutes: routesResult.defaultRoutes,
|
||||
rewriteRoutes: routesResult.rewriteRoutes,
|
||||
errorRoutes: routesResult.errorRoutes,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -898,10 +904,17 @@ function getRouteResult(
|
||||
defaultRoutes: Route[];
|
||||
redirectRoutes: Route[];
|
||||
rewriteRoutes: Route[];
|
||||
errorRoutes: Route[];
|
||||
} {
|
||||
const defaultRoutes: Route[] = [];
|
||||
const redirectRoutes: Route[] = [];
|
||||
const rewriteRoutes: Route[] = [];
|
||||
const errorRoutes: Route[] = [];
|
||||
const isNextjs =
|
||||
frontendBuilder &&
|
||||
((frontendBuilder.use && frontendBuilder.use.startsWith('@vercel/next')) ||
|
||||
(frontendBuilder.config &&
|
||||
frontendBuilder.config.framework === 'nextjs'));
|
||||
|
||||
if (apiRoutes && apiRoutes.length > 0) {
|
||||
if (options.featHandleMiss) {
|
||||
@@ -968,10 +981,21 @@ function getRouteResult(
|
||||
});
|
||||
}
|
||||
|
||||
if (options.featHandleMiss && !isNextjs) {
|
||||
// Exclude Next.js to avoid overriding custom error page
|
||||
// https://nextjs.org/docs/advanced-features/custom-error-page
|
||||
errorRoutes.push({
|
||||
status: 404,
|
||||
src: '^/(?!.*api).*$',
|
||||
dest: options.cleanUrls ? '/404' : '/404.html',
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -868,12 +868,14 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, pkg, { featHandleMiss });
|
||||
expect(builders![0].use).toBe('@vercel/next');
|
||||
expect(errors).toBe(null);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(redirectRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([]);
|
||||
expect(errorRoutes).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it('package.json + no build + next', async () => {
|
||||
@@ -888,12 +890,14 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, pkg, { featHandleMiss });
|
||||
expect(builders![0].use).toBe('@vercel/next');
|
||||
expect(errors).toBe(null);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(redirectRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([]);
|
||||
expect(errorRoutes).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it('package.json + no build', async () => {
|
||||
@@ -914,12 +918,15 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, null, { featHandleMiss });
|
||||
expect(builders).toBe(null);
|
||||
expect(errors).toBe(null);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(redirectRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([]);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('no package.json + public', async () => {
|
||||
@@ -930,6 +937,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, null, { featHandleMiss });
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
expect(errors).toBe(null);
|
||||
@@ -940,6 +948,8 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(redirectRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes!.length).toBe(1);
|
||||
expect((rewriteRoutes![0] as Source).status).toBe(404);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('no package.json + no build + raw static + api', async () => {
|
||||
@@ -950,6 +960,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, null, { featHandleMiss });
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/users.js');
|
||||
@@ -964,6 +975,8 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(redirectRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes!.length).toBe(1);
|
||||
expect((rewriteRoutes![0] as Source).status).toBe(404);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('package.json + no build + root + api', async () => {
|
||||
@@ -991,6 +1004,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, undefined, { featHandleMiss });
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/[endpoint]/[id].js');
|
||||
@@ -1003,6 +1017,8 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(rewriteRoutes!.length).toBe(2);
|
||||
expect((rewriteRoutes![0] as Source).src).toBe('^/api/([^/]+)/([^/]+)$');
|
||||
expect((rewriteRoutes![1] as Source).status).toBe(404);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('api + next + public', async () => {
|
||||
@@ -1017,6 +1033,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, pkg, { featHandleMiss });
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/endpoint.js');
|
||||
@@ -1030,6 +1047,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(redirectRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes!.length).toBe(1);
|
||||
expect((rewriteRoutes![0] as Source).status).toBe(404);
|
||||
expect(errorRoutes).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it('api + next + raw static', async () => {
|
||||
@@ -1044,6 +1062,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, pkg, { featHandleMiss });
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/endpoint.js');
|
||||
@@ -1057,6 +1076,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(redirectRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes!.length).toBe(1);
|
||||
expect((rewriteRoutes![0] as Source).status).toBe(404);
|
||||
expect(errorRoutes).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it('api + raw static', async () => {
|
||||
@@ -1067,6 +1087,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, null, { featHandleMiss });
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/endpoint.js');
|
||||
@@ -1080,6 +1101,8 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(redirectRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes!.length).toBe(1);
|
||||
expect((rewriteRoutes![0] as Source).status).toBe(404);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('api + raw static + package.json no build script', async () => {
|
||||
@@ -1094,6 +1117,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, pkg, { featHandleMiss });
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/version.js');
|
||||
@@ -1107,6 +1131,8 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(redirectRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes!.length).toBe(1);
|
||||
expect((rewriteRoutes![0] as Source).status).toBe(404);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('api + public', async () => {
|
||||
@@ -1117,7 +1143,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
'README.md',
|
||||
];
|
||||
|
||||
const { builders } = await detectBuilders(files, undefined, {
|
||||
const { builders, errorRoutes } = await detectBuilders(files, undefined, {
|
||||
featHandleMiss,
|
||||
});
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
@@ -1125,6 +1151,8 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
expect(builders![1].src).toBe('public/**/*');
|
||||
expect(builders!.length).toBe(2);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('api go with test files', async () => {
|
||||
@@ -1143,21 +1171,26 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
'api/src/controllers/user.module_test.go',
|
||||
];
|
||||
|
||||
const { builders } = await detectBuilders(files, undefined, {
|
||||
const { builders, errorRoutes } = await detectBuilders(files, undefined, {
|
||||
featHandleMiss,
|
||||
});
|
||||
expect(builders!.length).toBe(7);
|
||||
expect(builders!.some(b => b.src.endsWith('_test.go'))).toBe(false);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('just public', async () => {
|
||||
const files = ['public/index.html', 'public/favicon.ico', 'README.md'];
|
||||
|
||||
const { builders } = await detectBuilders(files, undefined, {
|
||||
const { builders, errorRoutes } = await detectBuilders(files, undefined, {
|
||||
featHandleMiss,
|
||||
});
|
||||
expect(builders![0].src).toBe('public/**/*');
|
||||
expect(builders![0].use).toBe('@vercel/static');
|
||||
expect(builders!.length).toBe(1);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('next + public', async () => {
|
||||
@@ -1167,10 +1200,13 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
};
|
||||
const files = ['package.json', 'public/index.html', 'README.md'];
|
||||
|
||||
const { builders } = await detectBuilders(files, pkg, { featHandleMiss });
|
||||
const { builders, errorRoutes } = await detectBuilders(files, pkg, {
|
||||
featHandleMiss,
|
||||
});
|
||||
expect(builders![0].use).toBe('@vercel/next');
|
||||
expect(builders![0].src).toBe('package.json');
|
||||
expect(builders!.length).toBe(1);
|
||||
expect(errorRoutes!.length).toBe(0);
|
||||
});
|
||||
|
||||
it('nuxt', async () => {
|
||||
@@ -1180,10 +1216,14 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
};
|
||||
const files = ['package.json', 'pages/index.js'];
|
||||
|
||||
const { builders } = await detectBuilders(files, pkg, { featHandleMiss });
|
||||
const { builders, errorRoutes } = await detectBuilders(files, pkg, {
|
||||
featHandleMiss,
|
||||
});
|
||||
expect(builders![0].use).toBe('@vercel/static-build');
|
||||
expect(builders![0].src).toBe('package.json');
|
||||
expect(builders!.length).toBe(1);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('nuxt + tag canary', async () => {
|
||||
@@ -1193,23 +1233,29 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
};
|
||||
const files = ['package.json', 'pages/index.js'];
|
||||
|
||||
const { builders } = await detectBuilders(files, pkg, {
|
||||
const { builders, errorRoutes } = await detectBuilders(files, pkg, {
|
||||
tag: 'canary',
|
||||
featHandleMiss,
|
||||
});
|
||||
expect(builders![0].use).toBe('@vercel/static-build@canary');
|
||||
expect(builders![0].src).toBe('package.json');
|
||||
expect(builders!.length).toBe(1);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('package.json with no build + api', async () => {
|
||||
const pkg = { dependencies: { next: '9.0.0' } };
|
||||
const files = ['package.json', 'api/[endpoint].js'];
|
||||
|
||||
const { builders } = await detectBuilders(files, pkg, { featHandleMiss });
|
||||
const { builders, errorRoutes } = await detectBuilders(files, pkg, {
|
||||
featHandleMiss,
|
||||
});
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/[endpoint].js');
|
||||
expect(builders!.length).toBe(1);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('package.json with no build + public directory', async () => {
|
||||
@@ -1328,7 +1374,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
it('many static files + one api file', async () => {
|
||||
const files = Array.from({ length: 5000 }).map((_, i) => `file${i}.html`);
|
||||
files.push('api/index.ts');
|
||||
const { builders } = await detectBuilders(files, undefined, {
|
||||
const { builders, errorRoutes } = await detectBuilders(files, undefined, {
|
||||
featHandleMiss,
|
||||
});
|
||||
|
||||
@@ -1337,6 +1383,8 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(builders![0].src).toBe('api/index.ts');
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
expect(builders![1].src).toBe('!{api/**,package.json}');
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('functions with nextjs', async () => {
|
||||
@@ -1685,6 +1733,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, null, {
|
||||
projectSettings,
|
||||
featHandleMiss,
|
||||
@@ -1697,6 +1746,8 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(redirectRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([]);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('Custom static output directory with api', async () => {
|
||||
@@ -1711,6 +1762,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, null, {
|
||||
projectSettings,
|
||||
featHandleMiss,
|
||||
@@ -1726,6 +1778,8 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(redirectRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes!.length).toBe(1);
|
||||
expect((rewriteRoutes![0] as Source).status).toBe(404);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('Framework with non-package.json entrypoint', async () => {
|
||||
@@ -1734,7 +1788,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
framework: 'hugo',
|
||||
};
|
||||
|
||||
const { builders } = await detectBuilders(files, null, {
|
||||
const { builders, errorRoutes } = await detectBuilders(files, null, {
|
||||
projectSettings,
|
||||
featHandleMiss,
|
||||
});
|
||||
@@ -1749,6 +1803,8 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('No framework, only package.json', async () => {
|
||||
@@ -1759,7 +1815,9 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const { builders } = await detectBuilders(files, pkg, { featHandleMiss });
|
||||
const { builders, errorRoutes } = await detectBuilders(files, pkg, {
|
||||
featHandleMiss,
|
||||
});
|
||||
|
||||
expect(builders).toEqual([
|
||||
{
|
||||
@@ -1770,13 +1828,15 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('Framework with an API', async () => {
|
||||
const files = ['config.rb', 'api/date.rb'];
|
||||
const projectSettings = { framework: 'middleman' };
|
||||
|
||||
const { builders } = await detectBuilders(files, null, {
|
||||
const { builders, errorRoutes } = await detectBuilders(files, null, {
|
||||
projectSettings,
|
||||
featHandleMiss,
|
||||
});
|
||||
@@ -1798,6 +1858,8 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('Error for non-api functions', async () => {
|
||||
@@ -1847,13 +1909,19 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
it('All static if `buildCommand` is an empty string with an `outputDirectory`', async () => {
|
||||
const files = ['out/index.html'];
|
||||
const projectSettings = { buildCommand: '', outputDirectory: 'out' };
|
||||
const { builders, errors } = await detectBuilders(files, null, {
|
||||
projectSettings,
|
||||
featHandleMiss,
|
||||
});
|
||||
const { builders, errors, errorRoutes } = await detectBuilders(
|
||||
files,
|
||||
null,
|
||||
{
|
||||
projectSettings,
|
||||
featHandleMiss,
|
||||
}
|
||||
);
|
||||
expect(errors).toBe(null);
|
||||
expect(builders![0]!.use).toBe('@vercel/static');
|
||||
expect(builders![0]!.src).toBe('out/**/*');
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('do not require build script when `buildCommand` is an empty string', async () => {
|
||||
@@ -2029,9 +2097,13 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
||||
{
|
||||
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
|
||||
|
||||
const { defaultRoutes, rewriteRoutes } = await detectBuilders(files, null, {
|
||||
featHandleMiss,
|
||||
});
|
||||
const { defaultRoutes, rewriteRoutes, errorRoutes } = await detectBuilders(
|
||||
files,
|
||||
null,
|
||||
{
|
||||
featHandleMiss,
|
||||
}
|
||||
);
|
||||
expect(defaultRoutes).toStrictEqual([
|
||||
{ handle: 'miss' },
|
||||
{
|
||||
@@ -2047,6 +2119,44 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
expect(errorRoutes).toStrictEqual([
|
||||
{
|
||||
status: 404,
|
||||
src: '^/(?!.*api).*$',
|
||||
dest: '/404.html',
|
||||
},
|
||||
]);
|
||||
|
||||
const pattern = new RegExp(errorRoutes![0].src!);
|
||||
|
||||
[
|
||||
'/',
|
||||
'/index.html',
|
||||
'/page.html',
|
||||
'/page',
|
||||
'/another/index.html',
|
||||
'/another/page.html',
|
||||
'/another/page',
|
||||
'/another/sub/index.html',
|
||||
'/another/sub/page.html',
|
||||
'/another/sub/page',
|
||||
].forEach(file => {
|
||||
expect(file).toMatch(pattern);
|
||||
});
|
||||
|
||||
[
|
||||
'/api',
|
||||
'/api/',
|
||||
'/api/index.html',
|
||||
'/api/page.html',
|
||||
'/api/page',
|
||||
'/api/sub',
|
||||
'/api/sub/index.html',
|
||||
'/api/sub/page.html',
|
||||
'/api/sub/page',
|
||||
].forEach(file => {
|
||||
expect(file).not.toMatch(pattern);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
@@ -2330,6 +2440,7 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, null, options);
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
@@ -2340,6 +2451,13 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
expect(errorRoutes).toStrictEqual([
|
||||
{
|
||||
status: 404,
|
||||
src: '^/(?!.*api).*$',
|
||||
dest: '/404',
|
||||
},
|
||||
]);
|
||||
|
||||
// expected redirect should match inputs
|
||||
const getLocation = createReplaceLocation(redirectRoutes);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "19.0.2-canary.11",
|
||||
"version": "19.0.2-canary.14",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Now",
|
||||
@@ -62,9 +62,9 @@
|
||||
"node": ">= 10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.3.2-canary.3",
|
||||
"@vercel/go": "1.1.2-canary.1",
|
||||
"@vercel/next": "2.6.3-canary.5",
|
||||
"@vercel/build-utils": "2.3.2-canary.4",
|
||||
"@vercel/go": "1.1.2-canary.2",
|
||||
"@vercel/next": "2.6.3-canary.6",
|
||||
"@vercel/node": "1.6.2-canary.5",
|
||||
"@vercel/python": "1.2.2-canary.2",
|
||||
"@vercel/ruby": "1.2.2-canary.1",
|
||||
|
||||
@@ -114,6 +114,7 @@ export default class DevServer {
|
||||
public cwd: string;
|
||||
public debug: boolean;
|
||||
public output: Output;
|
||||
public proxy: httpProxy;
|
||||
public envConfigs: EnvConfigs;
|
||||
public frameworkSlug: string | null;
|
||||
public files: BuilderInputs;
|
||||
@@ -124,7 +125,6 @@ export default class DevServer {
|
||||
private caseSensitive: boolean;
|
||||
private apiDir: string | null;
|
||||
private apiExtensions: Set<string>;
|
||||
private proxy: httpProxy;
|
||||
private server: http.Server;
|
||||
private stopping: boolean;
|
||||
private buildMatches: Map<string, BuildMatch>;
|
||||
@@ -581,6 +581,7 @@ export default class DevServer {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, pkg, {
|
||||
tag: getDistTag(cliPkg.version) === 'canary' ? 'canary' : 'latest',
|
||||
functions: config.functions,
|
||||
@@ -606,20 +607,25 @@ export default class DevServer {
|
||||
|
||||
config.builds = config.builds || [];
|
||||
config.builds.push(...builders);
|
||||
|
||||
const routes: Route[] = [];
|
||||
const { routes: nowConfigRoutes } = config;
|
||||
routes.push(...(redirectRoutes || []));
|
||||
routes.push(
|
||||
...appendRoutesToPhase({
|
||||
routes: nowConfigRoutes,
|
||||
newRoutes: rewriteRoutes,
|
||||
phase: 'filesystem',
|
||||
})
|
||||
);
|
||||
routes.push(...(defaultRoutes || []));
|
||||
config.routes = routes;
|
||||
}
|
||||
|
||||
let routes: Route[] = [];
|
||||
const { routes: nowConfigRoutes } = config;
|
||||
routes.push(...(redirectRoutes || []));
|
||||
routes.push(
|
||||
...appendRoutesToPhase({
|
||||
routes: nowConfigRoutes,
|
||||
newRoutes: rewriteRoutes,
|
||||
phase: 'filesystem',
|
||||
})
|
||||
);
|
||||
routes = appendRoutesToPhase({
|
||||
routes,
|
||||
newRoutes: errorRoutes,
|
||||
phase: 'error',
|
||||
});
|
||||
routes.push(...(defaultRoutes || []));
|
||||
config.routes = routes;
|
||||
}
|
||||
|
||||
if (Array.isArray(config.builds)) {
|
||||
@@ -1323,8 +1329,7 @@ export default class DevServer {
|
||||
const handleMap = getRoutesTypes(routes);
|
||||
const missRoutes = handleMap.get('miss') || [];
|
||||
const hitRoutes = handleMap.get('hit') || [];
|
||||
handleMap.delete('miss');
|
||||
handleMap.delete('hit');
|
||||
const errorRoutes = handleMap.get('error') || [];
|
||||
const phases: (HandleValue | null)[] = [null, 'filesystem'];
|
||||
|
||||
let routeResult: RouteResult | null = null;
|
||||
@@ -1362,7 +1367,7 @@ export default class DevServer {
|
||||
|
||||
debug(`ProxyPass: ${destUrl}`);
|
||||
this.setResponseHeaders(res, nowRequestId);
|
||||
return proxyPass(req, res, destUrl, this.proxy, this.output);
|
||||
return proxyPass(req, res, destUrl, this, nowRequestId);
|
||||
}
|
||||
|
||||
match = await findBuildMatch(
|
||||
@@ -1431,6 +1436,32 @@ export default class DevServer {
|
||||
routeResult.status = prevStatus;
|
||||
}
|
||||
|
||||
if (!match && errorRoutes.length > 0) {
|
||||
// error phase
|
||||
const routeResultForError = await devRouter(
|
||||
getReqUrl(routeResult),
|
||||
req.method,
|
||||
errorRoutes,
|
||||
this,
|
||||
routeResult.headers,
|
||||
[],
|
||||
'error'
|
||||
);
|
||||
|
||||
const matchForError = await findBuildMatch(
|
||||
this.buildMatches,
|
||||
this.files,
|
||||
routeResultForError.dest,
|
||||
this
|
||||
);
|
||||
|
||||
if (matchForError) {
|
||||
// error phase only applies if the file was found
|
||||
routeResult = routeResultForError;
|
||||
match = matchForError;
|
||||
}
|
||||
}
|
||||
|
||||
statusCode = routeResult.status;
|
||||
|
||||
if (match) {
|
||||
@@ -1475,8 +1506,8 @@ export default class DevServer {
|
||||
req,
|
||||
res,
|
||||
`http://localhost:${this.devProcessPort}`,
|
||||
this.proxy,
|
||||
this.output,
|
||||
this,
|
||||
nowRequestId,
|
||||
false
|
||||
);
|
||||
}
|
||||
@@ -1560,7 +1591,7 @@ export default class DevServer {
|
||||
req,
|
||||
res,
|
||||
nowRequestId,
|
||||
'NO_STATUS_CODE_FROM_DEV_SERVER',
|
||||
'NO_RESPONSE_FROM_FUNCTION',
|
||||
502
|
||||
);
|
||||
return;
|
||||
@@ -1595,8 +1626,8 @@ export default class DevServer {
|
||||
req,
|
||||
res,
|
||||
`http://localhost:${port}`,
|
||||
this.proxy,
|
||||
this.output,
|
||||
this,
|
||||
nowRequestId,
|
||||
false
|
||||
);
|
||||
} else {
|
||||
@@ -1625,8 +1656,8 @@ export default class DevServer {
|
||||
req,
|
||||
res,
|
||||
`http://localhost:${this.devProcessPort}`,
|
||||
this.proxy,
|
||||
this.output,
|
||||
this,
|
||||
nowRequestId,
|
||||
false
|
||||
);
|
||||
}
|
||||
@@ -1720,7 +1751,7 @@ export default class DevServer {
|
||||
req,
|
||||
res,
|
||||
nowRequestId,
|
||||
'NO_STATUS_CODE_FROM_LAMBDA',
|
||||
'NO_RESPONSE_FROM_FUNCTION',
|
||||
502
|
||||
);
|
||||
return;
|
||||
@@ -1936,24 +1967,27 @@ function proxyPass(
|
||||
req: http.IncomingMessage,
|
||||
res: http.ServerResponse,
|
||||
dest: string,
|
||||
proxy: httpProxy,
|
||||
output: Output,
|
||||
devServer: DevServer,
|
||||
nowRequestId: string,
|
||||
ignorePath: boolean = true
|
||||
): void {
|
||||
return proxy.web(
|
||||
return devServer.proxy.web(
|
||||
req,
|
||||
res,
|
||||
{ target: dest, ignorePath },
|
||||
(error: NodeJS.ErrnoException) => {
|
||||
// If the client hangs up a socket, we do not
|
||||
// want to do anything, as the client just expects
|
||||
// the connection to be closed.
|
||||
if (error.code === 'ECONNRESET') {
|
||||
res.end();
|
||||
return;
|
||||
devServer.output.error(
|
||||
`Failed to complete request to ${req.url}: ${error}`
|
||||
);
|
||||
if (!res.headersSent) {
|
||||
devServer.sendError(
|
||||
req,
|
||||
res,
|
||||
nowRequestId,
|
||||
'NO_RESPONSE_FROM_FUNCTION',
|
||||
502
|
||||
);
|
||||
}
|
||||
|
||||
output.error(`Failed to complete request to ${req.url}: ${error}`);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,22 +44,16 @@
|
||||
<p>
|
||||
<ul>
|
||||
<li>
|
||||
If you are a visitor; contact the website owner or try again later.
|
||||
</li>
|
||||
<li>
|
||||
If you are the owner; <a href="/_logs" target="_blank">check the logs</a> for the application error.
|
||||
Check the logs in your terminal window to see the application error.
|
||||
</li>
|
||||
</ul>
|
||||
<a target="_blank" href="https://vercel.com/docs/router-status/{{= it.http_status_code }}" class="docs-link" rel="noopener noreferrer">Developer Documentation →</a>
|
||||
<a target="_blank" href="https://vercel.com/docs/error/application/{{= it.error_code }}" class="docs-link" rel="noopener noreferrer">Developer Documentation →</a>
|
||||
</p>
|
||||
{{??}}
|
||||
<p>
|
||||
<ul>
|
||||
<li>
|
||||
If you are a visitor, please try again later.
|
||||
</li>
|
||||
<li>
|
||||
If you are the owner, no action is needed. Our engineers have been notified.
|
||||
Please open a <a target="_blank" href="https://github.com/vercel/vercel/issues/new/choose">GitHub issue</a> describing the problem you are experiencing with <code>vercel dev</code>.
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
1
packages/now-cli/test/dev/fixtures/00-list-directory/.gitignore
vendored
Normal file
1
packages/now-cli/test/dev/fixtures/00-list-directory/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.vercel
|
||||
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
function Custom404() {
|
||||
return (
|
||||
<main>
|
||||
<h1>Custom Gatsby 404</h1>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
export default Custom404;
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function Custom404() {
|
||||
return <h1>Custom Next 404</h1>;
|
||||
}
|
||||
1
packages/now-cli/test/dev/fixtures/public-and-api/.gitignore
vendored
Normal file
1
packages/now-cli/test/dev/fixtures/public-and-api/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!public
|
||||
@@ -0,0 +1 @@
|
||||
Custom 404 Page
|
||||
1
packages/now-cli/test/dev/fixtures/routes-check-true-status/.gitignore
vendored
Normal file
1
packages/now-cli/test/dev/fixtures/routes-check-true-status/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.vercel
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = (_req, res) => {
|
||||
res.end('Hello');
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
Exact Custom 404
|
||||
@@ -0,0 +1 @@
|
||||
Home Page
|
||||
@@ -0,0 +1 @@
|
||||
Custom User 404
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"version": 2,
|
||||
"routes": [
|
||||
{ "src": "/exact", "status": 404, "dest": "/exact-404.html" },
|
||||
{ "handle": "filesystem" },
|
||||
{ "src": "/(.*)", "status": 404, "dest": "/user-404.html" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Custom 404 Page
|
||||
@@ -0,0 +1 @@
|
||||
The about page
|
||||
@@ -0,0 +1 @@
|
||||
Contact Me Subdirectory
|
||||
@@ -0,0 +1 @@
|
||||
This is the home page
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"cleanUrls": true
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Custom 404 Page
|
||||
@@ -0,0 +1 @@
|
||||
The about page
|
||||
@@ -0,0 +1 @@
|
||||
Contact Subdirectory
|
||||
@@ -0,0 +1 @@
|
||||
This is the home page
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"version": 2,
|
||||
"trailingSlash": true
|
||||
}
|
||||
@@ -391,6 +391,17 @@ test(
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] validate routes that use custom 404 page',
|
||||
testFixtureStdio('routes-custom-404', async testPath => {
|
||||
await testPath(200, '/', 'Home Page');
|
||||
await testPath(404, '/nothing', 'Custom User 404');
|
||||
await testPath(404, '/exact', 'Exact Custom 404');
|
||||
await testPath(200, '/api/hello', 'Hello');
|
||||
await testPath(404, '/api/nothing', 'Custom User 404');
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] handles miss after route',
|
||||
testFixtureStdio('handle-miss-after-route', async testPath => {
|
||||
@@ -477,7 +488,8 @@ test(
|
||||
await testPath(200, '/api/date', /current date/);
|
||||
await testPath(200, '/api/rand', /random number/);
|
||||
await testPath(200, '/api/rand.js', /random number/);
|
||||
await testPath(404, '/api/api');
|
||||
await testPath(404, '/api/api', /NOT_FOUND/m);
|
||||
await testPath(404, '/nothing', /Custom 404 Page/);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -676,6 +688,17 @@ test(
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] should serve custom 404 when `cleanUrls: true`',
|
||||
testFixtureStdio('test-clean-urls-custom-404', async testPath => {
|
||||
await testPath(200, '/', 'This is the home page');
|
||||
await testPath(200, '/about', 'The about page');
|
||||
await testPath(200, '/contact/me', 'Contact Me Subdirectory');
|
||||
await testPath(404, '/nothing', 'Custom 404 Page');
|
||||
await testPath(404, '/nothing/', 'Custom 404 Page');
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] test cleanUrls and trailingSlash serve correct content',
|
||||
testFixtureStdio('test-clean-urls-trailing-slash', async testPath => {
|
||||
@@ -744,6 +767,16 @@ test(
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] should serve custom 404 when `trailingSlash: true`',
|
||||
testFixtureStdio('test-trailing-slash-custom-404', async testPath => {
|
||||
await testPath(200, '/', 'This is the home page');
|
||||
await testPath(200, '/about.html', 'The about page');
|
||||
await testPath(200, '/contact/', 'Contact Subdirectory');
|
||||
await testPath(404, '/nothing/', 'Custom 404 Page');
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] test trailingSlash false serve correct content',
|
||||
testFixtureStdio('test-trailing-slash-false', async testPath => {
|
||||
@@ -905,6 +938,8 @@ test(
|
||||
'[vercel dev] 10-nextjs-node',
|
||||
testFixtureStdio('10-nextjs-node', async testPath => {
|
||||
await testPath(200, '/', /Next.js \+ Node.js API/m);
|
||||
await testPath(200, '/api/date', new RegExp(new Date().getFullYear()));
|
||||
await testPath(404, '/nothing', /Custom Next 404/);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1363,4 +1398,3 @@ test(
|
||||
await testPath(200, '/index.css', 'This is index.css');
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
vc "github.com/vercel/go-bridge/go/bridgee"
|
||||
vc "github.com/vercel/go-bridge/go/bridge"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/go",
|
||||
"version": "1.1.2-canary.1",
|
||||
"version": "1.1.2-canary.2",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/next",
|
||||
"version": "2.6.3-canary.5",
|
||||
"version": "2.6.4",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
|
||||
@@ -20,6 +20,7 @@
|
||||
"devDependencies": {
|
||||
"@types/aws-lambda": "8.10.19",
|
||||
"@types/buffer-crc32": "0.2.0",
|
||||
"@types/find-up": "4.0.0",
|
||||
"@types/fs-extra": "8.0.0",
|
||||
"@types/next-server": "8.0.0",
|
||||
"@types/resolve-from": "5.0.1",
|
||||
@@ -30,6 +31,7 @@
|
||||
"buffer-crc32": "0.2.13",
|
||||
"escape-string-regexp": "3.0.0",
|
||||
"execa": "2.0.4",
|
||||
"find-up": "4.1.0",
|
||||
"fs-extra": "7.0.0",
|
||||
"get-port": "5.0.0",
|
||||
"resolve-from": "5.0.0",
|
||||
|
||||
@@ -70,6 +70,7 @@ import {
|
||||
syncEnvVars,
|
||||
validateEntrypoint,
|
||||
} from './utils';
|
||||
import findUp from 'find-up';
|
||||
|
||||
interface BuildParamsMeta {
|
||||
isDev: boolean | undefined;
|
||||
@@ -204,7 +205,39 @@ export const build = async ({
|
||||
}> => {
|
||||
validateEntrypoint(entrypoint);
|
||||
|
||||
const isSharedLambdas = config.sharedLambdas;
|
||||
const nowJsonPath = await findUp(['now.json', 'vercel.json'], {
|
||||
type: 'file',
|
||||
});
|
||||
let hasLegacyRoutes = false;
|
||||
const hasFunctionsConfig = !!config.functions;
|
||||
|
||||
if (nowJsonPath) {
|
||||
const nowJsonData = JSON.parse(await readFile(nowJsonPath, 'utf8'));
|
||||
|
||||
if (Array.isArray(nowJsonData.routes) && nowJsonData.routes.length > 0) {
|
||||
hasLegacyRoutes = true;
|
||||
console.warn(
|
||||
`WARNING: your application is being opted out of @vercel/next's optimized lambdas mode due to legacy routes in ${path.basename(
|
||||
nowJsonPath
|
||||
)}. http://err.sh/vercel/vercel/next-legacy-routes-optimized-lambdas`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasFunctionsConfig) {
|
||||
console.warn(
|
||||
`WARNING: Your application is being opted out of "@vercel/next" optimized lambdas mode due to \`functions\` config.\nMore info: http://err.sh/vercel/vercel/next-functions-config-optimized-lambdas`
|
||||
);
|
||||
}
|
||||
|
||||
// default to true but still allow opting out with the config
|
||||
const isSharedLambdas =
|
||||
!hasLegacyRoutes &&
|
||||
!hasFunctionsConfig &&
|
||||
typeof config.sharedLambdas === 'undefined'
|
||||
? true
|
||||
: !!config.sharedLambdas;
|
||||
|
||||
// Limit for max size each lambda can be, 50 MB if no custom limit
|
||||
const lambdaCompressedByteLimit = config.maxLambdaSize || 50 * 1000 * 1000;
|
||||
|
||||
@@ -1154,50 +1187,6 @@ export const build = async ({
|
||||
);
|
||||
|
||||
if (isSharedLambdas) {
|
||||
// since we combine the pages into lambda groups we need to merge
|
||||
// the lambda options into one this means they should only configure
|
||||
// lambda options for one page or one API as doing it for
|
||||
// another will override it
|
||||
const getMergedLambdaOptions = async (pageKeys: string[]) => {
|
||||
if (pageKeys.length === 0) return {};
|
||||
|
||||
const lambdaOptions = await getLambdaOptionsFromFunction({
|
||||
sourceFile: await getSourceFilePathFromPage({
|
||||
workPath,
|
||||
page: pageKeys[0],
|
||||
}),
|
||||
config,
|
||||
});
|
||||
|
||||
for (const page of pageKeys) {
|
||||
if (page === pageKeys[0]) continue;
|
||||
const sourceFile = await getSourceFilePathFromPage({
|
||||
workPath,
|
||||
page,
|
||||
});
|
||||
const newOptions = await getLambdaOptionsFromFunction({
|
||||
sourceFile,
|
||||
config,
|
||||
});
|
||||
|
||||
for (const key of Object.keys(newOptions)) {
|
||||
// eslint-disable-next-line
|
||||
if (typeof (newOptions as any)[key] !== 'undefined') {
|
||||
// eslint-disable-next-line
|
||||
(lambdaOptions as any)[key] = (newOptions as any)[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return lambdaOptions;
|
||||
};
|
||||
|
||||
const mergedPageLambdaOptions = await getMergedLambdaOptions(
|
||||
pageKeys.filter(page => !page.startsWith('api/'))
|
||||
);
|
||||
const mergedApiLambdaOptions = await getMergedLambdaOptions(
|
||||
pageKeys.filter(page => page.startsWith('api/'))
|
||||
);
|
||||
|
||||
const launcherPath = path.join(__dirname, 'templated-launcher-shared.js');
|
||||
const launcherData = await readFile(launcherPath, 'utf8');
|
||||
|
||||
@@ -1313,9 +1302,6 @@ export const build = async ({
|
||||
],
|
||||
handler: 'now__launcher.launcher',
|
||||
runtime: nodeVersion.runtime,
|
||||
...(group.isApiLambda
|
||||
? mergedApiLambdaOptions
|
||||
: mergedPageLambdaOptions),
|
||||
});
|
||||
} else {
|
||||
lambdas[
|
||||
@@ -1328,9 +1314,6 @@ export const build = async ({
|
||||
layers: pageLayers,
|
||||
handler: 'now__launcher.launcher',
|
||||
runtime: nodeVersion.runtime,
|
||||
...(group.isApiLambda
|
||||
? mergedApiLambdaOptions
|
||||
: mergedPageLambdaOptions),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
};
|
||||
@@ -1,226 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next",
|
||||
"config": {
|
||||
"sharedLambdas": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/lambda",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "MISS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/forever",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "PRERENDER"
|
||||
}
|
||||
},
|
||||
{ "delay": 2000 },
|
||||
{
|
||||
"path": "/forever",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "HIT"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/another",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "PRERENDER"
|
||||
}
|
||||
},
|
||||
{ "delay": 2000 },
|
||||
{
|
||||
"path": "/another",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "HIT"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/blog/post-1",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "PRERENDER"
|
||||
}
|
||||
},
|
||||
{ "delay": 2000 },
|
||||
{
|
||||
"path": "/blog/post-1",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "HIT"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/blog/post-2",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "PRERENDER"
|
||||
}
|
||||
},
|
||||
{ "delay": 2000 },
|
||||
{
|
||||
"path": "/blog/post-2",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "HIT"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/blog/post-3",
|
||||
"status": 200,
|
||||
"mustContain": "loading..."
|
||||
},
|
||||
{ "delay": 2000 },
|
||||
{
|
||||
"path": "/blog/post-3",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "/HIT|STALE/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/blog/post-4.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "MISS"
|
||||
}
|
||||
},
|
||||
{ "delay": 2000 },
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/blog/post-4.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "/HIT|STALE/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/blog/post-3",
|
||||
"status": 200,
|
||||
"mustContain": "post-3"
|
||||
},
|
||||
{
|
||||
"path": "/blog/post-1/comment-1",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "PRERENDER"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/blog/post-2/comment-2",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "PRERENDER"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/blog/post-3/comment-3",
|
||||
"status": 200,
|
||||
"mustContain": "loading..."
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/lambda.json",
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/forever.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "/PRERENDER|HIT/"
|
||||
}
|
||||
},
|
||||
{ "delay": 2000 },
|
||||
{
|
||||
"path": "/blog/post-3/comment-3",
|
||||
"status": 200,
|
||||
"mustContain": "comment-3"
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/forever.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "HIT"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/another.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "/HIT|STALE/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/another2.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "PRERENDER"
|
||||
}
|
||||
},
|
||||
{ "delay": 2000 },
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/another2.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "HIT"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/blog/post-1.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "/HIT|STALE|PRERENDER/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/blog/post-1337/comment-1337.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "PRERENDER"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/nofallback/one",
|
||||
"status": 200,
|
||||
"mustContain": "one"
|
||||
},
|
||||
{
|
||||
"path": "/nofallback/two",
|
||||
"status": 200,
|
||||
"mustContain": "two"
|
||||
},
|
||||
{
|
||||
"path": "/nofallback/nope",
|
||||
"status": 404,
|
||||
"mustContain": "This page could not be found"
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/nofallback/one.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "/HIT|STALE|PRERENDER/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/nofallback/two.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "/HIT|STALE|PRERENDER/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/nofallback/nope.json",
|
||||
"status": 404
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "9.2.3-canary.14",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getStaticProps() {
|
||||
return {
|
||||
props: {
|
||||
world: 'world',
|
||||
time: new Date().getTime(),
|
||||
},
|
||||
revalidate: 5,
|
||||
};
|
||||
}
|
||||
|
||||
export default ({ world, time }) => {
|
||||
return (
|
||||
<>
|
||||
<p>hello: {world}</p>
|
||||
<span>time: {time}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getStaticProps() {
|
||||
return {
|
||||
props: {
|
||||
world: 'world',
|
||||
time: new Date().getTime(),
|
||||
},
|
||||
revalidate: 5,
|
||||
};
|
||||
}
|
||||
|
||||
export default ({ world, time }) => {
|
||||
return (
|
||||
<>
|
||||
<p>hello: {world}</p>
|
||||
<span>time: {time}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getStaticPaths() {
|
||||
return {
|
||||
paths: [
|
||||
'/blog/post-1/comment-1',
|
||||
{ params: { post: 'post-2', comment: 'comment-2' } },
|
||||
'/blog/post-1337/comment-1337',
|
||||
],
|
||||
fallback: true,
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getStaticProps({ params }) {
|
||||
return {
|
||||
props: {
|
||||
post: params.post,
|
||||
comment: params.comment,
|
||||
time: new Date().getTime(),
|
||||
},
|
||||
revalidate: 2,
|
||||
};
|
||||
}
|
||||
|
||||
export default ({ post, comment, time }) => {
|
||||
if (!post) return <p>loading...</p>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>Post: {post}</p>
|
||||
<p>Comment: {comment}</p>
|
||||
<span>time: {time}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getStaticPaths() {
|
||||
return {
|
||||
paths: ['/blog/post-1', { params: { post: 'post-2' } }],
|
||||
fallback: true,
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getStaticProps({ params }) {
|
||||
if (params.post === 'post-10') {
|
||||
await new Promise(resolve => {
|
||||
setTimeout(() => resolve(), 1000);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
post: params.post,
|
||||
time: (await import('perf_hooks')).performance.now(),
|
||||
},
|
||||
revalidate: 10,
|
||||
};
|
||||
}
|
||||
|
||||
export default ({ post, time }) => {
|
||||
if (!post) return <p>loading...</p>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>Post: {post}</p>
|
||||
<span>time: {time}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getStaticProps() {
|
||||
return {
|
||||
props: {
|
||||
world: 'world',
|
||||
time: new Date().getTime(),
|
||||
},
|
||||
revalidate: false,
|
||||
};
|
||||
}
|
||||
|
||||
export default ({ world, time }) => {
|
||||
return (
|
||||
<>
|
||||
<p>hello: {world}</p>
|
||||
<span>time: {time}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export default () => 'Hi';
|
||||
@@ -1,5 +0,0 @@
|
||||
const Page = ({ data }) => <p>{data} world</p>;
|
||||
|
||||
Page.getInitialProps = () => ({ data: 'hello' });
|
||||
|
||||
export default Page;
|
||||
@@ -1,31 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getStaticPaths() {
|
||||
return {
|
||||
paths: ['/nofallback/one', { params: { slug: 'two' } }],
|
||||
fallback: false,
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getStaticProps({ params }) {
|
||||
return {
|
||||
props: {
|
||||
slug: params.slug,
|
||||
time: (await import('perf_hooks')).performance.now(),
|
||||
},
|
||||
revalidate: 10,
|
||||
};
|
||||
}
|
||||
|
||||
export default ({ slug, time }) => {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
Slug ({slug.length}): {slug}
|
||||
</p>
|
||||
<span>time: {time}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
!public
|
||||
@@ -1,211 +0,0 @@
|
||||
module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
experimental: {
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/to-another',
|
||||
destination: '/another/one',
|
||||
},
|
||||
{
|
||||
source: '/nav',
|
||||
destination: '/404',
|
||||
},
|
||||
{
|
||||
source: '/hello-world',
|
||||
destination: '/static/hello.txt',
|
||||
},
|
||||
{
|
||||
source: '/',
|
||||
destination: '/another',
|
||||
},
|
||||
{
|
||||
source: '/another',
|
||||
destination: '/multi-rewrites',
|
||||
},
|
||||
{
|
||||
source: '/first',
|
||||
destination: '/hello',
|
||||
},
|
||||
{
|
||||
source: '/second',
|
||||
destination: '/hello-again',
|
||||
},
|
||||
{
|
||||
source: '/to-hello',
|
||||
destination: '/hello',
|
||||
},
|
||||
{
|
||||
source: '/blog/post-1',
|
||||
destination: '/blog/post-2',
|
||||
},
|
||||
{
|
||||
source: '/test/:path',
|
||||
destination: '/:path',
|
||||
},
|
||||
{
|
||||
source: '/test-overwrite/:something/:another',
|
||||
destination: '/params/this-should-be-the-value',
|
||||
},
|
||||
{
|
||||
source: '/params/:something',
|
||||
destination: '/with-params',
|
||||
},
|
||||
{
|
||||
source: '/query-rewrite/:section/:name',
|
||||
destination: '/with-params?first=:section&second=:name',
|
||||
},
|
||||
{
|
||||
source: '/hidden/_next/:path*',
|
||||
destination: '/_next/:path*',
|
||||
},
|
||||
{
|
||||
source: '/api-hello',
|
||||
destination: '/api/hello',
|
||||
},
|
||||
{
|
||||
source: '/api-hello-regex/(.*)',
|
||||
destination: '/api/hello?name=:1',
|
||||
},
|
||||
{
|
||||
source: '/api-hello-param/:name',
|
||||
destination: '/api/hello?hello=:name',
|
||||
},
|
||||
{
|
||||
source: '/api-dynamic-param/:name',
|
||||
destination: '/api/dynamic/:name?hello=:name',
|
||||
},
|
||||
{
|
||||
source: '/:path/post-321',
|
||||
destination: '/with-params',
|
||||
},
|
||||
];
|
||||
},
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: '/redirect/me/to-about/:lang',
|
||||
destination: '/:lang/about',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/docs/router-status/:code',
|
||||
destination: '/docs/v2/network/status-codes#:code',
|
||||
statusCode: 301,
|
||||
},
|
||||
{
|
||||
source: '/docs/github',
|
||||
destination: '/docs/v2/advanced/now-for-github',
|
||||
statusCode: 301,
|
||||
},
|
||||
{
|
||||
source: '/docs/v2/advanced/:all(.*)',
|
||||
destination: '/docs/v2/more/:all',
|
||||
statusCode: 301,
|
||||
},
|
||||
{
|
||||
source: '/hello/:id/another',
|
||||
destination: '/blog/:id',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/redirect1',
|
||||
destination: '/',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/redirect2',
|
||||
destination: '/',
|
||||
statusCode: 301,
|
||||
},
|
||||
{
|
||||
source: '/redirect3',
|
||||
destination: '/another',
|
||||
statusCode: 302,
|
||||
},
|
||||
{
|
||||
source: '/redirect4',
|
||||
destination: '/',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/redir-chain1',
|
||||
destination: '/redir-chain2',
|
||||
statusCode: 301,
|
||||
},
|
||||
{
|
||||
source: '/redir-chain2',
|
||||
destination: '/redir-chain3',
|
||||
statusCode: 302,
|
||||
},
|
||||
{
|
||||
source: '/redir-chain3',
|
||||
destination: '/',
|
||||
statusCode: 303,
|
||||
},
|
||||
{
|
||||
source: '/to-external',
|
||||
destination: 'https://google.com',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/query-redirect/:section/:name',
|
||||
destination: '/with-params?first=:section&second=:name',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/named-like-unnamed/:0',
|
||||
destination: '/:0',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/redirect-override',
|
||||
destination: '/thank-you-next',
|
||||
permanent: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/add-header',
|
||||
headers: [
|
||||
{
|
||||
key: 'x-custom-header',
|
||||
value: 'hello world',
|
||||
},
|
||||
{
|
||||
key: 'x-another-header',
|
||||
value: 'hello again',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/my-headers/(.*)',
|
||||
headers: [
|
||||
{
|
||||
key: 'x-first-header',
|
||||
value: 'first',
|
||||
},
|
||||
{
|
||||
key: 'x-second-header',
|
||||
value: 'second',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'x-something',
|
||||
value: 'applied-everywhere',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,258 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next",
|
||||
"config": {
|
||||
"sharedLambdas": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
// should handle one-to-one rewrite successfully
|
||||
{
|
||||
"path": "/first",
|
||||
"mustContain": "hello"
|
||||
},
|
||||
|
||||
// should handle chained rewrites successfully
|
||||
{
|
||||
"path": "/",
|
||||
"mustContain": "multi-rewrites"
|
||||
},
|
||||
|
||||
// should not match dynamic route immediately after applying header
|
||||
{
|
||||
"path": "/blog/post-321",
|
||||
"mustContain": "with-params"
|
||||
},
|
||||
{
|
||||
"path": "/blog/post-321",
|
||||
"mustNotContain": "post-321"
|
||||
},
|
||||
|
||||
// should handle chained redirects successfully
|
||||
{
|
||||
"path": "/redir-chain1",
|
||||
"status": 301,
|
||||
"responseHeaders": {
|
||||
"location": "//redir-chain2/"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/redir-chain2",
|
||||
"status": 302,
|
||||
"responseHeaders": {
|
||||
"location": "//redir-chain3/"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/redir-chain3",
|
||||
"status": 303,
|
||||
"responseHeaders": {
|
||||
"location": "//$/"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
|
||||
// should redirect successfully with permanent: false
|
||||
{
|
||||
"path": "/redirect1",
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//$/"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
|
||||
// should redirect with params successfully
|
||||
{
|
||||
"path": "/hello/123/another",
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//blog/123/"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
|
||||
// should redirect with hash successfully
|
||||
{
|
||||
"path": "/docs/router-status/500",
|
||||
"status": 301,
|
||||
"responseHeaders": {
|
||||
"location": "/#500$/"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
|
||||
// should redirect successfully with provided statusCode
|
||||
{
|
||||
"path": "/redirect2",
|
||||
"status": 301,
|
||||
"responseHeaders": {
|
||||
"location": "//$/"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
|
||||
// should server static files through a rewrite
|
||||
{
|
||||
"path": "/hello-world",
|
||||
"mustContain": "hello world!"
|
||||
},
|
||||
|
||||
// should rewrite with params successfully
|
||||
{
|
||||
"path": "/test/hello",
|
||||
"mustContain": "Hello"
|
||||
},
|
||||
|
||||
// should double redirect successfully
|
||||
{
|
||||
"path": "/docs/github",
|
||||
"mustContain": "hi there"
|
||||
},
|
||||
|
||||
// should allow params in query for rewrite
|
||||
{
|
||||
"path": "/query-rewrite/hello/world?a=b",
|
||||
"mustContain": "\"a\":\"b\""
|
||||
},
|
||||
{
|
||||
"path": "/query-rewrite/hello/world?a=b",
|
||||
"mustContain": "\"section\":\"hello\""
|
||||
},
|
||||
{
|
||||
"path": "/query-rewrite/hello/world?a=b",
|
||||
"mustContain": "\"name\":\"world\""
|
||||
},
|
||||
{
|
||||
"path": "/query-rewrite/hello/world?a=b",
|
||||
"mustContain": "\"first\":\"hello\""
|
||||
},
|
||||
{
|
||||
"path": "/query-rewrite/hello/world?a=b",
|
||||
"mustContain": "\"second\":\"world\""
|
||||
},
|
||||
|
||||
// should not allow rewrite to override page file
|
||||
{
|
||||
"path": "/nav",
|
||||
"mustContain": "to-hello"
|
||||
},
|
||||
|
||||
// show allow redirect to override the page
|
||||
{
|
||||
"path": "/redirect-override",
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//thank-you-next$/"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
|
||||
// should match a page after a rewrite
|
||||
{
|
||||
"path": "/to-hello",
|
||||
"mustContain": "Hello"
|
||||
},
|
||||
|
||||
// should match dynamic route after rewrite
|
||||
{
|
||||
"path": "/blog/post-1",
|
||||
"mustContain": "post-2"
|
||||
},
|
||||
|
||||
// should match public file after rewrite
|
||||
{
|
||||
"path": "/blog/data.json",
|
||||
"mustContain": "\"hello\": \"world\""
|
||||
},
|
||||
|
||||
// should match /_next file after rewrite
|
||||
{
|
||||
"path": "/hidden/_next/static/testing-build-id/pages/hello.js",
|
||||
"mustContain": "createElement"
|
||||
},
|
||||
|
||||
// should allow redirecting to external resource
|
||||
{
|
||||
"path": "/to-external",
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "/google.com/"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
|
||||
// should apply headers for exact match
|
||||
{
|
||||
"path": "/add-header",
|
||||
"responseHeaders": {
|
||||
"x-custom-header": "hello world",
|
||||
"x-another-header": "hello again"
|
||||
}
|
||||
},
|
||||
|
||||
// should apply headers for multi match
|
||||
{
|
||||
"path": "/my-headers/first",
|
||||
"responseHeaders": {
|
||||
"x-first-header": "first",
|
||||
"x-second-header": "second"
|
||||
}
|
||||
},
|
||||
|
||||
// should handle basic api rewrite successfully
|
||||
{
|
||||
"path": "/api-hello",
|
||||
"mustContain": "{\"query\":{}}"
|
||||
},
|
||||
|
||||
// should handle api rewrite with param successfully
|
||||
{
|
||||
"path": "/api-hello-param/hello",
|
||||
"mustContain": "{\"query\":{\"hello\":\"hello\",\"name\":\"hello\"}}"
|
||||
},
|
||||
|
||||
// should handle encoded value in the pathname correctly
|
||||
{
|
||||
"path": "/redirect/me/to-about/%5Cgoogle.com",
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "/%5Cgoogle.com/about/"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
|
||||
// should route dynamic routes at the same level to page
|
||||
// lambdas correctly
|
||||
{
|
||||
"path": "/nested/teams/invite/hello-world",
|
||||
"mustContain": "hello-world"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export default () => 'hi'
|
||||
@@ -1 +0,0 @@
|
||||
export default async (req, res) => res.json({ query: req.query })
|
||||
@@ -1 +0,0 @@
|
||||
export default async (req, res) => res.json({ query: req.query });
|
||||
@@ -1,11 +0,0 @@
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
const Page = () => (
|
||||
<>
|
||||
<p>post: {useRouter().query.post}</p>
|
||||
</>
|
||||
)
|
||||
|
||||
Page.getInitialProps = () => ({ hello: 'world' })
|
||||
|
||||
export default Page
|
||||
@@ -1 +0,0 @@
|
||||
export default () => 'hi there';
|
||||
@@ -1,10 +0,0 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
export default () => (
|
||||
<>
|
||||
<h3 id="hello-again">Hello again</h3>
|
||||
<Link href="/nav">
|
||||
<a id="to-nav">to nav</a>
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
@@ -1,14 +0,0 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
const Page = () => (
|
||||
<>
|
||||
<h3 id="hello">Hello</h3>
|
||||
<Link href="/nav">
|
||||
<a id="to-nav">to nav</a>
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
|
||||
Page.getInitialProps = () => ({ hello: 'world' });
|
||||
|
||||
export default Page;
|
||||
@@ -1 +0,0 @@
|
||||
export default () => 'multi-rewrites';
|
||||
@@ -1,13 +0,0 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
export default () => (
|
||||
<>
|
||||
<h3 id="nav">Nav</h3>
|
||||
<Link href="/hello" as="/first">
|
||||
<a id="to-hello">to hello</a>
|
||||
</Link>
|
||||
<Link href="/hello-again" as="/second">
|
||||
<a id="to-hello-again">to hello-again</a>
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
@@ -1 +0,0 @@
|
||||
export default () => "hello from /[slug]/[team]/[id]"
|
||||
@@ -1,9 +0,0 @@
|
||||
function InviteCode(props) {
|
||||
return <p>invite code: {props.query?.inviteCode}</p>
|
||||
}
|
||||
|
||||
InviteCode.getInitialProps = ({ query }) => ({
|
||||
query
|
||||
})
|
||||
|
||||
export default InviteCode
|
||||
@@ -1,10 +0,0 @@
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
const Page = () => {
|
||||
const { query } = useRouter();
|
||||
return <p>{JSON.stringify(query)}</p>;
|
||||
};
|
||||
|
||||
Page.getInitialProps = () => ({ a: 'b' });
|
||||
|
||||
export default Page;
|
||||
@@ -1 +0,0 @@
|
||||
export default () => 'got to the page';
|
||||
@@ -1,10 +0,0 @@
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
const Page = () => {
|
||||
const { query } = useRouter();
|
||||
return <p>{JSON.stringify(query)}</p>;
|
||||
};
|
||||
|
||||
Page.getInitialProps = () => ({ hello: 'GIPGIP' });
|
||||
|
||||
export default Page;
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"hello": "world"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
hello world!
|
||||
@@ -15,7 +15,8 @@ it(
|
||||
buildResult: { output },
|
||||
} = await runBuildLambda(path.join(__dirname, 'standard'));
|
||||
expect(output['index']).toBeDefined();
|
||||
expect(output.goodbye).toBeDefined();
|
||||
expect(output.goodbye).not.toBeDefined();
|
||||
expect(output.__NEXT_PAGE_LAMBDA_0).toBeDefined();
|
||||
const filePaths = Object.keys(output);
|
||||
const serverlessError = filePaths.some(filePath =>
|
||||
filePath.match(/_error/)
|
||||
@@ -39,7 +40,9 @@ it(
|
||||
const {
|
||||
buildResult: { output },
|
||||
} = await runBuildLambda(path.join(__dirname, 'monorepo'));
|
||||
expect(output['www/index']).toBeDefined();
|
||||
|
||||
expect(output['www/index']).not.toBeDefined();
|
||||
expect(output['www/__NEXT_PAGE_LAMBDA_0']).toBeDefined();
|
||||
expect(output['www/static/test.txt']).toBeDefined();
|
||||
expect(output['www/data.txt']).toBeDefined();
|
||||
const filePaths = Object.keys(output);
|
||||
@@ -139,8 +142,9 @@ it(
|
||||
buildResult: { output },
|
||||
} = await runBuildLambda(path.join(__dirname, 'serverless-config'));
|
||||
|
||||
expect(output.index).toBeDefined();
|
||||
expect(output.goodbye).toBeDefined();
|
||||
expect(output.index).not.toBeDefined();
|
||||
expect(output.goodbye).not.toBeDefined();
|
||||
expect(output.__NEXT_PAGE_LAMBDA_0).toBeDefined();
|
||||
const filePaths = Object.keys(output);
|
||||
const serverlessError = filePaths.some(filePath =>
|
||||
filePath.match(/_error/)
|
||||
@@ -175,8 +179,9 @@ it(
|
||||
path.join(__dirname, 'serverless-config-monorepo-missing')
|
||||
);
|
||||
|
||||
expect(output['nested/index']).toBeDefined();
|
||||
expect(output['nested/goodbye']).toBeDefined();
|
||||
expect(output['nested/index']).not.toBeDefined();
|
||||
expect(output['nested/goodbye']).not.toBeDefined();
|
||||
expect(output['nested/__NEXT_PAGE_LAMBDA_0']).toBeDefined();
|
||||
const filePaths = Object.keys(output);
|
||||
const serverlessError = filePaths.some(filePath =>
|
||||
filePath.match(/_error/)
|
||||
@@ -208,8 +213,9 @@ it(
|
||||
path.join(__dirname, 'serverless-config-monorepo-present')
|
||||
);
|
||||
|
||||
expect(output['nested/index']).toBeDefined();
|
||||
expect(output['nested/goodbye']).toBeDefined();
|
||||
expect(output['nested/index']).not.toBeDefined();
|
||||
expect(output['nested/goodbye']).not.toBeDefined();
|
||||
expect(output['nested/__NEXT_PAGE_LAMBDA_0']).toBeDefined();
|
||||
const filePaths = Object.keys(output);
|
||||
const serverlessError = filePaths.some(filePath =>
|
||||
filePath.match(/_error/)
|
||||
@@ -275,7 +281,8 @@ it(
|
||||
} = await runBuildLambda(path.join(__dirname, 'serverless-config-object'));
|
||||
|
||||
expect(output['index']).toBeDefined();
|
||||
expect(output.goodbye).toBeDefined();
|
||||
expect(output.goodbye).not.toBeDefined();
|
||||
expect(output.__NEXT_PAGE_LAMBDA_0).toBeDefined();
|
||||
const filePaths = Object.keys(output);
|
||||
const serverlessError = filePaths.some(filePath =>
|
||||
filePath.match(/_error/)
|
||||
@@ -309,7 +316,8 @@ it(
|
||||
} = await runBuildLambda(path.join(__dirname, 'serverless-no-config'));
|
||||
|
||||
expect(output['index']).toBeDefined();
|
||||
expect(output.goodbye).toBeDefined();
|
||||
expect(output.goodbye).not.toBeDefined();
|
||||
expect(output.__NEXT_PAGE_LAMBDA_0).toBeDefined();
|
||||
const filePaths = Object.keys(output);
|
||||
const serverlessError = filePaths.some(filePath =>
|
||||
filePath.match(/_error/)
|
||||
|
||||
23
yarn.lock
23
yarn.lock
@@ -1696,6 +1696,13 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/find-up@4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/find-up/-/find-up-4.0.0.tgz#6b74a76670477a23f0793cfaf2dafc86df59723a"
|
||||
integrity sha512-QlRNKeOPFWKisbNtKVOOGXw3AeLbkw8UmT/EyEGM6brfqpYffKBcch7f1y40NYN9O90aK2+K0xBMDJfOAsg2qg==
|
||||
dependencies:
|
||||
find-up "*"
|
||||
|
||||
"@types/fs-extra@5.0.5":
|
||||
version "5.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.0.5.tgz#080d90a792f3fa2c5559eb44bd8ef840aae9104b"
|
||||
@@ -5159,6 +5166,14 @@ find-cache-dir@^2.0.0:
|
||||
make-dir "^2.0.0"
|
||||
pkg-dir "^3.0.0"
|
||||
|
||||
find-up@*, find-up@4.1.0, find-up@^4.0.0, find-up@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
|
||||
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
|
||||
dependencies:
|
||||
locate-path "^5.0.0"
|
||||
path-exists "^4.0.0"
|
||||
|
||||
find-up@^1.0.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
|
||||
@@ -5181,14 +5196,6 @@ find-up@^3.0.0:
|
||||
dependencies:
|
||||
locate-path "^3.0.0"
|
||||
|
||||
find-up@^4.0.0, find-up@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
|
||||
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
|
||||
dependencies:
|
||||
locate-path "^5.0.0"
|
||||
path-exists "^4.0.0"
|
||||
|
||||
flat-cache@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0"
|
||||
|
||||
Reference in New Issue
Block a user