mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-11 04:22:13 +00:00
Compare commits
17 Commits
vercel@28.
...
vercel@28.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b30f000d2a | ||
|
|
f78051ada9 | ||
|
|
10bc74904c | ||
|
|
c8f7a9a874 | ||
|
|
2fd3315221 | ||
|
|
54ef027cbe | ||
|
|
6620c7f600 | ||
|
|
38f40f1c15 | ||
|
|
63211b8b89 | ||
|
|
83ee5ea2b8 | ||
|
|
f063645646 | ||
|
|
4f8c5e344d | ||
|
|
70a53515bd | ||
|
|
4d4f0fa672 | ||
|
|
46c0fd153a | ||
|
|
1c8b4717e3 | ||
|
|
d52d26eaac |
@@ -14,7 +14,9 @@ In order to create the smallest possible lambdas Next.js has to be configured to
|
||||
npm install next --save
|
||||
```
|
||||
|
||||
2. Add the `now-build` script to your `package.json`
|
||||
2. Check [Node.js Version](https://vercel.link/node-version) in your Project Settings. Using an old or incompatible version of Node.js can cause the Build Step to fail with this error message.
|
||||
|
||||
3. Add the `now-build` script to your `package.json` [deprecated]
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -24,7 +26,7 @@ npm install next --save
|
||||
}
|
||||
```
|
||||
|
||||
3. Add `target: 'serverless'` to `next.config.js` [deprecated]
|
||||
4. Add `target: 'serverless'` to `next.config.js` [deprecated]
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
@@ -33,9 +35,9 @@ module.exports = {
|
||||
};
|
||||
```
|
||||
|
||||
4. Remove `distDir` from `next.config.js` as `@vercel/next` can't parse this file and expects your build output at `/.next`
|
||||
5. Remove `distDir` from `next.config.js` as `@vercel/next` can't parse this file and expects your build output at `/.next`
|
||||
|
||||
5. Optionally make sure the `"src"` in `"builds"` points to your application `package.json`
|
||||
6. Optionally make sure the `"src"` in `"builds"` points to your application `package.json`
|
||||
|
||||
```js
|
||||
{
|
||||
|
||||
@@ -13,10 +13,10 @@ function hydrate() {
|
||||
});
|
||||
}
|
||||
|
||||
if (window.requestIdleCallback) {
|
||||
window.requestIdleCallback(hydrate);
|
||||
if (typeof requestIdleCallback === "function") {
|
||||
requestIdleCallback(hydrate);
|
||||
} else {
|
||||
// Safari doesn't support requestIdleCallback
|
||||
// https://caniuse.com/requestidlecallback
|
||||
window.setTimeout(hydrate, 1);
|
||||
setTimeout(hydrate, 1);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
import type { EntryContext } from "@remix-run/node";
|
||||
import handleRequest from "@vercel/remix-entry-server";
|
||||
import { RemixServer } from "@remix-run/react";
|
||||
import { renderToString } from "react-dom/server";
|
||||
import type { EntryContext } from "@remix-run/server-runtime";
|
||||
|
||||
export default function handleRequest(
|
||||
export default function (
|
||||
request: Request,
|
||||
responseStatusCode: number,
|
||||
responseHeaders: Headers,
|
||||
remixContext: EntryContext
|
||||
) {
|
||||
const markup = renderToString(
|
||||
<RemixServer context={remixContext} url={request.url} />
|
||||
);
|
||||
|
||||
responseHeaders.set("Content-Type", "text/html");
|
||||
|
||||
return new Response("<!DOCTYPE html>" + markup, {
|
||||
headers: responseHeaders,
|
||||
status: responseStatusCode,
|
||||
});
|
||||
const remixServer = <RemixServer context={remixContext} url={request.url} />;
|
||||
return handleRequest(request, responseStatusCode, responseHeaders, remixServer)
|
||||
}
|
||||
|
||||
|
||||
11
examples/remix/app/routes/edge.tsx
Normal file
11
examples/remix/app/routes/edge.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
export const config = {
|
||||
runtime: 'edge'
|
||||
};
|
||||
|
||||
export default function Edge() {
|
||||
return (
|
||||
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
|
||||
<h1>Welcome to Remix@Edge</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -6,20 +6,20 @@
|
||||
"dev": "remix dev"
|
||||
},
|
||||
"dependencies": {
|
||||
"@remix-run/node": "^1.7.6",
|
||||
"@remix-run/react": "^1.7.6",
|
||||
"@remix-run/vercel": "^1.7.6",
|
||||
"@vercel/analytics": "^0.1.5",
|
||||
"@vercel/node": "^2.7.0",
|
||||
"@remix-run/node": "^1.13.0",
|
||||
"@remix-run/react": "^1.13.0",
|
||||
"@remix-run/serve": "^1.13.0",
|
||||
"@remix-run/server-runtime": "^1.13.0",
|
||||
"@vercel/analytics": "^0.1.10",
|
||||
"@vercel/remix-entry-server": "^0.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@remix-run/dev": "^1.7.6",
|
||||
"@remix-run/eslint-config": "^1.7.6",
|
||||
"@remix-run/serve": "^1.7.6",
|
||||
"@remix-run/dev": "^1.13.0",
|
||||
"@remix-run/eslint-config": "^1.13.0",
|
||||
"@types/react": "^18.0.25",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"eslint": "^8.28.0",
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
|
||||
3566
examples/remix/pnpm-lock.yaml
generated
3566
examples/remix/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,10 @@
|
||||
/** @type {import('@remix-run/dev').AppConfig} */
|
||||
/**
|
||||
* @type {import('@remix-run/dev').AppConfig}
|
||||
*/
|
||||
module.exports = {
|
||||
serverBuildTarget: "vercel",
|
||||
// When running locally in development mode, we use the built in remix
|
||||
// server. This does not understand the vercel lambda module format,
|
||||
// so we default back to the standard build output.
|
||||
server: process.env.NODE_ENV === "development" ? undefined : "./server.js",
|
||||
ignoredRouteFiles: ["**/.*"],
|
||||
ignoredRouteFiles: ['**/.*'],
|
||||
// appDirectory: "app",
|
||||
// assetsBuildDirectory: "public/build",
|
||||
// serverBuildPath: "api/index.js",
|
||||
// serverBuildPath: "build/index.js",
|
||||
// publicPath: "/build/",
|
||||
};
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
import { createRequestHandler } from "@remix-run/vercel";
|
||||
import * as build from "@remix-run/dev/server-build";
|
||||
|
||||
export default createRequestHandler({ build, mode: process.env.NODE_ENV });
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "28.16.1",
|
||||
"version": "28.16.4",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -44,13 +44,13 @@
|
||||
"@vercel/build-utils": "6.3.0",
|
||||
"@vercel/go": "2.3.7",
|
||||
"@vercel/hydrogen": "0.0.53",
|
||||
"@vercel/next": "3.4.7",
|
||||
"@vercel/next": "3.5.2",
|
||||
"@vercel/node": "2.9.6",
|
||||
"@vercel/python": "3.1.49",
|
||||
"@vercel/redwood": "1.1.5",
|
||||
"@vercel/remix": "1.3.3",
|
||||
"@vercel/ruby": "1.3.65",
|
||||
"@vercel/static-build": "1.3.9"
|
||||
"@vercel/remix": "1.3.5",
|
||||
"@vercel/ruby": "1.3.66",
|
||||
"@vercel/static-build": "1.3.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alex_neo/jest-expect-message": "1.0.5",
|
||||
@@ -96,7 +96,7 @@
|
||||
"@vercel/client": "12.4.0",
|
||||
"@vercel/error-utils": "1.0.8",
|
||||
"@vercel/frameworks": "1.3.1",
|
||||
"@vercel/fs-detectors": "3.7.14",
|
||||
"@vercel/fs-detectors": "3.8.0",
|
||||
"@vercel/fun": "1.0.4",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/routing-utils": "2.1.9",
|
||||
|
||||
@@ -22,7 +22,7 @@ const { format, inspect } = require('util');
|
||||
|
||||
/**
|
||||
* An simple output helper which accumulates error and debug log messages in
|
||||
* memory for potential persistance to disk while immediately outputting errors
|
||||
* memory for potential persistence to disk while immediately outputting errors
|
||||
* and debug messages, when the `--debug` flag is set, to `stderr`.
|
||||
*/
|
||||
class WorkerOutput {
|
||||
|
||||
@@ -76,6 +76,10 @@ async function isGlobal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (installPath.includes(['', 'fnm', 'node-versions', ''].join(sep))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const prefixPath =
|
||||
process.env.PREFIX ||
|
||||
process.env.npm_config_prefix ||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/fs-detectors",
|
||||
"version": "3.7.14",
|
||||
"version": "3.8.0",
|
||||
"description": "Vercel filesystem detectors",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -602,12 +602,11 @@ function validateFunctions({ functions = {} }: Options) {
|
||||
|
||||
if (
|
||||
func.memory !== undefined &&
|
||||
(func.memory < 128 || func.memory > 3008 || func.memory % 64 !== 0)
|
||||
(func.memory < 128 || func.memory > 3008)
|
||||
) {
|
||||
return {
|
||||
code: 'invalid_function_memory',
|
||||
message:
|
||||
'Functions must have a memory value between 128 and 3008 in steps of 64.',
|
||||
message: 'Functions must have a memory value between 128 and 3008',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -473,7 +473,7 @@ describe('Test `detectBuilders`', () => {
|
||||
});
|
||||
|
||||
it('invalid function memory', async () => {
|
||||
const functions = { 'pages/index.ts': { memory: 200 } };
|
||||
const functions = { 'pages/index.ts': { memory: 127 } };
|
||||
const files = ['pages/index.ts'];
|
||||
const { builders, errors } = await detectBuilders(files, null, {
|
||||
functions,
|
||||
@@ -484,6 +484,17 @@ describe('Test `detectBuilders`', () => {
|
||||
expect(errors![0].code).toBe('invalid_function_memory');
|
||||
});
|
||||
|
||||
it('should build with function memory not dividable by 64', async () => {
|
||||
const functions = { 'api/index.ts': { memory: 1000 } };
|
||||
const files = ['api/index.ts'];
|
||||
const { builders, errors } = await detectBuilders(files, null, {
|
||||
functions,
|
||||
});
|
||||
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(errors).toBeNull();
|
||||
});
|
||||
|
||||
it('missing runtime version', async () => {
|
||||
const functions = { 'pages/index.ts': { runtime: 'haha' } };
|
||||
const files = ['pages/index.ts'];
|
||||
@@ -1720,7 +1731,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
});
|
||||
|
||||
it('invalid function memory', async () => {
|
||||
const functions = { 'pages/index.ts': { memory: 200 } };
|
||||
const functions = { 'pages/index.ts': { memory: 127 } };
|
||||
const files = ['pages/index.ts'];
|
||||
const { builders, errors } = await detectBuilders(files, null, {
|
||||
functions,
|
||||
@@ -1732,6 +1743,18 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(errors![0].code).toBe('invalid_function_memory');
|
||||
});
|
||||
|
||||
it('should build with function memory not dividable by 64', async () => {
|
||||
const functions = { 'api/index.ts': { memory: 1000 } };
|
||||
const files = ['api/index.ts'];
|
||||
const { builders, errors } = await detectBuilders(files, null, {
|
||||
functions,
|
||||
featHandleMiss,
|
||||
});
|
||||
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(errors).toBeNull();
|
||||
});
|
||||
|
||||
it('missing runtime version', async () => {
|
||||
const functions = { 'pages/index.ts': { runtime: 'haha' } };
|
||||
const files = ['pages/index.ts'];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/next",
|
||||
"version": "3.4.7",
|
||||
"version": "3.5.2",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
|
||||
|
||||
@@ -88,6 +88,8 @@ import {
|
||||
PseudoLayerResult,
|
||||
updateRouteSrc,
|
||||
validateEntrypoint,
|
||||
getOperationType,
|
||||
isApiPage,
|
||||
} from './utils';
|
||||
|
||||
export const version = 2;
|
||||
@@ -1090,7 +1092,7 @@ export const build: BuildV2 = async ({
|
||||
handler: '___next_launcher.cjs',
|
||||
runtime: nodeVersion.runtime,
|
||||
...lambdaOptions,
|
||||
operationType: 'SSR',
|
||||
operationType: 'SSR', // always SSR because we're in legacy mode
|
||||
shouldAddHelpers: false,
|
||||
shouldAddSourcemapSupport: false,
|
||||
supportsMultiPayloads: !!process.env.NEXT_PRIVATE_MULTI_PAYLOAD,
|
||||
@@ -1126,10 +1128,6 @@ export const build: BuildV2 = async ({
|
||||
outputDirectory,
|
||||
appPathRoutesManifest,
|
||||
});
|
||||
const isApiPage = (page: string) =>
|
||||
page
|
||||
.replace(/\\/g, '/')
|
||||
.match(/(serverless|server)\/pages\/api(\/|\.js$)/);
|
||||
|
||||
const canUsePreviewMode = Object.keys(pages).some(page =>
|
||||
isApiPage(pages[page].fsPath)
|
||||
@@ -1598,6 +1596,10 @@ export const build: BuildV2 = async ({
|
||||
internalPages: [],
|
||||
});
|
||||
|
||||
for (const group of initialApiLambdaGroups) {
|
||||
group.isApiLambda = true;
|
||||
}
|
||||
|
||||
debug(
|
||||
JSON.stringify(
|
||||
{
|
||||
@@ -1819,6 +1821,10 @@ export const build: BuildV2 = async ({
|
||||
path.relative(baseDir, entryPath),
|
||||
'___next_launcher.cjs'
|
||||
),
|
||||
operationType: getOperationType({
|
||||
prerenderManifest,
|
||||
pageFileName,
|
||||
}),
|
||||
runtime: nodeVersion.runtime,
|
||||
nextVersion,
|
||||
...lambdaOptions,
|
||||
@@ -1839,6 +1845,7 @@ export const build: BuildV2 = async ({
|
||||
path.relative(baseDir, entryPath),
|
||||
'___next_launcher.cjs'
|
||||
),
|
||||
operationType: getOperationType({ pageFileName }), // can only be API or SSR
|
||||
runtime: nodeVersion.runtime,
|
||||
nextVersion,
|
||||
...lambdaOptions,
|
||||
@@ -2040,6 +2047,11 @@ export const build: BuildV2 = async ({
|
||||
pageLambdaMap[page] = group.lambdaIdentifier;
|
||||
}
|
||||
|
||||
const operationType = getOperationType({
|
||||
group,
|
||||
prerenderManifest,
|
||||
});
|
||||
|
||||
lambdas[group.lambdaIdentifier] =
|
||||
await createLambdaFromPseudoLayers({
|
||||
files: {
|
||||
@@ -2051,6 +2063,7 @@ export const build: BuildV2 = async ({
|
||||
path.relative(baseDir, entryPath),
|
||||
'___next_launcher.cjs'
|
||||
),
|
||||
operationType,
|
||||
runtime: nodeVersion.runtime,
|
||||
nextVersion,
|
||||
});
|
||||
@@ -2103,6 +2116,8 @@ export const build: BuildV2 = async ({
|
||||
...Object.entries(prerenderManifest.fallbackRoutes),
|
||||
...Object.entries(prerenderManifest.blockingFallbackRoutes),
|
||||
].forEach(([, { dataRouteRegex, dataRoute }]) => {
|
||||
if (!dataRoute || !dataRouteRegex) return;
|
||||
|
||||
dataRoutes.push({
|
||||
// Next.js provided data route regex
|
||||
src: dataRouteRegex.replace(
|
||||
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
getMiddlewareBundle,
|
||||
getFilesMapFromReasons,
|
||||
UnwrapPromise,
|
||||
getOperationType,
|
||||
} from './utils';
|
||||
import {
|
||||
nodeFileTrace,
|
||||
@@ -181,7 +182,10 @@ export async function serverBuild({
|
||||
}
|
||||
|
||||
const pageMatchesApi = (page: string) => {
|
||||
return page.startsWith('api/') || page === 'api.js';
|
||||
return (
|
||||
!appPathRoutesManifest?.[page] &&
|
||||
(page.startsWith('api/') || page === 'api.js')
|
||||
);
|
||||
};
|
||||
|
||||
const { i18n } = routesManifest;
|
||||
@@ -748,6 +752,10 @@ export async function serverBuild({
|
||||
internalPages,
|
||||
});
|
||||
|
||||
for (const group of apiLambdaGroups) {
|
||||
group.isApiLambda = true;
|
||||
}
|
||||
|
||||
debug(
|
||||
JSON.stringify(
|
||||
{
|
||||
@@ -856,6 +864,8 @@ export async function serverBuild({
|
||||
}
|
||||
}
|
||||
|
||||
const operationType = getOperationType({ group, prerenderManifest });
|
||||
|
||||
const lambda = await createLambdaFromPseudoLayers({
|
||||
files: {
|
||||
...launcherFiles,
|
||||
@@ -869,6 +879,7 @@ export async function serverBuild({
|
||||
),
|
||||
'___next_launcher.cjs'
|
||||
),
|
||||
operationType,
|
||||
memory: group.memory,
|
||||
runtime: nodeVersion.runtime,
|
||||
maxDuration: group.maxDuration,
|
||||
@@ -1128,8 +1139,19 @@ export async function serverBuild({
|
||||
// to match prerenders so we can route the same when the
|
||||
// __rsc__ header is present
|
||||
const edgeFunctions = middleware.edgeFunctions;
|
||||
// allow looking up original route from normalized route
|
||||
const inverseAppPathManifest: Record<string, string> = {};
|
||||
|
||||
for (const ogRoute of Object.keys(appPathRoutesManifest)) {
|
||||
inverseAppPathManifest[appPathRoutesManifest[ogRoute]] = ogRoute;
|
||||
}
|
||||
|
||||
for (let route of Object.values(appPathRoutesManifest)) {
|
||||
const ogRoute = inverseAppPathManifest[route];
|
||||
|
||||
if (ogRoute.endsWith('/route')) {
|
||||
continue;
|
||||
}
|
||||
route = path.posix.join('./', route === '/' ? '/index' : route);
|
||||
|
||||
if (lambdas[route]) {
|
||||
@@ -1142,6 +1164,10 @@ export async function serverBuild({
|
||||
}
|
||||
|
||||
const rscHeader = routesManifest.rsc?.header?.toLowerCase() || '__rsc__';
|
||||
const rscVaryHeader =
|
||||
routesManifest?.rsc?.varyHeader ||
|
||||
'RSC, Next-Router-State-Tree, Next-Router-Prefetch';
|
||||
|
||||
const completeDynamicRoutes: typeof dynamicRoutes = [];
|
||||
|
||||
if (appDir) {
|
||||
@@ -1414,7 +1440,9 @@ export async function serverBuild({
|
||||
},
|
||||
],
|
||||
dest: path.posix.join('/', entryDirectory, '/index.rsc'),
|
||||
headers: { vary: rscVaryHeader },
|
||||
continue: true,
|
||||
override: true,
|
||||
},
|
||||
{
|
||||
src: `^${path.posix.join(
|
||||
@@ -1429,7 +1457,9 @@ export async function serverBuild({
|
||||
},
|
||||
],
|
||||
dest: path.posix.join('/', entryDirectory, '/$1.rsc'),
|
||||
headers: { vary: rscVaryHeader },
|
||||
continue: true,
|
||||
override: true,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
@@ -849,16 +849,18 @@ export type NextPrerenderedRoutes = {
|
||||
staticRoutes: {
|
||||
[route: string]: {
|
||||
initialRevalidate: number | false;
|
||||
dataRoute: string;
|
||||
dataRoute: string | null;
|
||||
srcRoute: string | null;
|
||||
initialStatus?: number;
|
||||
initialHeaders?: Record<string, string>;
|
||||
};
|
||||
};
|
||||
|
||||
blockingFallbackRoutes: {
|
||||
[route: string]: {
|
||||
routeRegex: string;
|
||||
dataRoute: string;
|
||||
dataRouteRegex: string;
|
||||
dataRoute: string | null;
|
||||
dataRouteRegex: string | null;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -866,16 +868,16 @@ export type NextPrerenderedRoutes = {
|
||||
[route: string]: {
|
||||
fallback: string;
|
||||
routeRegex: string;
|
||||
dataRoute: string;
|
||||
dataRouteRegex: string;
|
||||
dataRoute: string | null;
|
||||
dataRouteRegex: string | null;
|
||||
};
|
||||
};
|
||||
|
||||
omittedRoutes: {
|
||||
[route: string]: {
|
||||
routeRegex: string;
|
||||
dataRoute: string;
|
||||
dataRouteRegex: string;
|
||||
dataRoute: string | null;
|
||||
dataRouteRegex: string | null;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1068,6 +1070,30 @@ export async function getPrerenderManifest(
|
||||
previewModeId: string;
|
||||
};
|
||||
notFoundRoutes?: string[];
|
||||
}
|
||||
| {
|
||||
version: 4;
|
||||
routes: {
|
||||
[route: string]: {
|
||||
initialRevalidateSeconds: number | false;
|
||||
srcRoute: string | null;
|
||||
dataRoute: string | null;
|
||||
initialStatus?: number;
|
||||
initialHeaders?: Record<string, string>;
|
||||
};
|
||||
};
|
||||
dynamicRoutes: {
|
||||
[route: string]: {
|
||||
routeRegex: string;
|
||||
fallback: string | false;
|
||||
dataRoute: string | null;
|
||||
dataRouteRegex: string | null;
|
||||
};
|
||||
};
|
||||
preview: {
|
||||
previewModeId: string;
|
||||
};
|
||||
notFoundRoutes?: string[];
|
||||
} = JSON.parse(await fs.readFile(pathPrerenderManifest, 'utf8'));
|
||||
|
||||
switch (manifest.version) {
|
||||
@@ -1122,7 +1148,8 @@ export async function getPrerenderManifest(
|
||||
return ret;
|
||||
}
|
||||
case 2:
|
||||
case 3: {
|
||||
case 3:
|
||||
case 4: {
|
||||
const routes = Object.keys(manifest.routes);
|
||||
const lazyRoutes = Object.keys(manifest.dynamicRoutes);
|
||||
|
||||
@@ -1143,6 +1170,15 @@ export async function getPrerenderManifest(
|
||||
routes.forEach(route => {
|
||||
const { initialRevalidateSeconds, dataRoute, srcRoute } =
|
||||
manifest.routes[route];
|
||||
|
||||
let initialStatus: undefined | number;
|
||||
let initialHeaders: undefined | Record<string, string>;
|
||||
|
||||
if (manifest.version === 4) {
|
||||
initialStatus = manifest.routes[route].initialStatus;
|
||||
initialHeaders = manifest.routes[route].initialHeaders;
|
||||
}
|
||||
|
||||
ret.staticRoutes[route] = {
|
||||
initialRevalidate:
|
||||
initialRevalidateSeconds === false
|
||||
@@ -1150,6 +1186,8 @@ export async function getPrerenderManifest(
|
||||
: Math.max(1, initialRevalidateSeconds),
|
||||
dataRoute,
|
||||
srcRoute,
|
||||
initialStatus,
|
||||
initialHeaders,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1313,6 +1351,7 @@ export type LambdaGroup = {
|
||||
maxDuration?: number;
|
||||
isStreaming?: boolean;
|
||||
isPrerenders?: boolean;
|
||||
isApiLambda: boolean;
|
||||
pseudoLayer: PseudoLayer;
|
||||
pseudoLayerBytes: number;
|
||||
pseudoLayerUncompressedBytes: number;
|
||||
@@ -1419,6 +1458,7 @@ export async function getPageLambdaGroups({
|
||||
pages: [page],
|
||||
...opts,
|
||||
isPrerenders: isPrerenderRoute,
|
||||
isApiLambda: !!isApiPage(page),
|
||||
pseudoLayerBytes: initialPseudoLayer.pseudoLayerBytes,
|
||||
pseudoLayerUncompressedBytes: initialPseudoLayerUncompressed,
|
||||
pseudoLayer: Object.assign({}, initialPseudoLayer.pseudoLayer),
|
||||
@@ -1650,7 +1690,7 @@ export const onPrerenderRouteInitial = (
|
||||
const { initialRevalidate, srcRoute, dataRoute } = pr;
|
||||
const route = srcRoute || routeKey;
|
||||
|
||||
const isAppPathRoute = appDir && dataRoute?.endsWith('.rsc');
|
||||
const isAppPathRoute = appDir && (!dataRoute || dataRoute?.endsWith('.rsc'));
|
||||
|
||||
const routeNoLocale = routesManifest?.i18n
|
||||
? normalizeLocalePath(routeKey, routesManifest.i18n.locales).pathname
|
||||
@@ -1808,7 +1848,9 @@ export const onPrerenderRoute =
|
||||
|
||||
let initialRevalidate: false | number;
|
||||
let srcRoute: string | null;
|
||||
let dataRoute: string;
|
||||
let dataRoute: string | null;
|
||||
let initialStatus: number | undefined;
|
||||
let initialHeaders: Record<string, string> | undefined;
|
||||
|
||||
if (isFallback || isBlocking) {
|
||||
const pr = isFallback
|
||||
@@ -1831,44 +1873,60 @@ export const onPrerenderRoute =
|
||||
dataRoute = prerenderManifest.omittedRoutes[routeKey].dataRoute;
|
||||
} else {
|
||||
const pr = prerenderManifest.staticRoutes[routeKey];
|
||||
({ initialRevalidate, srcRoute, dataRoute } = pr);
|
||||
({
|
||||
initialRevalidate,
|
||||
srcRoute,
|
||||
dataRoute,
|
||||
initialHeaders,
|
||||
initialStatus,
|
||||
} = pr);
|
||||
}
|
||||
|
||||
let isAppPathRoute = false;
|
||||
// TODO: leverage manifest to determine app paths more accurately
|
||||
if (appDir && srcRoute && dataRoute.endsWith('.rsc')) {
|
||||
if (appDir && srcRoute && (!dataRoute || dataRoute?.endsWith('.rsc'))) {
|
||||
isAppPathRoute = true;
|
||||
}
|
||||
|
||||
const isOmittedOrNotFound = isOmitted || isNotFound;
|
||||
const htmlFsRef =
|
||||
isBlocking || (isNotFound && !static404Page)
|
||||
? // Blocking pages do not have an HTML fallback
|
||||
null
|
||||
: new FileFsRef({
|
||||
fsPath: path.join(
|
||||
isAppPathRoute && !isOmittedOrNotFound && appDir
|
||||
? appDir
|
||||
: pagesDir,
|
||||
isFallback
|
||||
? // Fallback pages have a special file.
|
||||
addLocaleOrDefault(
|
||||
prerenderManifest.fallbackRoutes[routeKey].fallback,
|
||||
routesManifest,
|
||||
locale
|
||||
)
|
||||
: // Otherwise, the route itself should exist as a static HTML
|
||||
// file.
|
||||
`${
|
||||
isOmittedOrNotFound
|
||||
? addLocaleOrDefault('/404', routesManifest, locale)
|
||||
: routeFileNoExt
|
||||
}.html`
|
||||
),
|
||||
});
|
||||
let htmlFsRef: FileFsRef | null;
|
||||
|
||||
if (appDir && !dataRoute && isAppPathRoute && !(isBlocking || isFallback)) {
|
||||
const contentType = initialHeaders?.['content-type'];
|
||||
htmlFsRef = new FileFsRef({
|
||||
fsPath: path.join(appDir, `${routeFileNoExt}.body`),
|
||||
contentType: contentType || 'text/html;charset=utf-8',
|
||||
});
|
||||
} else {
|
||||
htmlFsRef =
|
||||
isBlocking || (isNotFound && !static404Page)
|
||||
? // Blocking pages do not have an HTML fallback
|
||||
null
|
||||
: new FileFsRef({
|
||||
fsPath: path.join(
|
||||
isAppPathRoute && !isOmittedOrNotFound && appDir
|
||||
? appDir
|
||||
: pagesDir,
|
||||
isFallback
|
||||
? // Fallback pages have a special file.
|
||||
addLocaleOrDefault(
|
||||
prerenderManifest.fallbackRoutes[routeKey].fallback,
|
||||
routesManifest,
|
||||
locale
|
||||
)
|
||||
: // Otherwise, the route itself should exist as a static HTML
|
||||
// file.
|
||||
`${
|
||||
isOmittedOrNotFound
|
||||
? addLocaleOrDefault('/404', routesManifest, locale)
|
||||
: routeFileNoExt
|
||||
}.html`
|
||||
),
|
||||
});
|
||||
}
|
||||
const jsonFsRef =
|
||||
// JSON data does not exist for fallback or blocking pages
|
||||
isFallback || isBlocking || (isNotFound && !static404Page)
|
||||
isFallback || isBlocking || (isNotFound && !static404Page) || !dataRoute
|
||||
? null
|
||||
: new FileFsRef({
|
||||
fsPath: path.join(
|
||||
@@ -1906,16 +1964,20 @@ export const onPrerenderRoute =
|
||||
);
|
||||
|
||||
let lambda: undefined | Lambda;
|
||||
let outputPathData = path.posix.join(entryDirectory, dataRoute);
|
||||
let outputPathData: null | string = null;
|
||||
|
||||
if (nonDynamicSsg || isFallback || isOmitted) {
|
||||
outputPathData = outputPathData.replace(
|
||||
new RegExp(`${escapeStringRegexp(origRouteFileNoExt)}.json$`),
|
||||
// ensure we escape "$" correctly while replacing as "$" is a special
|
||||
// character, we need to do double escaping as first is for the initial
|
||||
// replace on the routeFile and then the second on the outputPath
|
||||
`${routeFileNoExt.replace(/\$/g, '$$$$')}.json`
|
||||
);
|
||||
if (dataRoute) {
|
||||
outputPathData = path.posix.join(entryDirectory, dataRoute);
|
||||
|
||||
if (nonDynamicSsg || isFallback || isOmitted) {
|
||||
outputPathData = outputPathData.replace(
|
||||
new RegExp(`${escapeStringRegexp(origRouteFileNoExt)}.json$`),
|
||||
// ensure we escape "$" correctly while replacing as "$" is a special
|
||||
// character, we need to do double escaping as first is for the initial
|
||||
// replace on the routeFile and then the second on the outputPath
|
||||
`${routeFileNoExt.replace(/\$/g, '$$$$')}.json`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isSharedLambdas) {
|
||||
@@ -1952,7 +2014,7 @@ export const onPrerenderRoute =
|
||||
if (htmlFsRef == null || jsonFsRef == null) {
|
||||
throw new NowBuildError({
|
||||
code: 'NEXT_HTMLFSREF_JSONFSREF',
|
||||
message: 'invariant: htmlFsRef != null && jsonFsRef != null',
|
||||
message: `invariant: htmlFsRef != null && jsonFsRef != null ${routeFileNoExt}`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1964,7 +2026,10 @@ export const onPrerenderRoute =
|
||||
) {
|
||||
htmlFsRef.contentType = htmlContentType;
|
||||
prerenders[outputPathPage] = htmlFsRef;
|
||||
prerenders[outputPathData] = jsonFsRef;
|
||||
|
||||
if (outputPathData) {
|
||||
prerenders[outputPathData] = jsonFsRef;
|
||||
}
|
||||
}
|
||||
}
|
||||
const isNotFoundPreview =
|
||||
@@ -2026,11 +2091,10 @@ export const onPrerenderRoute =
|
||||
allowQuery = [];
|
||||
}
|
||||
}
|
||||
const rscVaryHeader =
|
||||
routesManifest?.rsc?.varyHeader ||
|
||||
'__rsc__, __next_router_state_tree__, __next_router_prefetch__';
|
||||
const rscContentTypeHeader =
|
||||
routesManifest?.rsc?.contentTypeHeader || 'application/octet-stream';
|
||||
|
||||
const rscEnabled = !!routesManifest?.rsc;
|
||||
const rscVaryHeader = routesManifest?.rsc?.varyHeader || 'RSC, Next-Router-State-Tree, Next-Router-Prefetch';
|
||||
const rscContentTypeHeader = routesManifest?.rsc?.contentTypeHeader || 'text/x-component';
|
||||
|
||||
prerenders[outputPathPage] = new Prerender({
|
||||
expiration: initialRevalidate,
|
||||
@@ -2039,27 +2103,8 @@ export const onPrerenderRoute =
|
||||
fallback: htmlFsRef,
|
||||
group: prerenderGroup,
|
||||
bypassToken: prerenderManifest.bypassToken,
|
||||
...(isNotFound
|
||||
? {
|
||||
initialStatus: 404,
|
||||
}
|
||||
: {}),
|
||||
|
||||
...(isAppPathRoute
|
||||
? {
|
||||
initialHeaders: {
|
||||
vary: rscVaryHeader,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
prerenders[outputPathData] = new Prerender({
|
||||
expiration: initialRevalidate,
|
||||
lambda,
|
||||
allowQuery,
|
||||
fallback: jsonFsRef,
|
||||
group: prerenderGroup,
|
||||
bypassToken: prerenderManifest.bypassToken,
|
||||
initialStatus,
|
||||
initialHeaders,
|
||||
|
||||
...(isNotFound
|
||||
? {
|
||||
@@ -2067,16 +2112,42 @@ export const onPrerenderRoute =
|
||||
}
|
||||
: {}),
|
||||
|
||||
...(isAppPathRoute
|
||||
...(rscEnabled
|
||||
? {
|
||||
initialHeaders: {
|
||||
'content-type': rscContentTypeHeader,
|
||||
...initialHeaders,
|
||||
vary: rscVaryHeader,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
|
||||
if (outputPathData) {
|
||||
prerenders[outputPathData] = new Prerender({
|
||||
expiration: initialRevalidate,
|
||||
lambda,
|
||||
allowQuery,
|
||||
fallback: jsonFsRef,
|
||||
group: prerenderGroup,
|
||||
bypassToken: prerenderManifest.bypassToken,
|
||||
|
||||
...(isNotFound
|
||||
? {
|
||||
initialStatus: 404,
|
||||
}
|
||||
: {}),
|
||||
|
||||
...(rscEnabled
|
||||
? {
|
||||
initialHeaders: {
|
||||
'content-type': rscContentTypeHeader,
|
||||
vary: rscVaryHeader,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
}
|
||||
|
||||
++prerenderGroup;
|
||||
|
||||
if (routesManifest?.i18n && isBlocking) {
|
||||
@@ -2090,29 +2161,30 @@ export const onPrerenderRoute =
|
||||
path.posix.join(entryDirectory, localeRouteFileNoExt),
|
||||
isServerMode
|
||||
);
|
||||
const localeOutputPathData = outputPathData.replace(
|
||||
new RegExp(`${escapeStringRegexp(origRouteFileNoExt)}.json$`),
|
||||
`${localeRouteFileNoExt}${
|
||||
localeRouteFileNoExt !== origRouteFileNoExt &&
|
||||
origRouteFileNoExt === '/index'
|
||||
? '/index'
|
||||
: ''
|
||||
}.json`
|
||||
);
|
||||
|
||||
const origPrerenderPage = prerenders[outputPathPage];
|
||||
const origPrerenderData = prerenders[outputPathData];
|
||||
|
||||
prerenders[localeOutputPathPage] = {
|
||||
...origPrerenderPage,
|
||||
group: prerenderGroup,
|
||||
} as Prerender;
|
||||
|
||||
prerenders[localeOutputPathData] = {
|
||||
...origPrerenderData,
|
||||
group: prerenderGroup,
|
||||
} as Prerender;
|
||||
if (outputPathData) {
|
||||
const localeOutputPathData = outputPathData.replace(
|
||||
new RegExp(`${escapeStringRegexp(origRouteFileNoExt)}.json$`),
|
||||
`${localeRouteFileNoExt}${
|
||||
localeRouteFileNoExt !== origRouteFileNoExt &&
|
||||
origRouteFileNoExt === '/index'
|
||||
? '/index'
|
||||
: ''
|
||||
}.json`
|
||||
);
|
||||
const origPrerenderData = prerenders[outputPathData];
|
||||
|
||||
prerenders[localeOutputPathData] = {
|
||||
...origPrerenderData,
|
||||
group: prerenderGroup,
|
||||
} as Prerender;
|
||||
}
|
||||
++prerenderGroup;
|
||||
}
|
||||
}
|
||||
@@ -2489,9 +2561,13 @@ export async function getMiddlewareBundle({
|
||||
// app/index/page -> index/index
|
||||
if (shortPath.startsWith('pages/')) {
|
||||
shortPath = shortPath.replace(/^pages\//, '');
|
||||
} else if (shortPath.startsWith('app/') && shortPath.endsWith('/page')) {
|
||||
} else if (
|
||||
shortPath.startsWith('app/') &&
|
||||
(shortPath.endsWith('/page') || shortPath.endsWith('/route'))
|
||||
) {
|
||||
shortPath =
|
||||
shortPath.replace(/^app\//, '').replace(/(^|\/)page$/, '') || 'index';
|
||||
shortPath.replace(/^app\//, '').replace(/(^|\/)(page|route)$/, '') ||
|
||||
'index';
|
||||
}
|
||||
|
||||
if (routesManifest?.basePath) {
|
||||
@@ -2677,3 +2753,49 @@ function transformSourceMap(
|
||||
|
||||
return { ...sourcemap, sources };
|
||||
}
|
||||
|
||||
interface LambdaGroupTypeInterface {
|
||||
isApiLambda: boolean;
|
||||
isPrerenders?: boolean;
|
||||
}
|
||||
|
||||
export function getOperationType({
|
||||
group,
|
||||
prerenderManifest,
|
||||
pageFileName,
|
||||
}: {
|
||||
group?: LambdaGroupTypeInterface;
|
||||
prerenderManifest?: NextPrerenderedRoutes;
|
||||
pageFileName?: string;
|
||||
}) {
|
||||
if (group?.isApiLambda || isApiPage(pageFileName)) {
|
||||
return 'API';
|
||||
}
|
||||
|
||||
if (group?.isPrerenders) {
|
||||
return 'ISR';
|
||||
}
|
||||
|
||||
if (pageFileName && prerenderManifest) {
|
||||
const { blockingFallbackRoutes = {}, fallbackRoutes = {} } =
|
||||
prerenderManifest;
|
||||
if (
|
||||
pageFileName in blockingFallbackRoutes ||
|
||||
pageFileName in fallbackRoutes
|
||||
) {
|
||||
return 'ISR';
|
||||
}
|
||||
}
|
||||
|
||||
return 'SSR';
|
||||
}
|
||||
|
||||
export function isApiPage(page: string | undefined) {
|
||||
if (!page) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return page
|
||||
.replace(/\\/g, '/')
|
||||
.match(/(serverless|server)\/pages\/api(\/|\.js$)/);
|
||||
}
|
||||
|
||||
21
packages/next/test/fixtures/00-app-dir-middleware/app/[variant]/[[...rest]]/page.js
vendored
Normal file
21
packages/next/test/fixtures/00-app-dir-middleware/app/[variant]/[[...rest]]/page.js
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
const paths = ['/', '/shop', '/product', '/who-we-are', '/about', '/contact'];
|
||||
|
||||
export default function Page({ params }) {
|
||||
return (
|
||||
<>
|
||||
<p>variant: {params.variant}</p>
|
||||
<p>slug: {params.rest?.join('/')}</p>
|
||||
<ul>
|
||||
{paths.map(path => {
|
||||
return (
|
||||
<li key={path}>
|
||||
<Link href={path}>to {path}</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
}
|
||||
10
packages/next/test/fixtures/00-app-dir-middleware/app/layout.js
vendored
Normal file
10
packages/next/test/fixtures/00-app-dir-middleware/app/layout.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
export default function Root({ children }) {
|
||||
return (
|
||||
<html className="this-is-the-document-html">
|
||||
<head>
|
||||
<title>{`hello world`}</title>
|
||||
</head>
|
||||
<body className="this-is-the-document-body">{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
12
packages/next/test/fixtures/00-app-dir-middleware/index.test.js
vendored
Normal file
12
packages/next/test/fixtures/00-app-dir-middleware/index.test.js
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/* eslint-env jest */
|
||||
const path = require('path');
|
||||
const { deployAndTest } = require('../../utils');
|
||||
|
||||
const ctx = {};
|
||||
|
||||
describe(`${__dirname.split(path.sep).pop()}`, () => {
|
||||
it('should deploy and pass probe checks', async () => {
|
||||
const info = await deployAndTest(__dirname);
|
||||
Object.assign(ctx, info);
|
||||
});
|
||||
});
|
||||
11
packages/next/test/fixtures/00-app-dir-middleware/middleware.js
vendored
Normal file
11
packages/next/test/fixtures/00-app-dir-middleware/middleware.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
export function middleware(request) {
|
||||
request.nextUrl.pathname = `/no-variant${request.nextUrl.pathname}`;
|
||||
return NextResponse.rewrite(request.nextUrl);
|
||||
}
|
||||
|
||||
// See "Matching Paths" below to learn more
|
||||
export const config = {
|
||||
matcher: ['/', '/shop', '/product', '/who-we-are', '/about', '/contact'],
|
||||
};
|
||||
14
packages/next/test/fixtures/00-app-dir-middleware/next.config.js
vendored
Normal file
14
packages/next/test/fixtures/00-app-dir-middleware/next.config.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
experimental: {
|
||||
appDir: true,
|
||||
runtime: 'nodejs',
|
||||
},
|
||||
rewrites: async () => {
|
||||
return [
|
||||
{
|
||||
source: '/rewritten-to-dashboard',
|
||||
destination: '/dashboard',
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
7
packages/next/test/fixtures/00-app-dir-middleware/package.json
vendored
Normal file
7
packages/next/test/fixtures/00-app-dir-middleware/package.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "experimental",
|
||||
"react-dom": "experimental"
|
||||
}
|
||||
}
|
||||
3
packages/next/test/fixtures/00-app-dir-middleware/pages/api/hello.js
vendored
Normal file
3
packages/next/test/fixtures/00-app-dir-middleware/pages/api/hello.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function handler(req, res) {
|
||||
return res.json({ hello: 'world' });
|
||||
}
|
||||
7
packages/next/test/fixtures/00-app-dir-middleware/pages/blog/[slug].js
vendored
Normal file
7
packages/next/test/fixtures/00-app-dir-middleware/pages/blog/[slug].js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function Page(props) {
|
||||
return (
|
||||
<>
|
||||
<p>hello from pages/blog/[slug]</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
1
packages/next/test/fixtures/00-app-dir-middleware/public/hello.txt
vendored
Normal file
1
packages/next/test/fixtures/00-app-dir-middleware/public/hello.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
hello world
|
||||
82
packages/next/test/fixtures/00-app-dir-middleware/vercel.json
vendored
Normal file
82
packages/next/test/fixtures/00-app-dir-middleware/vercel.json
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next"
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/",
|
||||
"status": 200,
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"mustContain": "no-variant",
|
||||
"mustNotContain": "index"
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"status": 200,
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"responseHeaders": {
|
||||
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
|
||||
},
|
||||
"headers": {
|
||||
"RSC": "1"
|
||||
},
|
||||
"mustContain": ":{",
|
||||
"mustNotContain": "<html"
|
||||
},
|
||||
{
|
||||
"path": "/shop",
|
||||
"status": 200,
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"mustContain": "no-variant",
|
||||
"mustNotContain": "shop.rsc"
|
||||
},
|
||||
{
|
||||
"path": "/shop",
|
||||
"status": 200,
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"responseHeaders": {
|
||||
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
|
||||
},
|
||||
"headers": {
|
||||
"RSC": "1"
|
||||
},
|
||||
"mustContain": ":{",
|
||||
"mustNotContain": "<html"
|
||||
},
|
||||
{
|
||||
"path": "/no-variant/shop",
|
||||
"status": 200,
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"mustContain": "no-variant",
|
||||
"mustNotContain": "shop.rsc"
|
||||
},
|
||||
{
|
||||
"path": "/no-variant/shop",
|
||||
"status": 200,
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"responseHeaders": {
|
||||
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
|
||||
},
|
||||
"headers": {
|
||||
"RSC": "1"
|
||||
},
|
||||
"mustContain": ":{",
|
||||
"mustNotContain": "<html"
|
||||
}
|
||||
]
|
||||
}
|
||||
7
packages/next/test/fixtures/00-app-dir/app/edge-route-handler/route.js
vendored
Normal file
7
packages/next/test/fixtures/00-app-dir/app/edge-route-handler/route.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
export const runtime = 'experimental-edge';
|
||||
|
||||
export const GET = req => {
|
||||
// use query to trigger dynamic usage
|
||||
console.log('query', Object.fromEntries(req.nextUrl.searchParams));
|
||||
return new Response('hello world');
|
||||
};
|
||||
@@ -32,6 +32,10 @@ if (parseInt(process.versions.node.split('.')[0], 10) >= 16) {
|
||||
expect(buildResult.output['dashboard/changelog']).toBeDefined();
|
||||
expect(buildResult.output['dashboard/deployments/[id]']).toBeDefined();
|
||||
|
||||
expect(buildResult.output['edge-route-handler']).toBeDefined();
|
||||
expect(buildResult.output['edge-route-handler'].type).toBe('EdgeFunction');
|
||||
expect(buildResult.output['edge-route-handler.rsc']).not.toBeDefined();
|
||||
|
||||
// prefixed static generation output with `/app` under dist server files
|
||||
expect(buildResult.output['dashboard'].type).toBe('Prerender');
|
||||
expect(buildResult.output['dashboard'].fallback.fsPath).toMatch(
|
||||
@@ -134,41 +138,70 @@ it('should build using server build', async () => {
|
||||
expect(output['index'].allowQuery).toBe(undefined);
|
||||
expect(output['index'].memory).toBe(512);
|
||||
expect(output['index'].maxDuration).toBe(5);
|
||||
expect(output['index'].operationType).toBe('SSR');
|
||||
|
||||
expect(output['another'].type).toBe('Lambda');
|
||||
expect(output['another'].memory).toBe(512);
|
||||
expect(output['another'].maxDuration).toBe(5);
|
||||
expect(output['another'].allowQuery).toBe(undefined);
|
||||
expect(output['another'].operationType).toBe('SSR');
|
||||
|
||||
expect(output['dynamic/[slug]'].type).toBe('Lambda');
|
||||
expect(output['dynamic/[slug]'].memory).toBe(undefined);
|
||||
expect(output['dynamic/[slug]'].maxDuration).toBe(5);
|
||||
expect(output['dynamic/[slug]'].operationType).toBe('SSR');
|
||||
|
||||
expect(output['fallback/[slug]'].type).toBe('Prerender');
|
||||
expect(output['fallback/[slug]'].allowQuery).toEqual(['slug']);
|
||||
expect(output['fallback/[slug]'].lambda.operationType).toBe('ISR');
|
||||
|
||||
expect(output['_next/data/testing-build-id/fallback/[slug].json'].type).toBe(
|
||||
'Prerender'
|
||||
);
|
||||
expect(
|
||||
output['_next/data/testing-build-id/fallback/[slug].json'].allowQuery
|
||||
).toEqual(['slug']);
|
||||
expect(
|
||||
output['_next/data/testing-build-id/fallback/[slug].json'].lambda
|
||||
.operationType
|
||||
).toBe('ISR');
|
||||
|
||||
expect(output['fallback/first'].type).toBe('Prerender');
|
||||
expect(output['fallback/first'].allowQuery).toEqual([]);
|
||||
expect(output['fallback/first'].lambda.operationType).toBe('ISR');
|
||||
|
||||
expect(output['_next/data/testing-build-id/fallback/first.json'].type).toBe(
|
||||
'Prerender'
|
||||
);
|
||||
expect(
|
||||
output['_next/data/testing-build-id/fallback/first.json'].allowQuery
|
||||
).toEqual([]);
|
||||
expect(
|
||||
output['_next/data/testing-build-id/fallback/first.json'].lambda
|
||||
.operationType
|
||||
).toBe('ISR');
|
||||
|
||||
expect(output['api'].type).toBe('Lambda');
|
||||
expect(output['api'].allowQuery).toBe(undefined);
|
||||
expect(output['api'].memory).toBe(128);
|
||||
expect(output['api'].maxDuration).toBe(5);
|
||||
expect(output['api'].operationType).toBe('API');
|
||||
|
||||
expect(output['api/another'].type).toBe('Lambda');
|
||||
expect(output['api/another'].allowQuery).toBe(undefined);
|
||||
expect(output['api/another'].operationType).toBe('API');
|
||||
|
||||
expect(output['api/blog/[slug]'].type).toBe('Lambda');
|
||||
expect(output['api/blog/[slug]'].allowQuery).toBe(undefined);
|
||||
expect(output['api/blog/[slug]'].operationType).toBe('API');
|
||||
|
||||
expect(output['static'].type).toBe('FileFsRef');
|
||||
expect(output['static'].allowQuery).toBe(undefined);
|
||||
expect(output['static'].operationType).toBe(undefined);
|
||||
|
||||
expect(output['ssg'].type).toBe('Prerender');
|
||||
expect(output['ssg'].allowQuery).toEqual([]);
|
||||
expect(output['ssg'].lambda.operationType).toBe('ISR');
|
||||
|
||||
expect(output['index'] === output['another']).toBe(true);
|
||||
expect(output['dynamic/[slug]'] !== output['fallback/[slug]'].lambda).toBe(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/remix",
|
||||
"version": "1.3.3",
|
||||
"version": "1.3.5",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
@@ -11,8 +11,9 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node build.js",
|
||||
"test-e2e": "pnpm test test/integration.test.ts",
|
||||
"test": "jest --env node --verbose --bail --runInBand"
|
||||
"test": "jest --env node --verbose --bail --runInBand",
|
||||
"test-unit": "pnpm test test/unit.*test.*",
|
||||
"test-e2e": "pnpm test test/integration.test.ts"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -4,7 +4,11 @@ import {
|
||||
Headers as NodeHeaders,
|
||||
Request as NodeRequest,
|
||||
writeReadableStreamToWritable,
|
||||
installGlobals,
|
||||
} from '@remix-run/node';
|
||||
|
||||
installGlobals();
|
||||
|
||||
import build from './index.js';
|
||||
|
||||
const handleRequest = createRemixRequestHandler(build, process.env.NODE_ENV);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Project } from 'ts-morph';
|
||||
import { promises as fs } from 'fs';
|
||||
import { basename, dirname, extname, join, relative, sep } from 'path';
|
||||
import { pathToRegexp, Key } from 'path-to-regexp';
|
||||
import {
|
||||
debug,
|
||||
download,
|
||||
@@ -30,7 +29,12 @@ import type {
|
||||
BuildResultV2Typical,
|
||||
} from '@vercel/build-utils';
|
||||
import type { ConfigRoute } from '@remix-run/dev/dist/config/routes';
|
||||
import { findConfig } from './utils';
|
||||
import {
|
||||
findConfig,
|
||||
getPathFromRoute,
|
||||
getRegExpFromPath,
|
||||
isLayoutRoute,
|
||||
} from './utils';
|
||||
|
||||
const _require: typeof require = eval('require');
|
||||
|
||||
@@ -173,12 +177,13 @@ module.exports = config;`;
|
||||
}
|
||||
}
|
||||
|
||||
const { serverBuildPath, routes: remixRoutes } = remixConfig;
|
||||
const { serverBuildPath } = remixConfig;
|
||||
const remixRoutes = Object.values(remixConfig.routes);
|
||||
|
||||
// Figure out which pages should be edge functions
|
||||
const edgePages = new Set<ConfigRoute>();
|
||||
const project = new Project();
|
||||
for (const route of Object.values(remixRoutes)) {
|
||||
for (const route of remixRoutes) {
|
||||
const routePath = join(remixConfig.appDirectory, route.file);
|
||||
const staticConfig = getConfig(project, routePath);
|
||||
const isEdge =
|
||||
@@ -228,27 +233,11 @@ module.exports = config;`;
|
||||
},
|
||||
];
|
||||
|
||||
for (const route of Object.values(remixRoutes)) {
|
||||
for (const route of remixRoutes) {
|
||||
// Layout routes don't get a function / route added
|
||||
const isLayoutRoute = Object.values(remixRoutes).some(
|
||||
r => r.parentId === route.id
|
||||
);
|
||||
if (isLayoutRoute) continue;
|
||||
|
||||
// Build up the full request path
|
||||
let currentRoute: ConfigRoute | undefined = route;
|
||||
const pathParts: string[] = [];
|
||||
do {
|
||||
if (currentRoute.index) pathParts.push('index');
|
||||
if (currentRoute.path) pathParts.push(currentRoute.path);
|
||||
if (currentRoute.parentId) {
|
||||
currentRoute = remixRoutes[currentRoute.parentId];
|
||||
} else {
|
||||
currentRoute = undefined;
|
||||
}
|
||||
} while (currentRoute);
|
||||
const path = join(...pathParts.reverse());
|
||||
if (isLayoutRoute(route.id, remixRoutes)) continue;
|
||||
|
||||
const path = getPathFromRoute(route, remixConfig.routes);
|
||||
const isEdge = edgePages.has(route);
|
||||
const fn =
|
||||
isEdge && edgeFunction
|
||||
@@ -264,13 +253,8 @@ module.exports = config;`;
|
||||
output[path] = fn;
|
||||
|
||||
// If this is a dynamic route then add a Vercel route
|
||||
const keys: Key[] = [];
|
||||
// Replace "/*" at the end to handle "splat routes"
|
||||
const splatPath = '/:params+';
|
||||
const rePath =
|
||||
path === '*' ? splatPath : `/${path.replace(/\/\*$/, splatPath)}`;
|
||||
const re = pathToRegexp(rePath, keys);
|
||||
if (keys.length > 0) {
|
||||
const re = getRegExpFromPath(path);
|
||||
if (re) {
|
||||
routes.push({
|
||||
src: re.source,
|
||||
dest: path,
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { existsSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { existsSync } from 'fs';
|
||||
import { pathToRegexp, Key } from 'path-to-regexp';
|
||||
import type {
|
||||
ConfigRoute,
|
||||
RouteManifest,
|
||||
} from '@remix-run/dev/dist/config/routes';
|
||||
|
||||
const configExts = ['.js', '.cjs', '.mjs'];
|
||||
|
||||
@@ -12,3 +17,39 @@ export function findConfig(dir: string, basename: string): string | undefined {
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function isLayoutRoute(
|
||||
routeId: string,
|
||||
routes: Pick<ConfigRoute, 'id' | 'parentId'>[]
|
||||
): boolean {
|
||||
return routes.some(r => r.parentId === routeId);
|
||||
}
|
||||
|
||||
export function getPathFromRoute(
|
||||
route: ConfigRoute,
|
||||
routes: RouteManifest
|
||||
): string {
|
||||
let currentRoute: ConfigRoute | undefined = route;
|
||||
const pathParts: string[] = [];
|
||||
do {
|
||||
if (currentRoute.index) pathParts.push('index');
|
||||
if (currentRoute.path) pathParts.push(currentRoute.path);
|
||||
if (currentRoute.parentId) {
|
||||
currentRoute = routes[currentRoute.parentId];
|
||||
} else {
|
||||
currentRoute = undefined;
|
||||
}
|
||||
} while (currentRoute);
|
||||
const path = pathParts.reverse().join('/');
|
||||
return path;
|
||||
}
|
||||
|
||||
export function getRegExpFromPath(path: string): RegExp | false {
|
||||
const keys: Key[] = [];
|
||||
// Replace "/*" at the end to handle "splat routes"
|
||||
const splatPath = '/:params+';
|
||||
const rePath =
|
||||
path === '*' ? splatPath : `/${path.replace(/\/\*$/, splatPath)}`;
|
||||
const re = pathToRegexp(rePath, keys);
|
||||
return keys.length > 0 ? re : false;
|
||||
}
|
||||
|
||||
13
packages/remix/test/fixtures/01-remix-basics/app/routes/instanceof.tsx
vendored
Normal file
13
packages/remix/test/fixtures/01-remix-basics/app/routes/instanceof.tsx
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { json } from '@remix-run/server-runtime';
|
||||
import { useLoaderData } from '@remix-run/react';
|
||||
import type { LoaderArgs } from '@remix-run/server-runtime';
|
||||
|
||||
export const loader = ({ request }: LoaderArgs) => {
|
||||
const instanceOfRequest = request instanceof Request;
|
||||
return json({ instanceOfRequest });
|
||||
};
|
||||
|
||||
export default function InstanceOf() {
|
||||
const data = useLoaderData<typeof loader>();
|
||||
return <div>{`InstanceOfRequest: ${data.instanceOfRequest}`}</div>;
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
{ "path": "/nested", "mustContain": "Nested index page" },
|
||||
{ "path": "/nested/another", "mustContain": "Nested another page" },
|
||||
{ "path": "/nested/index", "mustContain": "Not Found" },
|
||||
{ "path": "/asdf", "mustContain": "Not Found" }
|
||||
{ "path": "/asdf", "mustContain": "Not Found" },
|
||||
{ "path": "/instanceof", "mustContain": "InstanceOfRequest: true" }
|
||||
]
|
||||
}
|
||||
|
||||
17
packages/remix/test/unit.find-config.test.ts
vendored
Normal file
17
packages/remix/test/unit.find-config.test.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import { join } from 'path';
|
||||
import { findConfig } from '../src/utils';
|
||||
|
||||
const fixture = (name: string) => join(__dirname, 'fixtures', name);
|
||||
|
||||
describe('findConfig()', () => {
|
||||
it.each([
|
||||
{ name: '01-remix-basics', config: 'remix.config.js' },
|
||||
{ name: '02-remix-basics-mjs', config: 'remix.config.mjs' },
|
||||
{ name: '03-with-pnpm', config: 'remix.config.js' },
|
||||
{ name: '04-with-npm9-linked', config: 'remix.config.js' },
|
||||
])('should find `$config` from "$name"', ({ name, config }) => {
|
||||
const dir = fixture(name);
|
||||
const resolved = findConfig(dir, 'remix.config');
|
||||
expect(resolved).toEqual(join(dir, config));
|
||||
});
|
||||
});
|
||||
73
packages/remix/test/unit.get-path-from-route.test.ts
vendored
Normal file
73
packages/remix/test/unit.get-path-from-route.test.ts
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
import { getPathFromRoute } from '../src/utils';
|
||||
import type { RouteManifest } from '@remix-run/dev/dist/config/routes';
|
||||
|
||||
describe('getPathFromRoute()', () => {
|
||||
const routes: RouteManifest = {
|
||||
root: { path: '', id: 'root', file: 'root.tsx' },
|
||||
'routes/$foo.$bar.$baz': {
|
||||
path: ':foo/:bar/:baz',
|
||||
id: 'routes/$foo.$bar.$baz',
|
||||
parentId: 'root',
|
||||
file: 'routes/$foo.$bar.$baz.tsx',
|
||||
},
|
||||
'routes/api.hello': {
|
||||
path: 'api/hello',
|
||||
id: 'routes/api.hello',
|
||||
parentId: 'root',
|
||||
file: 'routes/api.hello.tsx',
|
||||
},
|
||||
'routes/projects': {
|
||||
path: 'projects',
|
||||
id: 'routes/projects',
|
||||
parentId: 'root',
|
||||
file: 'routes/projects.tsx',
|
||||
},
|
||||
'routes/projects/index': {
|
||||
path: undefined,
|
||||
index: true,
|
||||
id: 'routes/projects/indexx',
|
||||
parentId: 'routes/projects',
|
||||
file: 'routes/projects/indexx.tsx',
|
||||
},
|
||||
'routes/projects/$': {
|
||||
path: '*',
|
||||
id: 'routes/projects/$',
|
||||
parentId: 'routes/projects',
|
||||
file: 'routes/projects/$.tsx',
|
||||
},
|
||||
'routes/index': {
|
||||
path: undefined,
|
||||
index: true,
|
||||
id: 'routes/index',
|
||||
parentId: 'root',
|
||||
file: 'routes/index.tsx',
|
||||
},
|
||||
'routes/node': {
|
||||
path: 'node',
|
||||
id: 'routes/node',
|
||||
parentId: 'root',
|
||||
file: 'routes/node.tsx',
|
||||
},
|
||||
'routes/$': {
|
||||
path: '*',
|
||||
id: 'routes/$',
|
||||
parentId: 'root',
|
||||
file: 'routes/$.tsx',
|
||||
},
|
||||
};
|
||||
|
||||
it.each([
|
||||
{ id: 'root', expected: '' },
|
||||
{ id: 'routes/index', expected: 'index' },
|
||||
{ id: 'routes/api.hello', expected: 'api/hello' },
|
||||
{ id: 'routes/projects', expected: 'projects' },
|
||||
{ id: 'routes/projects/index', expected: 'projects/index' },
|
||||
{ id: 'routes/projects/$', expected: 'projects/*' },
|
||||
{ id: 'routes/$foo.$bar.$baz', expected: ':foo/:bar/:baz' },
|
||||
{ id: 'routes/node', expected: 'node' },
|
||||
{ id: 'routes/$', expected: '*' },
|
||||
])('should return `$expected` for "$id" route', ({ id, expected }) => {
|
||||
const route = routes[id];
|
||||
expect(getPathFromRoute(route, routes)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
124
packages/remix/test/unit.get-regexp-from-path.test.ts
vendored
Normal file
124
packages/remix/test/unit.get-regexp-from-path.test.ts
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
import { getRegExpFromPath } from '../src/utils';
|
||||
|
||||
describe('getRegExpFromPath()', () => {
|
||||
describe('paths without parameters', () => {
|
||||
it.each([{ path: 'index' }, { path: 'api/hello' }, { path: 'projects' }])(
|
||||
'should return `false` for "$path"',
|
||||
({ path }) => {
|
||||
expect(getRegExpFromPath(path)).toEqual(false);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe.each([
|
||||
{
|
||||
path: '*',
|
||||
urls: [
|
||||
{
|
||||
url: '/',
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: '/foo',
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: '/projects/foo',
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: '/projects/another',
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: '/to/infinity/and/beyond',
|
||||
expected: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'projects/*',
|
||||
urls: [
|
||||
{
|
||||
url: '/',
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: '/foo',
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: '/projects/foo',
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: '/projects/another',
|
||||
expected: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: ':foo',
|
||||
urls: [
|
||||
{
|
||||
url: '/',
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: '/foo',
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: '/projects/foo',
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: '/projects/another',
|
||||
expected: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'blog/:id/edit',
|
||||
urls: [
|
||||
{
|
||||
url: '/',
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: '/foo',
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: '/blog/123/edit',
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: '/blog/456/edit',
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: '/blog/123/456/edit',
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: '/blog/123/another',
|
||||
expected: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
])('with path "$path"', ({ path, urls }) => {
|
||||
const re = getRegExpFromPath(path) as RegExp;
|
||||
|
||||
it('should return RegExp', () => {
|
||||
expect(re).toBeInstanceOf(RegExp);
|
||||
});
|
||||
|
||||
it.each(urls)(
|
||||
'should match URL "$url" - $expected',
|
||||
({ url, expected }) => {
|
||||
expect(re.test(url)).toEqual(expected);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
21
packages/remix/test/unit.is-layout-route.test.ts
vendored
Normal file
21
packages/remix/test/unit.is-layout-route.test.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import { isLayoutRoute } from '../src/utils';
|
||||
|
||||
describe('isLayoutRoute()', () => {
|
||||
const routes = [
|
||||
{ id: 'root' },
|
||||
{ id: 'routes/auth', parentId: 'root' },
|
||||
{ id: 'routes/login', parentId: 'routes/auth' },
|
||||
{ id: 'routes/logout', parentId: 'routes/auth' },
|
||||
{ id: 'routes/index', parentId: 'root' },
|
||||
];
|
||||
|
||||
it.each([
|
||||
{ id: 'root', expected: true },
|
||||
{ id: 'routes/auth', expected: true },
|
||||
{ id: 'routes/index', expected: false },
|
||||
{ id: 'routes/login', expected: false },
|
||||
{ id: 'routes/logout', expected: false },
|
||||
])('should return `$expected` for "$id" route', ({ id, expected }) => {
|
||||
expect(isLayoutRoute(id, routes)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@vercel/ruby",
|
||||
"author": "Nathan Cahill <nathan@nathancahill.com>",
|
||||
"version": "1.3.65",
|
||||
"version": "1.3.66",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "index.rb", "use": "@vercel/ruby" }],
|
||||
"probes": [{ "path": "/", "mustContain": "gem:RANDOMNESS_PLACEHOLDER" }]
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "gem:RANDOMNESS_PLACEHOLDER" },
|
||||
{ "path": "/", "method": "HEAD", "status": 200 }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ def webrick_handler(httpMethod, path, body, headers)
|
||||
{
|
||||
:statusCode => res.code.to_i,
|
||||
:headers => res_headers,
|
||||
:body => res.body,
|
||||
:body => res.body.nil? ? "" : res.body,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/static-build",
|
||||
"version": "1.3.9",
|
||||
"version": "1.3.10",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/build-step",
|
||||
@@ -44,7 +44,7 @@
|
||||
"@types/semver": "7.3.13",
|
||||
"@vercel/build-utils": "6.3.0",
|
||||
"@vercel/frameworks": "1.3.1",
|
||||
"@vercel/fs-detectors": "3.7.14",
|
||||
"@vercel/fs-detectors": "3.8.0",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/routing-utils": "2.1.9",
|
||||
"@vercel/static-config": "2.0.13",
|
||||
|
||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -208,19 +208,19 @@ importers:
|
||||
'@vercel/client': 12.4.0
|
||||
'@vercel/error-utils': 1.0.8
|
||||
'@vercel/frameworks': 1.3.1
|
||||
'@vercel/fs-detectors': 3.7.14
|
||||
'@vercel/fs-detectors': 3.8.0
|
||||
'@vercel/fun': 1.0.4
|
||||
'@vercel/go': 2.3.7
|
||||
'@vercel/hydrogen': 0.0.53
|
||||
'@vercel/ncc': 0.24.0
|
||||
'@vercel/next': 3.4.7
|
||||
'@vercel/next': 3.5.2
|
||||
'@vercel/node': 2.9.6
|
||||
'@vercel/python': 3.1.49
|
||||
'@vercel/redwood': 1.1.5
|
||||
'@vercel/remix': 1.3.3
|
||||
'@vercel/remix': 1.3.5
|
||||
'@vercel/routing-utils': 2.1.9
|
||||
'@vercel/ruby': 1.3.65
|
||||
'@vercel/static-build': 1.3.9
|
||||
'@vercel/ruby': 1.3.66
|
||||
'@vercel/static-build': 1.3.10
|
||||
'@zeit/source-map-support': 0.6.2
|
||||
ajv: 6.12.2
|
||||
alpha-sort: 2.0.1
|
||||
@@ -989,7 +989,7 @@ importers:
|
||||
'@types/semver': 7.3.13
|
||||
'@vercel/build-utils': 6.3.0
|
||||
'@vercel/frameworks': 1.3.1
|
||||
'@vercel/fs-detectors': 3.7.14
|
||||
'@vercel/fs-detectors': 3.8.0
|
||||
'@vercel/gatsby-plugin-vercel-analytics': 1.0.7
|
||||
'@vercel/gatsby-plugin-vercel-builder': 1.1.7
|
||||
'@vercel/ncc': 0.24.0
|
||||
|
||||
@@ -384,7 +384,7 @@ async function fetchDeploymentUrl(url, opts) {
|
||||
for (let i = 0; i < 50; i += 1) {
|
||||
const resp = await fetch(url, opts);
|
||||
const text = await resp.text();
|
||||
if (text && !text.includes('Join Free')) {
|
||||
if (typeof text !== 'undefined' && !text.includes('Join Free')) {
|
||||
return { resp, text };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user