mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-24 03:39:11 +00:00
Compare commits
34 Commits
vercel@20.
...
@vercel/bu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e18ff683b2 | ||
|
|
f28293a5a8 | ||
|
|
a4963a89c7 | ||
|
|
21df39fe8c | ||
|
|
5ad9d61451 | ||
|
|
8b5a2aa44f | ||
|
|
d0da1ce195 | ||
|
|
fd5d3b2921 | ||
|
|
d22bdeb8d0 | ||
|
|
c120fd82f9 | ||
|
|
2474a80ff1 | ||
|
|
cc1cdbe610 | ||
|
|
0b92f8ceee | ||
|
|
be82a88d1a | ||
|
|
64da08f0f2 | ||
|
|
2e957fce55 | ||
|
|
c7ead151f5 | ||
|
|
e2baf9b00f | ||
|
|
ba8ef7bc98 | ||
|
|
c5d04e0d4f | ||
|
|
b4f849418d | ||
|
|
b37c4f211e | ||
|
|
f06efe167c | ||
|
|
296d8da676 | ||
|
|
981e4183c6 | ||
|
|
1706a00cb9 | ||
|
|
d7bd03cf1d | ||
|
|
e27ff8d2e0 | ||
|
|
bc6da39cc2 | ||
|
|
727109c64a | ||
|
|
817410a8e2 | ||
|
|
e022e7b79c | ||
|
|
dda6c24a3d | ||
|
|
7c922a4092 |
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@@ -73,8 +73,8 @@ The logs of this deployment will contain the actual error which may help you to
|
||||
Some of the Builders use `@vercel/nft` to tree-shake files before deployment. If you suspect an error with this tree-shaking mechanism, you can create the following script in your project:
|
||||
|
||||
```js
|
||||
const trace = require('@vercel/nft');
|
||||
trace(['path/to/entrypoint.js'], {
|
||||
const { nodeFileTrace } = require('@vercel/nft');
|
||||
nodeFileTrace(['path/to/entrypoint.js'], {
|
||||
ts: true,
|
||||
mixedModules: true,
|
||||
})
|
||||
|
||||
@@ -31,7 +31,7 @@ function Index() {
|
||||
</h2>
|
||||
<p>
|
||||
<a
|
||||
href="https://github.com/vercel/vercel/blob/master/gatsby"
|
||||
href="https://github.com/vercel/vercel/tree/master/examples/gatsby"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "9.5.1",
|
||||
"next": "9.5.4",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1"
|
||||
}
|
||||
|
||||
@@ -56,7 +56,13 @@
|
||||
"outputDirectory": {
|
||||
"placeholder": "Next.js default"
|
||||
}
|
||||
}
|
||||
},
|
||||
"recommendedIntegrations": [
|
||||
{
|
||||
"id": "oac_5lUsiANun1DEzgLg0NZx5Es3",
|
||||
"dependencies": ["next-plugin-sentry", "next-sentry-source-maps"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Gatsby.js",
|
||||
|
||||
4
packages/frameworks/index.d.ts
vendored
4
packages/frameworks/index.d.ts
vendored
@@ -31,4 +31,8 @@ export interface Framework {
|
||||
devCommand: Setting;
|
||||
outputDirectory: Setting;
|
||||
};
|
||||
recommendedIntegrations?: {
|
||||
id: string;
|
||||
dependencies: string[];
|
||||
}[];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/frameworks",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2-canary.0",
|
||||
"main": "frameworks.json",
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
|
||||
19
packages/frameworks/test/frameworks.unit.test.ts
vendored
19
packages/frameworks/test/frameworks.unit.test.ts
vendored
@@ -97,6 +97,25 @@ const Schema = {
|
||||
outputDirectory: SchemaSettings,
|
||||
},
|
||||
},
|
||||
recommendedIntegrations: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
required: ['id', 'dependencies'],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
},
|
||||
dependencies: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "2.5.3",
|
||||
"version": "2.5.5-canary.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
@@ -29,7 +29,7 @@
|
||||
"@types/node-fetch": "^2.1.6",
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/yazl": "^2.4.1",
|
||||
"@vercel/frameworks": "0.1.1",
|
||||
"@vercel/frameworks": "0.1.2-canary.0",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"aggregate-error": "3.0.1",
|
||||
"async-retry": "1.2.3",
|
||||
|
||||
@@ -458,7 +458,10 @@ function detectFrontBuilder(
|
||||
config.outputDirectory = projectSettings.outputDirectory;
|
||||
}
|
||||
|
||||
if (pkg && (framework !== null || createdAt < Date.parse('2020-03-01'))) {
|
||||
if (
|
||||
pkg &&
|
||||
(framework === undefined || createdAt < Date.parse('2020-03-01'))
|
||||
) {
|
||||
const deps: PackageJson['dependencies'] = {
|
||||
...pkg.dependencies,
|
||||
...pkg.devDependencies,
|
||||
|
||||
@@ -1080,6 +1080,46 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(errorRoutes).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it('Using "Create React App" framework with `next` in dependencies should NOT autodetect Next.js for new projects', async () => {
|
||||
const pkg = {
|
||||
scripts: {
|
||||
dev: 'react-scripts start',
|
||||
build: 'react-scripts build',
|
||||
},
|
||||
dependencies: {
|
||||
next: '9.3.5',
|
||||
react: '16.13.1',
|
||||
'react-dom': '16.13.1',
|
||||
'react-scripts': '2.1.1',
|
||||
},
|
||||
};
|
||||
const files = ['package.json', 'src/index.js', 'public/favicon.ico'];
|
||||
const projectSettings = {
|
||||
framework: 'create-react-app',
|
||||
buildCommand: 'react-scripts build',
|
||||
createdAt: Date.parse('2020-07-01'),
|
||||
};
|
||||
|
||||
const { builders, errorRoutes } = await detectBuilders(files, pkg, {
|
||||
projectSettings,
|
||||
featHandleMiss,
|
||||
});
|
||||
|
||||
expect(builders).toEqual([
|
||||
{
|
||||
use: '@vercel/static-build',
|
||||
src: 'package.json',
|
||||
config: {
|
||||
zeroConfig: true,
|
||||
framework: projectSettings.framework,
|
||||
buildCommand: projectSettings.buildCommand,
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('Using "Other" framework with Storybook should NOT autodetect Next.js for new projects', async () => {
|
||||
const pkg = {
|
||||
scripts: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "20.1.2-canary.2",
|
||||
"version": "20.1.3-canary.0",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -61,9 +61,9 @@
|
||||
"node": ">= 10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.5.3",
|
||||
"@vercel/build-utils": "2.5.5-canary.0",
|
||||
"@vercel/go": "1.1.6",
|
||||
"@vercel/node": "1.8.4-canary.0",
|
||||
"@vercel/node": "1.8.4",
|
||||
"@vercel/python": "1.2.3",
|
||||
"@vercel/ruby": "1.2.4",
|
||||
"update-notifier": "4.1.0"
|
||||
@@ -100,7 +100,7 @@
|
||||
"@types/universal-analytics": "0.4.2",
|
||||
"@types/which": "1.3.2",
|
||||
"@types/write-json-file": "2.2.1",
|
||||
"@vercel/frameworks": "0.1.1",
|
||||
"@vercel/frameworks": "0.1.2-canary.0",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@zeit/fun": "0.11.2",
|
||||
"@zeit/source-map-support": "0.6.2",
|
||||
|
||||
@@ -18,7 +18,7 @@ const help = () => {
|
||||
|
||||
${chalk.dim('Commands:')}
|
||||
|
||||
ls [app] Show all aliases (or per app name)
|
||||
ls Show all aliases
|
||||
set <deployment> <alias> Create a new alias
|
||||
rm <alias> Remove an alias using its hostname
|
||||
|
||||
@@ -39,19 +39,19 @@ const help = () => {
|
||||
-N, --next Show next page of results
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Add a new alias to ${chalk.underline('my-api.now.sh')}
|
||||
${chalk.gray('–')} Add a new alias to ${chalk.underline('my-api.vercel.app')}
|
||||
|
||||
${chalk.cyan(
|
||||
`$ ${getPkgName()} alias set ${chalk.underline(
|
||||
'api-ownv3nc9f8.now.sh'
|
||||
)} ${chalk.underline('my-api.now.sh')}`
|
||||
'api-ownv3nc9f8.vercel.app'
|
||||
)} ${chalk.underline('my-api.vercel.app')}`
|
||||
)}
|
||||
|
||||
Custom domains work as alias targets
|
||||
|
||||
${chalk.cyan(
|
||||
`$ ${getPkgName()} alias set ${chalk.underline(
|
||||
'api-ownv3nc9f8.now.sh'
|
||||
'api-ownv3nc9f8.vercel.app'
|
||||
)} ${chalk.underline('my-api.com')}`
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import plural from 'pluralize';
|
||||
import table from 'text-table';
|
||||
import Now from '../../util';
|
||||
import Client from '../../util/client.ts';
|
||||
@@ -52,21 +51,17 @@ export default async function ls(ctx, opts, args, output) {
|
||||
const lsStamp = stamp();
|
||||
let cancelWait;
|
||||
|
||||
if (args.length > 1) {
|
||||
if (args.length > 0) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getCommandName('alias ls [alias]')}`
|
||||
`${getCommandName('alias ls')}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
cancelWait = output.spinner(
|
||||
args[0]
|
||||
? `Fetching alias details for "${args[0]}" under ${chalk.bold(
|
||||
contextName
|
||||
)}`
|
||||
: `Fetching aliases under ${chalk.bold(contextName)}`
|
||||
`Fetching aliases under ${chalk.bold(contextName)}`
|
||||
);
|
||||
|
||||
const { aliases, pagination } = await getAliases(
|
||||
@@ -76,32 +71,8 @@ export default async function ls(ctx, opts, args, output) {
|
||||
);
|
||||
if (cancelWait) cancelWait();
|
||||
|
||||
if (args[0]) {
|
||||
const alias = aliases.find(
|
||||
item => item.uid === args[0] || item.alias === args[0]
|
||||
);
|
||||
if (!alias) {
|
||||
output.error(`Could not match path alias for: ${args[0]}`);
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (opts['--json']) {
|
||||
console.log(JSON.stringify({ rules: alias.rules }, null, 2));
|
||||
} else {
|
||||
const rules = alias.rules || [];
|
||||
output.log(
|
||||
`${rules.length} path alias ${plural(
|
||||
'rule',
|
||||
rules.length
|
||||
)} found under ${chalk.bold(contextName)} ${lsStamp()}`
|
||||
);
|
||||
output.print(`${printPathAliasTable(rules)}\n`);
|
||||
}
|
||||
} else {
|
||||
output.log(`aliases found under ${chalk.bold(contextName)} ${lsStamp()}`);
|
||||
console.log(printAliasTable(aliases));
|
||||
}
|
||||
output.log(`aliases found under ${chalk.bold(contextName)} ${lsStamp()}`);
|
||||
console.log(printAliasTable(aliases));
|
||||
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(opts, ['_', '--next']);
|
||||
@@ -121,14 +92,10 @@ function printAliasTable(aliases) {
|
||||
[
|
||||
['source', 'url', 'age'].map(h => chalk.gray(h)),
|
||||
...aliases.map(a => [
|
||||
a.rules && a.rules.length
|
||||
? chalk.cyan(`[${plural('rule', a.rules.length, true)}]`)
|
||||
: // for legacy reasons, we might have situations
|
||||
// where the deployment was deleted and the alias
|
||||
// not collected appropriately, and we need to handle it
|
||||
a.deployment && a.deployment.url
|
||||
? a.deployment.url
|
||||
: chalk.gray('–'),
|
||||
// for legacy reasons, we might have situations
|
||||
// where the deployment was deleted and the alias
|
||||
// not collected appropriately, and we need to handle it
|
||||
a.deployment && a.deployment.url ? a.deployment.url : chalk.gray('–'),
|
||||
a.alias,
|
||||
ms(Date.now() - new Date(a.createdAt)),
|
||||
]),
|
||||
@@ -140,21 +107,3 @@ function printAliasTable(aliases) {
|
||||
}
|
||||
).replace(/^/gm, ' ')}\n\n`;
|
||||
}
|
||||
|
||||
function printPathAliasTable(rules) {
|
||||
const header = [['pathname', 'method', 'dest'].map(s => chalk.gray(s))];
|
||||
return `${table(
|
||||
header.concat(
|
||||
rules.map(rule => [
|
||||
rule.pathname ? rule.pathname : chalk.cyan('[fallthrough]'),
|
||||
rule.method ? rule.method : '*',
|
||||
rule.dest,
|
||||
])
|
||||
),
|
||||
{
|
||||
align: ['l', 'l', 'l', 'l'],
|
||||
hsep: ' '.repeat(6),
|
||||
stringLength: strlen,
|
||||
}
|
||||
).replace(/^(.*)/gm, ' $1')}\n`;
|
||||
}
|
||||
|
||||
@@ -388,7 +388,7 @@ function handleCreateAliasError<T>(
|
||||
}
|
||||
if (error instanceof ERRORS.InvalidAlias) {
|
||||
output.error(
|
||||
`Invalid alias. Please confirm that the alias you provided is a valid hostname. Note: For \`now.sh\`, only sub and sub-sub domains are supported.`
|
||||
`Invalid alias. Please confirm that the alias you provided is a valid hostname. Note: For \`vercel.app\`, only sub and sub-sub domains are supported.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -117,12 +117,6 @@ export type Deployment = {
|
||||
creator: { uid: string };
|
||||
};
|
||||
|
||||
type PathAliasRule = {
|
||||
pathname: string;
|
||||
method: Array<'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'>;
|
||||
dest: string;
|
||||
};
|
||||
|
||||
export type Alias = {
|
||||
uid: string;
|
||||
alias: string;
|
||||
@@ -137,13 +131,6 @@ export type Alias = {
|
||||
email: string;
|
||||
};
|
||||
deploymentId?: string;
|
||||
rules?: PathAliasRule[];
|
||||
};
|
||||
|
||||
export type PathRule = {
|
||||
dest: string;
|
||||
pathname?: string;
|
||||
method?: Array<string>;
|
||||
};
|
||||
|
||||
export type DNSRecord = {
|
||||
|
||||
@@ -172,6 +172,10 @@ export default async function processDeployment({
|
||||
if (event.type === 'created') {
|
||||
deployingSpinner();
|
||||
|
||||
if (bar && !bar.complete) {
|
||||
bar.tick(bar.total + 1);
|
||||
}
|
||||
|
||||
now._host = event.payload.url;
|
||||
|
||||
await linkFolderToProject(
|
||||
|
||||
@@ -28,9 +28,6 @@ module.exports = async function prepare(session) {
|
||||
'first.png': getImageFile(session, { size: 30 }),
|
||||
'second.png': getImageFile(session, { size: 20 }),
|
||||
},
|
||||
'single-dotfile': {
|
||||
'.testing': 'i am a dotfile',
|
||||
},
|
||||
'empty-directory': {},
|
||||
'config-scope-property-email': {
|
||||
'now.json': `{ "scope": "${session}@zeit.pub", "builds": [ { "src": "*.html", "use": "@now/static" } ] }`,
|
||||
@@ -207,23 +204,6 @@ module.exports = async function prepare(session) {
|
||||
},
|
||||
}),
|
||||
},
|
||||
'alias-rules': {
|
||||
'rules.json': JSON.stringify({
|
||||
rules: [
|
||||
// for example:
|
||||
// { pathname: '/', dest: '' },
|
||||
// { pathname: '/', dest: '', method: 'GET' }
|
||||
// Will be generated by the actual test
|
||||
],
|
||||
}),
|
||||
'invalid-rules.json': JSON.stringify({
|
||||
what: { what: 0 },
|
||||
}),
|
||||
'invalid-type-rules.json': JSON.stringify({
|
||||
rules: { what: 0 },
|
||||
}),
|
||||
'invalid-json-rules.json': '==ok',
|
||||
},
|
||||
'zero-config-next-js': {
|
||||
'pages/index.js':
|
||||
'export default () => <div><h1>Now CLI test</h1><p>Zero-config + Next.js</p></div>',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "9.0.2",
|
||||
"version": "9.0.4-canary.0",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"homepage": "https://vercel.com",
|
||||
@@ -37,7 +37,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.5.3",
|
||||
"@vercel/build-utils": "2.5.5-canary.0",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "3.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/next",
|
||||
"version": "2.6.26",
|
||||
"version": "2.6.32-canary.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
|
||||
@@ -29,7 +29,7 @@
|
||||
"@vercel/nft": "0.9.2",
|
||||
"async-sema": "3.0.1",
|
||||
"buffer-crc32": "0.2.13",
|
||||
"escape-string-regexp": "3.0.0",
|
||||
"escape-string-regexp": "2.0.0",
|
||||
"execa": "2.0.4",
|
||||
"find-up": "4.1.0",
|
||||
"fs-extra": "7.0.0",
|
||||
|
||||
@@ -59,7 +59,7 @@ export default async function createServerlessConfig(
|
||||
|
||||
const primaryConfigPath = path.join(entryPath, 'next.config.js');
|
||||
const secondaryConfigPath = path.join(workPath, 'next.config.js');
|
||||
const backupConfigName = `next.config.original.${Date.now()}.js`;
|
||||
const backupConfigName = `next.config.__vercel_builder_backup__.js`;
|
||||
|
||||
const hasPrimaryConfig = fs.existsSync(primaryConfigPath);
|
||||
const hasSecondaryConfig = fs.existsSync(secondaryConfigPath);
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
PrepareCacheOptions,
|
||||
Prerender,
|
||||
} from '@vercel/build-utils';
|
||||
import { Handler, Route } from '@vercel/routing-utils';
|
||||
import { Handler, Route, Source } from '@vercel/routing-utils';
|
||||
import {
|
||||
convertHeaders,
|
||||
convertRedirects,
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
import { nodeFileTrace, NodeFileTraceReasons } from '@vercel/nft';
|
||||
import { Sema } from 'async-sema';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
// escape-string-regexp version must match Next.js version
|
||||
import escapeStringRegexp from 'escape-string-regexp';
|
||||
import findUp from 'find-up';
|
||||
import { lstat, pathExists, readFile, remove, writeFile } from 'fs-extra';
|
||||
@@ -31,6 +32,7 @@ import buildUtils from './build-utils';
|
||||
import createServerlessConfig from './create-serverless-config';
|
||||
import nextLegacyVersions from './legacy-versions';
|
||||
import {
|
||||
addLocaleOrDefault,
|
||||
createLambdaFromPseudoLayers,
|
||||
createPseudoLayer,
|
||||
EnvConfig,
|
||||
@@ -39,6 +41,7 @@ import {
|
||||
getDynamicRoutes,
|
||||
getExportIntent,
|
||||
getExportStatus,
|
||||
getImagesManifest,
|
||||
getNextConfig,
|
||||
getPathsInside,
|
||||
getPrerenderManifest,
|
||||
@@ -46,6 +49,7 @@ import {
|
||||
getRoutesManifest,
|
||||
getSourceFilePathFromPage,
|
||||
isDynamicRoute,
|
||||
normalizeLocalePath,
|
||||
normalizePackageJson,
|
||||
normalizePage,
|
||||
PseudoLayer,
|
||||
@@ -219,7 +223,12 @@ export const build = async ({
|
||||
meta = {} as BuildParamsMeta,
|
||||
}: BuildParamsType): Promise<{
|
||||
routes: Route[];
|
||||
images?: { domains: string[]; sizes: number[] };
|
||||
output: Files;
|
||||
wildcard?: Array<{
|
||||
domain: string;
|
||||
value: string;
|
||||
}>;
|
||||
watch?: string[];
|
||||
childProcesses: ChildProcess[];
|
||||
}> => {
|
||||
@@ -302,6 +311,7 @@ export const build = async ({
|
||||
|
||||
return {
|
||||
output: {},
|
||||
images: undefined,
|
||||
routes: await getRoutes(
|
||||
entryPath,
|
||||
entryDirectory,
|
||||
@@ -367,7 +377,7 @@ export const build = async ({
|
||||
}
|
||||
|
||||
console.log('Installing dependencies...');
|
||||
await runNpmInstall(entryPath, ['--prefer-offline'], spawnOpts, meta);
|
||||
await runNpmInstall(entryPath, [], spawnOpts, meta);
|
||||
|
||||
// Refetch Next version now that dependencies are installed.
|
||||
// This will now resolve the actual installed Next version,
|
||||
@@ -420,6 +430,7 @@ export const build = async ({
|
||||
outputDirectory,
|
||||
nextVersion
|
||||
);
|
||||
const imagesManifest = await getImagesManifest(entryPath, outputDirectory);
|
||||
const prerenderManifest = await getPrerenderManifest(entryPath);
|
||||
const headers: Route[] = [];
|
||||
const rewrites: Route[] = [];
|
||||
@@ -428,6 +439,24 @@ export const build = async ({
|
||||
let dynamicRoutes: Route[] = [];
|
||||
// whether they have enabled pages/404.js as the custom 404 page
|
||||
let hasPages404 = false;
|
||||
let buildId = '';
|
||||
let escapedBuildId = '';
|
||||
|
||||
if (isLegacy || isSharedLambdas) {
|
||||
try {
|
||||
buildId = await readFile(
|
||||
path.join(entryPath, outputDirectory, 'BUILD_ID'),
|
||||
'utf8'
|
||||
);
|
||||
escapedBuildId = escapeStringRegexp(buildId);
|
||||
} catch (err) {
|
||||
throw new NowBuildError({
|
||||
code: 'NOW_NEXT_NO_BUILD_ID',
|
||||
message:
|
||||
'The BUILD_ID file was not found in the Output Directory. Did you forget to run "next build" in your Build Command?',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (routesManifest) {
|
||||
switch (routesManifest.version) {
|
||||
@@ -458,7 +487,7 @@ export const build = async ({
|
||||
continue;
|
||||
}
|
||||
|
||||
dataRoutes.push({
|
||||
const route = {
|
||||
src: (
|
||||
dataRoute.namedDataRouteRegex || dataRoute.dataRouteRegex
|
||||
).replace(/^\^/, `^${appMountPrefixNoTrailingSlash}`),
|
||||
@@ -477,7 +506,31 @@ export const build = async ({
|
||||
}`
|
||||
),
|
||||
check: true,
|
||||
});
|
||||
};
|
||||
|
||||
const { i18n } = routesManifest;
|
||||
|
||||
if (i18n) {
|
||||
route.src = route.src.replace(
|
||||
// we need to double escape the build ID here
|
||||
// to replace it properly
|
||||
`/${escapedBuildId}/`,
|
||||
`/${escapedBuildId}/(?${
|
||||
ssgDataRoute ? '<nextLocale>' : ':'
|
||||
}${i18n.locales
|
||||
.map(locale => escapeStringRegexp(locale))
|
||||
.join('|')})/`
|
||||
);
|
||||
|
||||
// make sure to route to the correct prerender output
|
||||
if (ssgDataRoute) {
|
||||
route.dest = route.dest.replace(
|
||||
`/${buildId}/`,
|
||||
`/${buildId}/$nextLocale/`
|
||||
);
|
||||
}
|
||||
}
|
||||
dataRoutes.push(route);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,6 +572,44 @@ export const build = async ({
|
||||
}
|
||||
}
|
||||
|
||||
if (imagesManifest) {
|
||||
switch (imagesManifest.version) {
|
||||
case 1: {
|
||||
if (!imagesManifest.images) {
|
||||
throw new NowBuildError({
|
||||
code: 'NEXT_IMAGES_MISSING',
|
||||
message:
|
||||
'image-manifest.json "images" is required. Contact support if this continues to happen.',
|
||||
});
|
||||
}
|
||||
const { images } = imagesManifest;
|
||||
if (!Array.isArray(images.domains)) {
|
||||
throw new NowBuildError({
|
||||
code: 'NEXT_IMAGES_DOMAINS',
|
||||
message:
|
||||
'image-manifest.json "images.domains" must be an array. Contact support if this continues to happen.',
|
||||
});
|
||||
}
|
||||
if (!Array.isArray(images.sizes)) {
|
||||
throw new NowBuildError({
|
||||
code: 'NEXT_IMAGES_DOMAINS',
|
||||
message:
|
||||
'image-manifest.json "images.sizes" must be an array. Contact support if this continues to happen.',
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new NowBuildError({
|
||||
code: 'NEXT_IMAGES_VERSION_UNKNOWN',
|
||||
message:
|
||||
'This version of `@vercel/next` does not support the version of Next.js you are trying to deploy.\n' +
|
||||
'Please upgrade your `@vercel/next` builder and try again. Contact support if this continues to happen.',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const userExport = await getExportStatus(entryPath);
|
||||
|
||||
if (userExport) {
|
||||
@@ -564,6 +655,13 @@ export const build = async ({
|
||||
|
||||
return {
|
||||
output,
|
||||
images:
|
||||
imagesManifest?.images?.loader === 'default'
|
||||
? {
|
||||
domains: imagesManifest.images.domains,
|
||||
sizes: imagesManifest.images.sizes,
|
||||
}
|
||||
: undefined,
|
||||
routes: [
|
||||
// User headers
|
||||
...headers,
|
||||
@@ -645,12 +743,7 @@ export const build = async ({
|
||||
|
||||
if (isLegacy) {
|
||||
debug('Running npm install --production...');
|
||||
await runNpmInstall(
|
||||
entryPath,
|
||||
['--prefer-offline', '--production'],
|
||||
spawnOpts,
|
||||
meta
|
||||
);
|
||||
await runNpmInstall(entryPath, ['--production'], spawnOpts, meta);
|
||||
}
|
||||
|
||||
if (process.env.NPM_AUTH_TOKEN) {
|
||||
@@ -667,27 +760,7 @@ export const build = async ({
|
||||
const staticPages: { [key: string]: FileFsRef } = {};
|
||||
const dynamicPages: string[] = [];
|
||||
let static404Page: string | undefined;
|
||||
let buildId = '';
|
||||
let page404Path = '';
|
||||
let escapedBuildId = '';
|
||||
|
||||
if (isLegacy || isSharedLambdas) {
|
||||
try {
|
||||
buildId = await readFile(
|
||||
path.join(entryPath, outputDirectory, 'BUILD_ID'),
|
||||
'utf8'
|
||||
);
|
||||
escapedBuildId = escapeStringRegexp(buildId);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
'BUILD_ID not found in ".next". The "package.json" "build" script did not run "next build"'
|
||||
);
|
||||
throw new NowBuildError({
|
||||
code: 'NOW_NEXT_NO_BUILD_ID',
|
||||
message: 'Missing BUILD_ID',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (isLegacy) {
|
||||
const filesAfterBuild = await glob('**', entryPath);
|
||||
@@ -796,7 +869,10 @@ export const build = async ({
|
||||
|
||||
Object.keys(staticPageFiles).forEach((page: string) => {
|
||||
const pathname = page.replace(/\.html$/, '');
|
||||
const routeName = normalizePage(pathname);
|
||||
const routeName = normalizeLocalePath(
|
||||
normalizePage(pathname),
|
||||
routesManifest?.i18n?.locales
|
||||
).pathname;
|
||||
|
||||
// Prerendered routes emit a `.html` file but should not be treated as a
|
||||
// static page.
|
||||
@@ -829,6 +905,17 @@ export const build = async ({
|
||||
? path.join(entryDirectory, '_errors/404')
|
||||
: undefined;
|
||||
|
||||
// TODO: locale specific 404s
|
||||
const { i18n } = routesManifest || {};
|
||||
|
||||
if (!static404Page && i18n) {
|
||||
static404Page = staticPages[
|
||||
path.join(entryDirectory, i18n.defaultLocale, '404')
|
||||
]
|
||||
? path.join(entryDirectory, i18n.defaultLocale, '404')
|
||||
: undefined;
|
||||
}
|
||||
|
||||
// > 1 because _error is a lambda but isn't used if a static 404 is available
|
||||
const pageKeys = Object.keys(pages);
|
||||
let hasLambdas = !static404Page || pageKeys.length > 1;
|
||||
@@ -1141,25 +1228,36 @@ export const build = async ({
|
||||
};
|
||||
}
|
||||
|
||||
const pageLambdaRoute: Route = {
|
||||
src: `^${escapeStringRegexp(outputName).replace(
|
||||
/\/index$/,
|
||||
'(/|/index|)'
|
||||
)}/?$`,
|
||||
dest: `${path.join('/', currentLambdaGroup.lambdaIdentifier)}`,
|
||||
headers: {
|
||||
'x-nextjs-page': outputName,
|
||||
},
|
||||
check: true,
|
||||
const addPageLambdaRoute = (escapedOutputPath: string) => {
|
||||
const pageLambdaRoute: Route = {
|
||||
src: `^${escapedOutputPath.replace(/\/index$/, '(/|/index|)')}/?$`,
|
||||
dest: `${path.join('/', currentLambdaGroup.lambdaIdentifier)}`,
|
||||
headers: {
|
||||
'x-nextjs-page': outputName,
|
||||
},
|
||||
check: true,
|
||||
};
|
||||
|
||||
// we only need to add the additional routes if shared lambdas
|
||||
// is enabled
|
||||
if (routeIsDynamic) {
|
||||
dynamicPageLambdaRoutes.push(pageLambdaRoute);
|
||||
dynamicPageLambdaRoutesMap[outputName] = pageLambdaRoute;
|
||||
} else {
|
||||
pageLambdaRoutes.push(pageLambdaRoute);
|
||||
}
|
||||
};
|
||||
|
||||
// we only need to add the additional routes if shared lambdas
|
||||
// is enabled
|
||||
if (routeIsDynamic) {
|
||||
dynamicPageLambdaRoutes.push(pageLambdaRoute);
|
||||
dynamicPageLambdaRoutesMap[outputName] = pageLambdaRoute;
|
||||
const { i18n } = routesManifest || {};
|
||||
|
||||
if (i18n) {
|
||||
addPageLambdaRoute(
|
||||
`[/]?(?:${i18n.locales
|
||||
.map(locale => escapeStringRegexp(locale))
|
||||
.join('|')})?${escapeStringRegexp(outputName)}`
|
||||
);
|
||||
} else {
|
||||
pageLambdaRoutes.push(pageLambdaRoute);
|
||||
addPageLambdaRoute(escapeStringRegexp(outputName));
|
||||
}
|
||||
|
||||
if (page === '_error.js' || (hasPages404 && page === '404.js')) {
|
||||
@@ -1288,7 +1386,32 @@ export const build = async ({
|
||||
new Set(prerenderManifest.omittedRoutes)
|
||||
).then(arr =>
|
||||
arr.map(route => {
|
||||
route.src = route.src.replace('^', `^${dynamicPrefix}`);
|
||||
const { i18n } = routesManifest || {};
|
||||
|
||||
if (i18n) {
|
||||
const { pathname } = url.parse(route.dest!);
|
||||
const isFallback = prerenderManifest.fallbackRoutes[pathname!];
|
||||
|
||||
route.src = route.src.replace(
|
||||
'^',
|
||||
`^${dynamicPrefix ? `${dynamicPrefix}[/]?` : '[/]?'}(?${
|
||||
isFallback ? '<nextLocale>' : ':'
|
||||
}${i18n.locales
|
||||
.map(locale => escapeStringRegexp(locale))
|
||||
.join('|')})?`
|
||||
);
|
||||
|
||||
if (isFallback) {
|
||||
// ensure destination has locale prefix to match prerender output
|
||||
// path so that the prerender object is used
|
||||
route.dest = route.dest!.replace(
|
||||
`${path.join('/', entryDirectory, '/')}`,
|
||||
`${path.join('/', entryDirectory, '$nextLocale', '/')}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
route.src = route.src.replace('^', `^${dynamicPrefix}`);
|
||||
}
|
||||
return route;
|
||||
})
|
||||
);
|
||||
@@ -1321,6 +1444,31 @@ export const build = async ({
|
||||
/\/\/ __LAUNCHER_PAGE_HANDLER__/g,
|
||||
`
|
||||
const url = require('url');
|
||||
|
||||
${
|
||||
routesManifest?.i18n
|
||||
? `
|
||||
function stripLocalePath(pathname) {
|
||||
// first item will be empty string from splitting at first char
|
||||
const pathnameParts = pathname.split('/')
|
||||
|
||||
;(${JSON.stringify(
|
||||
routesManifest.i18n.locales
|
||||
)}).some((locale) => {
|
||||
if (pathnameParts[1].toLowerCase() === locale.toLowerCase()) {
|
||||
pathnameParts.splice(1, 1)
|
||||
pathname = pathnameParts.join('/') || '/'
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return pathname
|
||||
}
|
||||
`
|
||||
: `function stripLocalePath(pathname) { return pathname }`
|
||||
}
|
||||
|
||||
page = function(req, res) {
|
||||
try {
|
||||
const pages = {
|
||||
@@ -1345,7 +1493,7 @@ export const build = async ({
|
||||
if (!toRender) {
|
||||
try {
|
||||
const { pathname } = url.parse(req.url)
|
||||
toRender = pathname.replace(/\\/$/, '')
|
||||
toRender = stripLocalePath(pathname).replace(/\\/$/, '')
|
||||
} catch (_) {
|
||||
// handle failing to parse url
|
||||
res.statusCode = 400
|
||||
@@ -1364,6 +1512,7 @@ export const build = async ({
|
||||
.replace(new RegExp('/_next/data/${escapedBuildId}/'), '/')
|
||||
.replace(/\\.json$/, '')
|
||||
|
||||
toRender = stripLocalePath(toRender)
|
||||
currentPage = pages[toRender]
|
||||
}
|
||||
|
||||
@@ -1474,7 +1623,15 @@ export const build = async ({
|
||||
let prerenderGroup = 1;
|
||||
const onPrerenderRoute = (
|
||||
routeKey: string,
|
||||
{ isBlocking, isFallback }: { isBlocking: boolean; isFallback: boolean }
|
||||
{
|
||||
isBlocking,
|
||||
isFallback,
|
||||
locale,
|
||||
}: {
|
||||
isBlocking: boolean;
|
||||
isFallback: boolean;
|
||||
locale?: string;
|
||||
}
|
||||
) => {
|
||||
if (isBlocking && isFallback) {
|
||||
throw new NowBuildError({
|
||||
@@ -1484,7 +1641,27 @@ export const build = async ({
|
||||
}
|
||||
|
||||
// Get the route file as it'd be mounted in the builder output
|
||||
const routeFileNoExt = routeKey === '/' ? '/index' : routeKey;
|
||||
let routeFileNoExt = routeKey === '/' ? '/index' : routeKey;
|
||||
const origRouteFileNoExt = routeFileNoExt;
|
||||
|
||||
const nonDynamicSsg =
|
||||
!isFallback &&
|
||||
!isBlocking &&
|
||||
!prerenderManifest.staticRoutes[routeKey].srcRoute;
|
||||
|
||||
// if there isn't a srcRoute then it's a non-dynamic SSG page and
|
||||
if (nonDynamicSsg || isFallback) {
|
||||
routeFileNoExt = addLocaleOrDefault(
|
||||
// root index files are located without folder/index.html
|
||||
routeFileNoExt,
|
||||
routesManifest,
|
||||
locale
|
||||
);
|
||||
}
|
||||
|
||||
const isNotFound = prerenderManifest.notFoundRoutes.includes(
|
||||
routeFileNoExt
|
||||
);
|
||||
|
||||
const htmlFsRef = isBlocking
|
||||
? // Blocking pages do not have an HTML fallback
|
||||
@@ -1494,7 +1671,11 @@ export const build = async ({
|
||||
pagesDir,
|
||||
isFallback
|
||||
? // Fallback pages have a special file.
|
||||
prerenderManifest.fallbackRoutes[routeKey].fallback
|
||||
addLocaleOrDefault(
|
||||
prerenderManifest.fallbackRoutes[routeKey].fallback,
|
||||
routesManifest,
|
||||
locale
|
||||
)
|
||||
: // Otherwise, the route itself should exist as a static HTML
|
||||
// file.
|
||||
`${routeFileNoExt}.html`
|
||||
@@ -1533,14 +1714,25 @@ export const build = async ({
|
||||
}
|
||||
|
||||
const outputPathPage = path.posix.join(entryDirectory, routeFileNoExt);
|
||||
const outputPathPageOrig = path.posix.join(
|
||||
entryDirectory,
|
||||
origRouteFileNoExt
|
||||
);
|
||||
let lambda: undefined | Lambda;
|
||||
const outputPathData = path.posix.join(entryDirectory, dataRoute);
|
||||
let outputPathData = path.posix.join(entryDirectory, dataRoute);
|
||||
|
||||
if (nonDynamicSsg || isFallback) {
|
||||
outputPathData = outputPathData.replace(
|
||||
new RegExp(`${escapeStringRegexp(origRouteFileNoExt)}.json$`),
|
||||
`${routeFileNoExt}.json`
|
||||
);
|
||||
}
|
||||
|
||||
if (isSharedLambdas) {
|
||||
const outputSrcPathPage = path.join(
|
||||
'/',
|
||||
srcRoute == null
|
||||
? outputPathPage
|
||||
? outputPathPageOrig
|
||||
: path.join(entryDirectory, srcRoute === '/' ? '/index' : srcRoute)
|
||||
);
|
||||
|
||||
@@ -1549,7 +1741,7 @@ export const build = async ({
|
||||
} else {
|
||||
const outputSrcPathPage =
|
||||
srcRoute == null
|
||||
? outputPathPage
|
||||
? outputPathPageOrig
|
||||
: path.posix.join(
|
||||
entryDirectory,
|
||||
srcRoute === '/' ? '/index' : srcRoute
|
||||
@@ -1558,7 +1750,7 @@ export const build = async ({
|
||||
lambda = lambdas[outputSrcPathPage];
|
||||
}
|
||||
|
||||
if (initialRevalidate === false) {
|
||||
if (!isNotFound && initialRevalidate === false) {
|
||||
if (htmlFsRef == null || jsonFsRef == null) {
|
||||
throw new NowBuildError({
|
||||
code: 'NEXT_HTMLFSREF_JSONFSREF',
|
||||
@@ -1573,7 +1765,7 @@ export const build = async ({
|
||||
}
|
||||
}
|
||||
|
||||
if (prerenders[outputPathPage] == null) {
|
||||
if (prerenders[outputPathPage] == null && !isNotFound) {
|
||||
if (lambda == null) {
|
||||
throw new NowBuildError({
|
||||
code: 'NEXT_MISSING_LAMBDA',
|
||||
@@ -1598,6 +1790,18 @@ export const build = async ({
|
||||
|
||||
++prerenderGroup;
|
||||
}
|
||||
|
||||
if ((nonDynamicSsg || isFallback) && routesManifest?.i18n && !locale) {
|
||||
// load each locale
|
||||
for (const locale of routesManifest.i18n.locales) {
|
||||
if (locale === routesManifest.i18n.defaultLocale) continue;
|
||||
onPrerenderRoute(routeKey, {
|
||||
isBlocking,
|
||||
isFallback,
|
||||
locale,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Object.keys(prerenderManifest.staticRoutes).forEach(route =>
|
||||
@@ -1726,6 +1930,8 @@ export const build = async ({
|
||||
}
|
||||
}
|
||||
|
||||
const { i18n } = routesManifest || {};
|
||||
|
||||
return {
|
||||
output: {
|
||||
...publicDirectoryFiles,
|
||||
@@ -1736,6 +1942,24 @@ export const build = async ({
|
||||
...staticFiles,
|
||||
...staticDirectoryFiles,
|
||||
},
|
||||
wildcard: i18n?.domains
|
||||
? i18n?.domains.map(item => {
|
||||
return {
|
||||
domain: item.domain,
|
||||
value:
|
||||
item.defaultLocale === i18n.defaultLocale
|
||||
? ''
|
||||
: `/${item.defaultLocale}`,
|
||||
};
|
||||
})
|
||||
: undefined,
|
||||
images:
|
||||
imagesManifest?.images?.loader === 'default'
|
||||
? {
|
||||
domains: imagesManifest.images.domains,
|
||||
sizes: imagesManifest.images.sizes,
|
||||
}
|
||||
: undefined,
|
||||
/*
|
||||
Desired routes order
|
||||
- Runtime headers
|
||||
@@ -1751,14 +1975,135 @@ export const build = async ({
|
||||
...headers,
|
||||
|
||||
// redirects
|
||||
...redirects,
|
||||
...redirects.map(_redir => {
|
||||
if (i18n) {
|
||||
const redir = _redir as Source;
|
||||
// detect the trailing slash redirect and make sure it's
|
||||
// kept above the wildcard mapping to prevent erroneous redirects
|
||||
// since non-continue routes come after continue the $wildcard
|
||||
// route will come before the redirect otherwise and if the
|
||||
// redirect is triggered it breaks locale mapping
|
||||
|
||||
const location =
|
||||
redir.headers && (redir.headers.location || redir.headers.Location);
|
||||
|
||||
if (
|
||||
redir.status === 308 &&
|
||||
(location === '/$1' || location === '/$1/')
|
||||
) {
|
||||
// we set continue true
|
||||
redir.continue = true;
|
||||
}
|
||||
}
|
||||
return _redir;
|
||||
}),
|
||||
|
||||
...(i18n
|
||||
? [
|
||||
// Handle auto-adding current default locale to path based on $wildcard
|
||||
{
|
||||
src: `^${path.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
'/'
|
||||
)}(?!(?:_next/.*|${i18n.locales
|
||||
.map(locale => escapeStringRegexp(locale))
|
||||
.join('|')})(?:/.*|$))(.*)$`,
|
||||
// TODO: this needs to contain or not contain a trailing slash
|
||||
// to prevent the trailing slash redirect from being triggered
|
||||
dest: '$wildcard/$1',
|
||||
continue: true,
|
||||
},
|
||||
|
||||
// Handle redirecting to locale specific domains
|
||||
...(i18n.domains
|
||||
? [
|
||||
{
|
||||
// TODO: enable redirecting between domains, will require
|
||||
// updating the src with the desired locales to redirect
|
||||
src: '/',
|
||||
locale: {
|
||||
redirect: i18n.domains.reduce(
|
||||
(prev: Record<string, string>, item) => {
|
||||
prev[item.defaultLocale] = `http${
|
||||
item.http ? '' : 's'
|
||||
}://${item.domain}/`;
|
||||
return prev;
|
||||
},
|
||||
{}
|
||||
),
|
||||
cookie: 'NEXT_LOCALE',
|
||||
},
|
||||
continue: true,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
// Handle redirecting to locale paths
|
||||
{
|
||||
// TODO: enable redirecting between paths, will require
|
||||
// updating the src with the desired locales to redirect.
|
||||
// if default locale is included in this src it won't be visitable
|
||||
// by users who prefer another language since the cookie isn't set
|
||||
// on redirect currently like in `next start`
|
||||
src: '/',
|
||||
locale: {
|
||||
redirect: i18n.locales.reduce(
|
||||
(prev: Record<string, string>, locale) => {
|
||||
prev[locale] =
|
||||
locale === i18n.defaultLocale ? `/` : `/${locale}`;
|
||||
return prev;
|
||||
},
|
||||
{}
|
||||
),
|
||||
cookie: 'NEXT_LOCALE',
|
||||
},
|
||||
continue: true,
|
||||
},
|
||||
|
||||
{
|
||||
src: `^${path.join('/', entryDirectory)}$`,
|
||||
dest: `/${i18n.defaultLocale}`,
|
||||
continue: true,
|
||||
},
|
||||
|
||||
// Auto-prefix non-locale path with default locale
|
||||
{
|
||||
src: `^${path.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
'/'
|
||||
)}(?!(?:_next/.*|${i18n.locales
|
||||
.map(locale => escapeStringRegexp(locale))
|
||||
.join('|')})(?:/.*|$))(.*)$`,
|
||||
dest: `/${i18n.defaultLocale}/$1`,
|
||||
continue: true,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
// Make sure to 404 for the /404 path itself
|
||||
{
|
||||
src: path.join('/', entryDirectory, '404'),
|
||||
status: 404,
|
||||
continue: true,
|
||||
},
|
||||
...(i18n
|
||||
? [
|
||||
{
|
||||
src: `${path.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
'/'
|
||||
)}(?:${i18n.locales
|
||||
.map(locale => escapeStringRegexp(locale))
|
||||
.join('|')})?[/]?404`,
|
||||
status: 404,
|
||||
continue: true,
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
src: path.join('/', entryDirectory, '404'),
|
||||
status: 404,
|
||||
continue: true,
|
||||
},
|
||||
]),
|
||||
|
||||
// Next.js page lambdas, `static/` folder, reserved assets, and `public/`
|
||||
// folder
|
||||
@@ -1788,6 +2133,40 @@ export const build = async ({
|
||||
dest: '$0',
|
||||
},
|
||||
|
||||
// remove default locale prefix to check public files
|
||||
...(i18n
|
||||
? [
|
||||
{
|
||||
src: `${path.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
i18n.defaultLocale,
|
||||
'/'
|
||||
)}(.*)`,
|
||||
dest: `${path.join('/', entryDirectory, '/')}$1`,
|
||||
check: true,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
// for non-shared lambdas remove locale prefix if present
|
||||
// to allow checking for lambda
|
||||
...(isSharedLambdas || !i18n
|
||||
? []
|
||||
: [
|
||||
{
|
||||
src: `${path.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
'/'
|
||||
)}(?:${i18n?.locales
|
||||
.map(locale => escapeStringRegexp(locale))
|
||||
.join('|')})/(.*)`,
|
||||
dest: '/$1',
|
||||
check: true,
|
||||
},
|
||||
]),
|
||||
|
||||
// routes that are called after each rewrite or after routes
|
||||
// if there no rewrites
|
||||
{ handle: 'rewrite' },
|
||||
@@ -1828,39 +2207,60 @@ export const build = async ({
|
||||
// Custom Next.js 404 page
|
||||
{ handle: 'error' } as Handler,
|
||||
|
||||
isSharedLambdas
|
||||
? {
|
||||
src: path.join('/', entryDirectory, '.*'),
|
||||
// if static 404 is not present but we have pages/404.js
|
||||
// it is a lambda due to _app getInitialProps
|
||||
dest: path.join(
|
||||
'/',
|
||||
(static404Page
|
||||
? static404Page
|
||||
: pageLambdaMap[page404Path]) as string
|
||||
),
|
||||
|
||||
status: 404,
|
||||
headers: {
|
||||
'x-nextjs-page': page404Path,
|
||||
...(i18n && static404Page
|
||||
? [
|
||||
{
|
||||
src: `${path.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
'/'
|
||||
)}(?<nextLocale>${i18n.locales
|
||||
.map(locale => escapeStringRegexp(locale))
|
||||
.join('|')})(/.*|$)`,
|
||||
dest: '/$nextLocale/404',
|
||||
status: 404,
|
||||
},
|
||||
}
|
||||
: {
|
||||
src: path.join('/', entryDirectory, '.*'),
|
||||
// if static 404 is not present but we have pages/404.js
|
||||
// it is a lambda due to _app getInitialProps
|
||||
dest: static404Page
|
||||
? path.join('/', static404Page)
|
||||
: path.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
hasPages404 &&
|
||||
lambdas[path.join('./', entryDirectory, '404')]
|
||||
? '404'
|
||||
: '_error'
|
||||
),
|
||||
status: 404,
|
||||
},
|
||||
{
|
||||
src: path.join('/', entryDirectory, '.*'),
|
||||
dest: `/${i18n.defaultLocale}/404`,
|
||||
status: 404,
|
||||
},
|
||||
]
|
||||
: [
|
||||
isSharedLambdas
|
||||
? {
|
||||
src: path.join('/', entryDirectory, '.*'),
|
||||
// if static 404 is not present but we have pages/404.js
|
||||
// it is a lambda due to _app getInitialProps
|
||||
dest: path.join(
|
||||
'/',
|
||||
(static404Page
|
||||
? static404Page
|
||||
: pageLambdaMap[page404Path]) as string
|
||||
),
|
||||
|
||||
status: 404,
|
||||
headers: {
|
||||
'x-nextjs-page': page404Path,
|
||||
},
|
||||
}
|
||||
: {
|
||||
src: path.join('/', entryDirectory, '.*'),
|
||||
// if static 404 is not present but we have pages/404.js
|
||||
// it is a lambda due to _app getInitialProps
|
||||
dest: static404Page
|
||||
? path.join('/', static404Page)
|
||||
: path.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
hasPages404 &&
|
||||
lambdas[path.join('./', entryDirectory, '404')]
|
||||
? '404'
|
||||
: '_error'
|
||||
),
|
||||
status: 404,
|
||||
},
|
||||
]),
|
||||
]),
|
||||
],
|
||||
watch: [],
|
||||
|
||||
@@ -326,6 +326,15 @@ export type RoutesManifest = {
|
||||
namedDataRouteRegex?: string;
|
||||
routeKeys?: { [named: string]: string };
|
||||
}>;
|
||||
i18n?: {
|
||||
defaultLocale: string;
|
||||
locales: string[];
|
||||
domains?: Array<{
|
||||
http?: boolean;
|
||||
domain: string;
|
||||
defaultLocale: string;
|
||||
}>;
|
||||
};
|
||||
};
|
||||
|
||||
export async function getRoutesManifest(
|
||||
@@ -502,6 +511,41 @@ export async function getDynamicRoutes(
|
||||
return routes;
|
||||
}
|
||||
|
||||
type LoaderKey = 'imgix' | 'cloudinary' | 'akamai' | 'default';
|
||||
|
||||
type ImagesManifest = {
|
||||
version: number;
|
||||
images: {
|
||||
loader: LoaderKey;
|
||||
sizes: number[];
|
||||
domains: string[];
|
||||
};
|
||||
};
|
||||
|
||||
export async function getImagesManifest(
|
||||
entryPath: string,
|
||||
outputDirectory: string
|
||||
): Promise<ImagesManifest | undefined> {
|
||||
const pathImagesManifest = path.join(
|
||||
entryPath,
|
||||
outputDirectory,
|
||||
'images-manifest.json'
|
||||
);
|
||||
|
||||
const hasImagesManifest = await fs
|
||||
.access(pathImagesManifest)
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
if (!hasImagesManifest) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const imagesManifest: ImagesManifest = require(pathImagesManifest);
|
||||
return imagesManifest;
|
||||
}
|
||||
|
||||
function syncEnvVars(base: EnvConfig, removeEnv: EnvConfig, addEnv: EnvConfig) {
|
||||
// Remove any env vars from `removeEnv`
|
||||
// that are not present in the `addEnv`
|
||||
@@ -711,6 +755,8 @@ export type NextPrerenderedRoutes = {
|
||||
};
|
||||
|
||||
omittedRoutes: string[];
|
||||
|
||||
notFoundRoutes: string[];
|
||||
};
|
||||
|
||||
export async function getExportIntent(
|
||||
@@ -801,6 +847,7 @@ export async function getPrerenderManifest(
|
||||
fallbackRoutes: {},
|
||||
bypassToken: null,
|
||||
omittedRoutes: [],
|
||||
notFoundRoutes: [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -846,6 +893,7 @@ export async function getPrerenderManifest(
|
||||
preview: {
|
||||
previewModeId: string;
|
||||
};
|
||||
notFoundRoutes?: string[];
|
||||
} = JSON.parse(await fs.readFile(pathPrerenderManifest, 'utf8'));
|
||||
|
||||
switch (manifest.version) {
|
||||
@@ -860,6 +908,7 @@ export async function getPrerenderManifest(
|
||||
bypassToken:
|
||||
(manifest.preview && manifest.preview.previewModeId) || null,
|
||||
omittedRoutes: [],
|
||||
notFoundRoutes: [],
|
||||
};
|
||||
|
||||
routes.forEach(route => {
|
||||
@@ -914,8 +963,13 @@ export async function getPrerenderManifest(
|
||||
fallbackRoutes: {},
|
||||
bypassToken: manifest.preview.previewModeId,
|
||||
omittedRoutes: [],
|
||||
notFoundRoutes: [],
|
||||
};
|
||||
|
||||
if (manifest.notFoundRoutes) {
|
||||
ret.notFoundRoutes.push(...manifest.notFoundRoutes);
|
||||
}
|
||||
|
||||
routes.forEach(route => {
|
||||
const {
|
||||
initialRevalidateSeconds,
|
||||
@@ -969,6 +1023,7 @@ export async function getPrerenderManifest(
|
||||
fallbackRoutes: {},
|
||||
bypassToken: null,
|
||||
omittedRoutes: [],
|
||||
notFoundRoutes: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1034,6 +1089,46 @@ function isDirectory(path: string) {
|
||||
return fs.existsSync(path) && fs.lstatSync(path).isDirectory();
|
||||
}
|
||||
|
||||
export function normalizeLocalePath(
|
||||
pathname: string,
|
||||
locales?: string[]
|
||||
): {
|
||||
detectedLocale?: string;
|
||||
pathname: string;
|
||||
} {
|
||||
let detectedLocale: string | undefined;
|
||||
// first item will be empty string from splitting at first char
|
||||
const pathnameParts = pathname.split('/');
|
||||
|
||||
(locales || []).some(locale => {
|
||||
if (pathnameParts[1].toLowerCase() === locale.toLowerCase()) {
|
||||
detectedLocale = locale;
|
||||
pathnameParts.splice(1, 1);
|
||||
pathname = pathnameParts.join('/') || '/';
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
return {
|
||||
pathname,
|
||||
detectedLocale,
|
||||
};
|
||||
}
|
||||
|
||||
export function addLocaleOrDefault(
|
||||
pathname: string,
|
||||
routesManifest?: RoutesManifest,
|
||||
locale?: string
|
||||
) {
|
||||
if (!routesManifest?.i18n) return pathname;
|
||||
if (!locale) locale = routesManifest.i18n.defaultLocale;
|
||||
|
||||
return locale
|
||||
? `/${locale}${pathname === '/index' ? '' : pathname}`
|
||||
: pathname;
|
||||
}
|
||||
|
||||
export {
|
||||
excludeFiles,
|
||||
validateEntrypoint,
|
||||
|
||||
23
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/next.config.js
vendored
Normal file
23
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/next.config.js
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
experimental: {
|
||||
i18n: {
|
||||
locales: ['nl-NL', 'nl-BE', 'nl', 'fr-BE', 'fr', 'en-US', 'en'],
|
||||
defaultLocale: 'en-US',
|
||||
// TODO: testing locale domains support, will require custom
|
||||
// testing set-up as test accounts are used currently
|
||||
domains: [
|
||||
{
|
||||
domain: 'example.be',
|
||||
defaultLocale: 'nl-BE',
|
||||
},
|
||||
{
|
||||
domain: 'example.fr',
|
||||
defaultLocale: 'fr',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
318
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/now.json
vendored
Normal file
318
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/now.json
vendored
Normal file
@@ -0,0 +1,318 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next",
|
||||
"config": {
|
||||
"sharedLambdas": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/",
|
||||
"headers": {
|
||||
"accept-language": "en;q=0.9"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//en/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"headers": {
|
||||
"accept-language": "nl;q=0.9"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//nl/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"headers": {
|
||||
"accept-language": "nl-NL;q=0.9"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//nl-NL/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"headers": {
|
||||
"accept-language": "fr;q=0.9"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//fr/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"headers": {
|
||||
"accept-language": "en-US;q=0.9"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 200,
|
||||
"mustContain": "index page"
|
||||
},
|
||||
{
|
||||
"path": "/en-US",
|
||||
"headers": {
|
||||
"accept-language": "nl;q=0.9"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 200,
|
||||
"mustContain": "index page"
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/",
|
||||
"status": 200,
|
||||
"mustContain": "index page"
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"status": 200,
|
||||
"mustContain": ">en-US<"
|
||||
},
|
||||
{
|
||||
"path": "/en",
|
||||
"status": 200,
|
||||
"mustContain": "index page"
|
||||
},
|
||||
{
|
||||
"path": "/en",
|
||||
"status": 200,
|
||||
"mustContain": ">en<"
|
||||
},
|
||||
{
|
||||
"path": "/fr",
|
||||
"status": 200,
|
||||
"mustContain": "index page"
|
||||
},
|
||||
{
|
||||
"path": "/fr",
|
||||
"status": 200,
|
||||
"mustContain": ">fr<"
|
||||
},
|
||||
{
|
||||
"path": "/nl",
|
||||
"status": 200,
|
||||
"mustContain": "index page"
|
||||
},
|
||||
{
|
||||
"path": "/nl",
|
||||
"status": 200,
|
||||
"mustContain": ">nl<"
|
||||
},
|
||||
{
|
||||
"path": "/nl-NL",
|
||||
"status": 200,
|
||||
"mustContain": "index page"
|
||||
},
|
||||
{
|
||||
"path": "/nl-NL",
|
||||
"status": 200,
|
||||
"mustContain": ">nl-NL<"
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/non-existent",
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"path": "/fr/non-existent",
|
||||
"status": 404,
|
||||
"mustContain": "lang=\"fr\""
|
||||
},
|
||||
{
|
||||
"path": "/en/non-existent",
|
||||
"status": 404,
|
||||
"mustContain": "lang=\"en\""
|
||||
},
|
||||
{
|
||||
"path": "/en-US/non-existent",
|
||||
"status": 404,
|
||||
"mustContain": "lang=\"en-US\""
|
||||
},
|
||||
{
|
||||
"path": "/nl/non-existent",
|
||||
"status": 404,
|
||||
"mustContain": "lang=\"nl\""
|
||||
},
|
||||
{
|
||||
"path": "/nl-NL/non-existent",
|
||||
"status": 404,
|
||||
"mustContain": "lang=\"nl-NL\""
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/hello.txt",
|
||||
"status": 200,
|
||||
"mustContain": "hello world!"
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/gsp",
|
||||
"status": 200,
|
||||
"mustContain": "gsp page"
|
||||
},
|
||||
{
|
||||
"path": "/gsp",
|
||||
"status": 200,
|
||||
"mustContain": ">en-US<"
|
||||
},
|
||||
{
|
||||
"path": "/en/gsp",
|
||||
"status": 200,
|
||||
"mustContain": "gsp page"
|
||||
},
|
||||
{
|
||||
"path": "/en/gsp",
|
||||
"status": 200,
|
||||
"mustContain": ">en<"
|
||||
},
|
||||
{
|
||||
"path": "/nl/gsp",
|
||||
"status": 200,
|
||||
"mustContain": "gsp page"
|
||||
},
|
||||
{
|
||||
"path": "/nl/gsp",
|
||||
"status": 200,
|
||||
"mustContain": ">nl<"
|
||||
},
|
||||
{
|
||||
"path": "/fr/gsp",
|
||||
"status": 200,
|
||||
"mustContain": "gsp page"
|
||||
},
|
||||
{
|
||||
"path": "/fr/gsp",
|
||||
"status": 200,
|
||||
"mustContain": ">fr<"
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/gssp",
|
||||
"status": 200,
|
||||
"mustContain": "gssp page"
|
||||
},
|
||||
{
|
||||
"path": "/gssp",
|
||||
"status": 200,
|
||||
"mustContain": ">en-US<"
|
||||
},
|
||||
{
|
||||
"path": "/en/gssp",
|
||||
"status": 200,
|
||||
"mustContain": "gssp page"
|
||||
},
|
||||
{
|
||||
"path": "/en/gssp",
|
||||
"status": 200,
|
||||
"mustContain": ">en<"
|
||||
},
|
||||
{
|
||||
"path": "/nl/gssp",
|
||||
"status": 200,
|
||||
"mustContain": "gssp page"
|
||||
},
|
||||
{
|
||||
"path": "/nl/gssp",
|
||||
"status": 200,
|
||||
"mustContain": ">nl<"
|
||||
},
|
||||
{
|
||||
"path": "/fr/gssp",
|
||||
"status": 200,
|
||||
"mustContain": "gssp page"
|
||||
},
|
||||
{
|
||||
"path": "/fr/gssp",
|
||||
"status": 200,
|
||||
"mustContain": ">fr<"
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": "gssp page"
|
||||
},
|
||||
{
|
||||
"path": "/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": ">en-US<"
|
||||
},
|
||||
{
|
||||
"path": "/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": "slug\":\"first\""
|
||||
},
|
||||
{
|
||||
"path": "/en/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": "gssp page"
|
||||
},
|
||||
{
|
||||
"path": "/en/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": ">en<"
|
||||
},
|
||||
{
|
||||
"path": "/en/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": "slug\":\"first\""
|
||||
},
|
||||
{
|
||||
"path": "/nl/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": "gssp page"
|
||||
},
|
||||
{
|
||||
"path": "/nl/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": ">nl<"
|
||||
},
|
||||
{
|
||||
"path": "/nl/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": "slug\":\"first\""
|
||||
},
|
||||
{
|
||||
"path": "/fr/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": "gssp page"
|
||||
},
|
||||
{
|
||||
"path": "/fr/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": ">fr<"
|
||||
},
|
||||
{
|
||||
"path": "/fr/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": "slug\":\"first\""
|
||||
}
|
||||
]
|
||||
}
|
||||
7
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/package.json
vendored
Normal file
7
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/package.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
31
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/another.js
vendored
Normal file
31
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/another.js
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="another">another page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps = ({ locale, locales }) => {
|
||||
return {
|
||||
props: {
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
21
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/auto-export/index.js
vendored
Normal file
21
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/auto-export/index.js
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="auto-export">auto-export page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
}
|
||||
44
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/gsp/fallback/[slug].js
vendored
Normal file
44
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/gsp/fallback/[slug].js
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
if (router.isFallback) return 'Loading...';
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="gsp">gsp page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps = ({ params, locale, locales }) => {
|
||||
return {
|
||||
props: {
|
||||
params,
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticPaths = () => {
|
||||
return {
|
||||
// the default locale will be used since one isn't defined here
|
||||
paths: ['first', 'second'].map(slug => ({
|
||||
params: { slug },
|
||||
})),
|
||||
fallback: true,
|
||||
};
|
||||
};
|
||||
32
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/gsp/index.js
vendored
Normal file
32
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/gsp/index.js
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="gsp">gsp page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: should non-dynamic GSP pages pre-render for each locale?
|
||||
export const getStaticProps = ({ locale, locales }) => {
|
||||
return {
|
||||
props: {
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
if (router.isFallback) return 'Loading...';
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="gsp">gsp page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps = ({ params, locale, locales }) => {
|
||||
return {
|
||||
props: {
|
||||
params,
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticPaths = () => {
|
||||
return {
|
||||
paths: [
|
||||
{ params: { slug: 'first' } },
|
||||
'/gsp/no-fallback/second',
|
||||
{ params: { slug: 'first' }, locale: 'en-US' },
|
||||
'/nl-NL/gsp/no-fallback/second',
|
||||
],
|
||||
fallback: false,
|
||||
};
|
||||
};
|
||||
32
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/gssp/[slug].js
vendored
Normal file
32
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/gssp/[slug].js
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="gssp">gssp page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps = ({ params, locale, locales }) => {
|
||||
return {
|
||||
props: {
|
||||
params,
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
31
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/gssp/index.js
vendored
Normal file
31
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/gssp/index.js
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="gssp">gssp page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps = ({ locale, locales }) => {
|
||||
return {
|
||||
props: {
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
46
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/index.js
vendored
Normal file
46
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/index.js
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="index">index page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/another">
|
||||
<a id="to-another">to /another</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gsp">
|
||||
<a id="to-gsp">to /gsp</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gsp/fallback/first">
|
||||
<a id="to-fallback-first">to /gsp/fallback/first</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gsp/fallback/hello">
|
||||
<a id="to-fallback-hello">to /gsp/fallback/hello</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gsp/no-fallback/first">
|
||||
<a id="to-no-fallback-first">to /gsp/no-fallback/first</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gssp">
|
||||
<a id="to-gssp">to /gssp</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gssp/first">
|
||||
<a id="to-gssp-slug">to /gssp/first</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
54
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/links.js
vendored
Normal file
54
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/links.js
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
const { nextLocale } = router.query;
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="links">links page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/another" locale={nextLocale}>
|
||||
<a id="to-another">to /another</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gsp" locale={nextLocale}>
|
||||
<a id="to-gsp">to /gsp</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gsp/fallback/first" locale={nextLocale}>
|
||||
<a id="to-fallback-first">to /gsp/fallback/first</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gsp/fallback/hello" locale={nextLocale}>
|
||||
<a id="to-fallback-hello">to /gsp/fallback/hello</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gsp/no-fallback/first" locale={nextLocale}>
|
||||
<a id="to-no-fallback-first">to /gsp/no-fallback/first</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gssp" locale={nextLocale}>
|
||||
<a id="to-gssp">to /gssp</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gssp/first" locale={nextLocale}>
|
||||
<a id="to-gssp-slug">to /gssp/first</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// make SSR page so we have query values immediately
|
||||
export const getServerSideProps = () => {
|
||||
return {
|
||||
props: {},
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
if (router.isFallback) return 'Loading...';
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="gsp">gsp page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps = ({ params, locale, locales }) => {
|
||||
if (locale === 'en' || locale === 'nl') {
|
||||
return {
|
||||
unstable_notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
params,
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticPaths = () => {
|
||||
return {
|
||||
// the default locale will be used since one isn't defined here
|
||||
paths: ['first', 'second'].map(slug => ({
|
||||
params: { slug },
|
||||
})),
|
||||
fallback: true,
|
||||
};
|
||||
};
|
||||
37
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/not-found/index.js
vendored
Normal file
37
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/not-found/index.js
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="gsp">gsp page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps = ({ locale, locales }) => {
|
||||
if (locale === 'en' || locale === 'nl') {
|
||||
return {
|
||||
unstable_notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
1
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/public/hello.txt
vendored
Normal file
1
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/public/hello.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
hello world!
|
||||
23
packages/now-next/test/fixtures/00-i18n-support/next.config.js
vendored
Normal file
23
packages/now-next/test/fixtures/00-i18n-support/next.config.js
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
experimental: {
|
||||
i18n: {
|
||||
locales: ['nl-NL', 'nl-BE', 'nl', 'fr-BE', 'fr', 'en-US', 'en'],
|
||||
defaultLocale: 'en-US',
|
||||
// TODO: testing locale domains support, will require custom
|
||||
// testing set-up as test accounts are used currently
|
||||
domains: [
|
||||
{
|
||||
domain: 'example.be',
|
||||
defaultLocale: 'nl-BE',
|
||||
},
|
||||
{
|
||||
domain: 'example.fr',
|
||||
defaultLocale: 'fr',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
390
packages/now-next/test/fixtures/00-i18n-support/now.json
vendored
Normal file
390
packages/now-next/test/fixtures/00-i18n-support/now.json
vendored
Normal file
@@ -0,0 +1,390 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next"
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/",
|
||||
"headers": {
|
||||
"accept-language": "en;q=0.9"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//en/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"headers": {
|
||||
"accept-language": "nl;q=0.9"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//nl/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"headers": {
|
||||
"accept-language": "nl-NL;q=0.9"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//nl-NL/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"headers": {
|
||||
"accept-language": "fr;q=0.9"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//fr/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"headers": {
|
||||
"accept-language": "en-US;q=0.9"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 200,
|
||||
"mustContain": "index page"
|
||||
},
|
||||
{
|
||||
"path": "/en-US",
|
||||
"headers": {
|
||||
"accept-language": "nl;q=0.9"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 200,
|
||||
"mustContain": "index page"
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/",
|
||||
"status": 200,
|
||||
"mustContain": "index page"
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"status": 200,
|
||||
"mustContain": ">en-US<"
|
||||
},
|
||||
{
|
||||
"path": "/en",
|
||||
"status": 200,
|
||||
"mustContain": "index page"
|
||||
},
|
||||
{
|
||||
"path": "/en",
|
||||
"status": 200,
|
||||
"mustContain": ">en<"
|
||||
},
|
||||
{
|
||||
"path": "/fr",
|
||||
"status": 200,
|
||||
"mustContain": "index page"
|
||||
},
|
||||
{
|
||||
"path": "/fr",
|
||||
"status": 200,
|
||||
"mustContain": ">fr<"
|
||||
},
|
||||
{
|
||||
"path": "/nl",
|
||||
"status": 200,
|
||||
"mustContain": "index page"
|
||||
},
|
||||
{
|
||||
"path": "/nl",
|
||||
"status": 200,
|
||||
"mustContain": ">nl<"
|
||||
},
|
||||
{
|
||||
"path": "/nl-NL",
|
||||
"status": 200,
|
||||
"mustContain": "index page"
|
||||
},
|
||||
{
|
||||
"path": "/nl-NL",
|
||||
"status": 200,
|
||||
"mustContain": ">nl-NL<"
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/non-existent",
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"path": "/fr/non-existent",
|
||||
"status": 404,
|
||||
"mustContain": "lang=\"fr\""
|
||||
},
|
||||
{
|
||||
"path": "/en/non-existent",
|
||||
"status": 404,
|
||||
"mustContain": "lang=\"en\""
|
||||
},
|
||||
{
|
||||
"path": "/en-US/non-existent",
|
||||
"status": 404,
|
||||
"mustContain": "lang=\"en-US\""
|
||||
},
|
||||
{
|
||||
"path": "/nl/non-existent",
|
||||
"status": 404,
|
||||
"mustContain": "lang=\"nl\""
|
||||
},
|
||||
{
|
||||
"path": "/nl-NL/non-existent",
|
||||
"status": 404,
|
||||
"mustContain": "lang=\"nl-NL\""
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/hello.txt",
|
||||
"status": 200,
|
||||
"mustContain": "hello world!"
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/gsp",
|
||||
"status": 200,
|
||||
"mustContain": "gsp page"
|
||||
},
|
||||
{
|
||||
"path": "/gsp",
|
||||
"status": 200,
|
||||
"mustContain": ">en-US<"
|
||||
},
|
||||
{
|
||||
"path": "/en/gsp",
|
||||
"status": 200,
|
||||
"mustContain": "gsp page"
|
||||
},
|
||||
{
|
||||
"path": "/en/gsp",
|
||||
"status": 200,
|
||||
"mustContain": ">en<"
|
||||
},
|
||||
{
|
||||
"path": "/nl/gsp",
|
||||
"status": 200,
|
||||
"mustContain": "gsp page"
|
||||
},
|
||||
{
|
||||
"path": "/nl/gsp",
|
||||
"status": 200,
|
||||
"mustContain": ">nl<"
|
||||
},
|
||||
{
|
||||
"path": "/fr/gsp",
|
||||
"status": 200,
|
||||
"mustContain": "gsp page"
|
||||
},
|
||||
{
|
||||
"path": "/fr/gsp",
|
||||
"status": 200,
|
||||
"mustContain": ">fr<"
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/gssp",
|
||||
"status": 200,
|
||||
"mustContain": "gssp page"
|
||||
},
|
||||
{
|
||||
"path": "/gssp",
|
||||
"status": 200,
|
||||
"mustContain": ">en-US<"
|
||||
},
|
||||
{
|
||||
"path": "/en/gssp",
|
||||
"status": 200,
|
||||
"mustContain": "gssp page"
|
||||
},
|
||||
{
|
||||
"path": "/en/gssp",
|
||||
"status": 200,
|
||||
"mustContain": ">en<"
|
||||
},
|
||||
{
|
||||
"path": "/nl/gssp",
|
||||
"status": 200,
|
||||
"mustContain": "gssp page"
|
||||
},
|
||||
{
|
||||
"path": "/nl/gssp",
|
||||
"status": 200,
|
||||
"mustContain": ">nl<"
|
||||
},
|
||||
{
|
||||
"path": "/fr/gssp",
|
||||
"status": 200,
|
||||
"mustContain": "gssp page"
|
||||
},
|
||||
{
|
||||
"path": "/fr/gssp",
|
||||
"status": 200,
|
||||
"mustContain": ">fr<"
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": "gssp page"
|
||||
},
|
||||
{
|
||||
"path": "/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": ">en-US<"
|
||||
},
|
||||
{
|
||||
"path": "/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": "slug\":\"first\""
|
||||
},
|
||||
{
|
||||
"path": "/en/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": "gssp page"
|
||||
},
|
||||
{
|
||||
"path": "/en/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": ">en<"
|
||||
},
|
||||
{
|
||||
"path": "/en/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": "slug\":\"first\""
|
||||
},
|
||||
{
|
||||
"path": "/nl/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": "gssp page"
|
||||
},
|
||||
{
|
||||
"path": "/nl/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": ">nl<"
|
||||
},
|
||||
{
|
||||
"path": "/nl/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": "slug\":\"first\""
|
||||
},
|
||||
{
|
||||
"path": "/fr/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": "gssp page"
|
||||
},
|
||||
{
|
||||
"path": "/fr/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": ">fr<"
|
||||
},
|
||||
{
|
||||
"path": "/fr/gssp/first",
|
||||
"status": 200,
|
||||
"mustContain": "slug\":\"first\""
|
||||
},
|
||||
|
||||
// TODO: update when directory listing is disabled
|
||||
// and these are proper 404s
|
||||
{
|
||||
"path": "/en/not-found",
|
||||
"status": 200,
|
||||
"mustContain": "Index of"
|
||||
},
|
||||
{
|
||||
"path": "/nl/not-found",
|
||||
"status": 200,
|
||||
"mustContain": "Index of"
|
||||
},
|
||||
{
|
||||
"path": "/en-US/not-found",
|
||||
"status": 200,
|
||||
"mustContain": "lang=\"en-US\""
|
||||
},
|
||||
{
|
||||
"path": "/nl-NL/not-found",
|
||||
"status": 200,
|
||||
"mustContain": "lang=\"nl-NL\""
|
||||
},
|
||||
{
|
||||
"path": "/fr/not-found",
|
||||
"status": 200,
|
||||
"mustContain": "lang=\"fr\""
|
||||
},
|
||||
|
||||
// this will always be a 200 unless fallback: blocking is used
|
||||
// since the static fallback page is served before the 404
|
||||
// page is rendered
|
||||
{
|
||||
"path": "/en/not-found/fallback/first",
|
||||
"status": 200,
|
||||
"mustContain": "lang=\"en\""
|
||||
},
|
||||
{
|
||||
"path": "/en/not-found/fallback/first",
|
||||
"status": 200,
|
||||
"mustNotContain": "gsp page"
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/en/not-found/fallback/first.json",
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"path": "/en/not-found/fallback/first",
|
||||
"status": 200,
|
||||
"mustContain": "lang=\"en\""
|
||||
},
|
||||
{
|
||||
"path": "/en/not-found/fallback/first",
|
||||
"status": 200,
|
||||
"mustNotContain": "gsp page"
|
||||
},
|
||||
{
|
||||
"path": "/fr/not-found/fallback/first",
|
||||
"status": 200,
|
||||
"mustContain": "lang=\"fr\""
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/fr/not-found/fallback/first.json",
|
||||
"status": 200
|
||||
},
|
||||
{
|
||||
"path": "/fr/not-found/fallback/first",
|
||||
"status": 200,
|
||||
"mustContain": "lang=\"fr\""
|
||||
},
|
||||
{
|
||||
"path": "/fr/not-found/fallback/first",
|
||||
"status": 200,
|
||||
"mustContain": "gsp page"
|
||||
}
|
||||
]
|
||||
}
|
||||
7
packages/now-next/test/fixtures/00-i18n-support/package.json
vendored
Normal file
7
packages/now-next/test/fixtures/00-i18n-support/package.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
31
packages/now-next/test/fixtures/00-i18n-support/pages/another.js
vendored
Normal file
31
packages/now-next/test/fixtures/00-i18n-support/pages/another.js
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="another">another page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps = ({ locale, locales }) => {
|
||||
return {
|
||||
props: {
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
21
packages/now-next/test/fixtures/00-i18n-support/pages/auto-export/index.js
vendored
Normal file
21
packages/now-next/test/fixtures/00-i18n-support/pages/auto-export/index.js
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="auto-export">auto-export page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
}
|
||||
44
packages/now-next/test/fixtures/00-i18n-support/pages/gsp/fallback/[slug].js
vendored
Normal file
44
packages/now-next/test/fixtures/00-i18n-support/pages/gsp/fallback/[slug].js
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
if (router.isFallback) return 'Loading...';
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="gsp">gsp page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps = ({ params, locale, locales }) => {
|
||||
return {
|
||||
props: {
|
||||
params,
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticPaths = () => {
|
||||
return {
|
||||
// the default locale will be used since one isn't defined here
|
||||
paths: ['first', 'second'].map(slug => ({
|
||||
params: { slug },
|
||||
})),
|
||||
fallback: true,
|
||||
};
|
||||
};
|
||||
32
packages/now-next/test/fixtures/00-i18n-support/pages/gsp/index.js
vendored
Normal file
32
packages/now-next/test/fixtures/00-i18n-support/pages/gsp/index.js
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="gsp">gsp page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: should non-dynamic GSP pages pre-render for each locale?
|
||||
export const getStaticProps = ({ locale, locales }) => {
|
||||
return {
|
||||
props: {
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
46
packages/now-next/test/fixtures/00-i18n-support/pages/gsp/no-fallback/[slug].js
vendored
Normal file
46
packages/now-next/test/fixtures/00-i18n-support/pages/gsp/no-fallback/[slug].js
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
if (router.isFallback) return 'Loading...';
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="gsp">gsp page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps = ({ params, locale, locales }) => {
|
||||
return {
|
||||
props: {
|
||||
params,
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticPaths = () => {
|
||||
return {
|
||||
paths: [
|
||||
{ params: { slug: 'first' } },
|
||||
'/gsp/no-fallback/second',
|
||||
{ params: { slug: 'first' }, locale: 'en-US' },
|
||||
'/nl-NL/gsp/no-fallback/second',
|
||||
],
|
||||
fallback: false,
|
||||
};
|
||||
};
|
||||
32
packages/now-next/test/fixtures/00-i18n-support/pages/gssp/[slug].js
vendored
Normal file
32
packages/now-next/test/fixtures/00-i18n-support/pages/gssp/[slug].js
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="gssp">gssp page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps = ({ params, locale, locales }) => {
|
||||
return {
|
||||
props: {
|
||||
params,
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
31
packages/now-next/test/fixtures/00-i18n-support/pages/gssp/index.js
vendored
Normal file
31
packages/now-next/test/fixtures/00-i18n-support/pages/gssp/index.js
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="gssp">gssp page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps = ({ locale, locales }) => {
|
||||
return {
|
||||
props: {
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
55
packages/now-next/test/fixtures/00-i18n-support/pages/index.js
vendored
Normal file
55
packages/now-next/test/fixtures/00-i18n-support/pages/index.js
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="index">index page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/another">
|
||||
<a id="to-another">to /another</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gsp">
|
||||
<a id="to-gsp">to /gsp</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gsp/fallback/first">
|
||||
<a id="to-fallback-first">to /gsp/fallback/first</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gsp/fallback/hello">
|
||||
<a id="to-fallback-hello">to /gsp/fallback/hello</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gsp/no-fallback/first">
|
||||
<a id="to-no-fallback-first">to /gsp/no-fallback/first</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gssp">
|
||||
<a id="to-gssp">to /gssp</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gssp/first">
|
||||
<a id="to-gssp-slug">to /gssp/first</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps = ({ locale, locales }) => {
|
||||
return {
|
||||
props: {
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
54
packages/now-next/test/fixtures/00-i18n-support/pages/links.js
vendored
Normal file
54
packages/now-next/test/fixtures/00-i18n-support/pages/links.js
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
const { nextLocale } = router.query;
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="links">links page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/another" locale={nextLocale}>
|
||||
<a id="to-another">to /another</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gsp" locale={nextLocale}>
|
||||
<a id="to-gsp">to /gsp</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gsp/fallback/first" locale={nextLocale}>
|
||||
<a id="to-fallback-first">to /gsp/fallback/first</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gsp/fallback/hello" locale={nextLocale}>
|
||||
<a id="to-fallback-hello">to /gsp/fallback/hello</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gsp/no-fallback/first" locale={nextLocale}>
|
||||
<a id="to-no-fallback-first">to /gsp/no-fallback/first</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gssp" locale={nextLocale}>
|
||||
<a id="to-gssp">to /gssp</a>
|
||||
</Link>
|
||||
<br />
|
||||
<Link href="/gssp/first" locale={nextLocale}>
|
||||
<a id="to-gssp-slug">to /gssp/first</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// make SSR page so we have query values immediately
|
||||
export const getServerSideProps = () => {
|
||||
return {
|
||||
props: {},
|
||||
};
|
||||
};
|
||||
50
packages/now-next/test/fixtures/00-i18n-support/pages/not-found/fallback/[slug].js
vendored
Normal file
50
packages/now-next/test/fixtures/00-i18n-support/pages/not-found/fallback/[slug].js
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
if (router.isFallback) return 'Loading...';
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="gsp">gsp page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps = ({ params, locale, locales }) => {
|
||||
if (locale === 'en' || locale === 'nl') {
|
||||
return {
|
||||
unstable_notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
params,
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticPaths = () => {
|
||||
return {
|
||||
// the default locale will be used since one isn't defined here
|
||||
paths: ['first', 'second'].map(slug => ({
|
||||
params: { slug },
|
||||
})),
|
||||
fallback: true,
|
||||
};
|
||||
};
|
||||
37
packages/now-next/test/fixtures/00-i18n-support/pages/not-found/index.js
vendored
Normal file
37
packages/now-next/test/fixtures/00-i18n-support/pages/not-found/index.js
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Page(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id="gsp">gsp page</p>
|
||||
<p id="props">{JSON.stringify(props)}</p>
|
||||
<p id="router-locale">{router.locale}</p>
|
||||
<p id="router-locales">{JSON.stringify(router.locales)}</p>
|
||||
<p id="router-query">{JSON.stringify(router.query)}</p>
|
||||
<p id="router-pathname">{router.pathname}</p>
|
||||
<p id="router-as-path">{router.asPath}</p>
|
||||
<Link href="/">
|
||||
<a id="to-index">to /</a>
|
||||
</Link>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps = ({ locale, locales }) => {
|
||||
if (locale === 'en' || locale === 'nl') {
|
||||
return {
|
||||
unstable_notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
locale,
|
||||
locales,
|
||||
},
|
||||
};
|
||||
};
|
||||
1
packages/now-next/test/fixtures/00-i18n-support/public/hello.txt
vendored
Normal file
1
packages/now-next/test/fixtures/00-i18n-support/public/hello.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
hello world!
|
||||
@@ -207,7 +207,9 @@ it(
|
||||
|
||||
expect(contents.some(name => name === 'next.config.js')).toBeTruthy();
|
||||
expect(
|
||||
contents.some(name => name.includes('next.config.original.'))
|
||||
contents.some(name =>
|
||||
name.includes('next.config.__vercel_builder_backup__')
|
||||
)
|
||||
).toBeTruthy();
|
||||
},
|
||||
FOUR_MINUTES
|
||||
@@ -278,7 +280,9 @@ it(
|
||||
|
||||
expect(contents.some(name => name === 'next.config.js')).toBeTruthy();
|
||||
expect(
|
||||
contents.some(name => name.includes('next.config.original.'))
|
||||
contents.some(name =>
|
||||
name.includes('next.config.__vercel_builder_backup__')
|
||||
)
|
||||
).toBeTruthy();
|
||||
},
|
||||
FOUR_MINUTES
|
||||
@@ -345,7 +349,9 @@ it(
|
||||
|
||||
expect(contents.some(name => name === 'next.config.js')).toBeTruthy();
|
||||
expect(
|
||||
contents.some(name => name.includes('next.config.original.'))
|
||||
contents.some(name =>
|
||||
name.includes('next.config.__vercel_builder_backup__')
|
||||
)
|
||||
).toBeTruthy();
|
||||
},
|
||||
FOUR_MINUTES
|
||||
@@ -380,7 +386,9 @@ it(
|
||||
|
||||
expect(contents.some(name => name === 'next.config.js')).toBeTruthy();
|
||||
expect(
|
||||
contents.some(name => name.includes('next.config.original.'))
|
||||
contents.some(name =>
|
||||
name.includes('next.config.__vercel_builder_backup__')
|
||||
)
|
||||
).toBeFalsy();
|
||||
},
|
||||
FOUR_MINUTES
|
||||
@@ -422,7 +430,9 @@ it(
|
||||
|
||||
expect(contents.some(name => name === 'next.config.js')).toBeTruthy();
|
||||
expect(
|
||||
contents.some(name => name.includes('next.config.original.'))
|
||||
contents.some(name =>
|
||||
name.includes('next.config.__vercel_builder_backup__')
|
||||
)
|
||||
).toBeFalsy();
|
||||
},
|
||||
FOUR_MINUTES
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/node",
|
||||
"version": "1.8.4-canary.0",
|
||||
"version": "1.8.4",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/routing-utils",
|
||||
"version": "1.8.4",
|
||||
"version": "1.9.1-canary.1",
|
||||
"description": "Vercel routing utilities",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -36,6 +36,40 @@ export const routesSchema = {
|
||||
},
|
||||
},
|
||||
},
|
||||
locale: {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
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,
|
||||
|
||||
@@ -17,6 +17,10 @@ export type Source = {
|
||||
continue?: boolean;
|
||||
check?: boolean;
|
||||
status?: number;
|
||||
locale?: {
|
||||
redirect: Record<string, string>;
|
||||
cookie: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type Handler = {
|
||||
|
||||
15
packages/now-routing-utils/test/index.spec.js
vendored
15
packages/now-routing-utils/test/index.spec.js
vendored
@@ -36,6 +36,21 @@ describe('normalizeRoutes', () => {
|
||||
|
||||
test('accepts valid routes', () => {
|
||||
const routes = [
|
||||
{
|
||||
src: '^(?:/(?<value>en|fr))?(?<path>/.*)$',
|
||||
locale: {
|
||||
value: '$value',
|
||||
path: '$path',
|
||||
default: 'en',
|
||||
cookie: 'NEXT_LOCALE',
|
||||
},
|
||||
},
|
||||
{
|
||||
src: '^/(?:en/?|fr/?)$',
|
||||
locale: {
|
||||
redirect: { en: '/en', fr: '/fr' },
|
||||
},
|
||||
},
|
||||
{ src: '^/about$' },
|
||||
{
|
||||
src: '^/blog$',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/redwood",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2-canary.0",
|
||||
"main": "./dist/index.js",
|
||||
"license": "MIT",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@netlify/zip-it-and-ship-it": "1.2.0",
|
||||
"@vercel/frameworks": "0.1.1"
|
||||
"@vercel/frameworks": "0.1.2-canary.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/aws-lambda": "8.10.19",
|
||||
|
||||
13
yarn.lock
13
yarn.lock
@@ -4793,21 +4793,16 @@ escape-html@1.0.3:
|
||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
|
||||
|
||||
escape-string-regexp@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-3.0.0.tgz#1dad9cc28aed682be0de197280f79911a5fccd61"
|
||||
integrity sha512-11dXIUC3umvzEViLP117d0KN6LJzZxh5+9F4E/7WLAAw7GrHk8NpUR+g9iJi/pe9C0py4F8rs0hreyRCwlAuZg==
|
||||
escape-string-regexp@2.0.0, escape-string-regexp@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
|
||||
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
|
||||
|
||||
escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
||||
|
||||
escape-string-regexp@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
|
||||
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
|
||||
|
||||
escodegen@^1.8.0, escodegen@^1.9.1:
|
||||
version "1.14.2"
|
||||
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.2.tgz#14ab71bf5026c2aa08173afba22c6f3173284a84"
|
||||
|
||||
Reference in New Issue
Block a user