mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-25 19:00:01 +00:00
Compare commits
64 Commits
@vercel/no
...
vercel@28.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a92c68e0ff | ||
|
|
84f3a5bc1b | ||
|
|
186bd9bb51 | ||
|
|
9ae84ba05f | ||
|
|
a638755e95 | ||
|
|
15eb018f84 | ||
|
|
7f89aca52c | ||
|
|
dc9ec1bc6d | ||
|
|
df93b01248 | ||
|
|
fb37ad22ab | ||
|
|
684f69bc5b | ||
|
|
ddae78458d | ||
|
|
fc614a7a92 | ||
|
|
6e4ea0774e | ||
|
|
cfc1c9e818 | ||
|
|
62a872fc0e | ||
|
|
03b5bfbaa2 | ||
|
|
aa305e5c66 | ||
|
|
2309c43fce | ||
|
|
0bbb06daa7 | ||
|
|
61de63d285 | ||
|
|
1ca3704297 | ||
|
|
ae4180b287 | ||
|
|
40081cb319 | ||
|
|
9200be61d2 | ||
|
|
1390f6d2ee | ||
|
|
b78cfc9ba5 | ||
|
|
803a9363f9 | ||
|
|
4a0a3b64a2 | ||
|
|
347c2de3a2 | ||
|
|
a05cc11719 | ||
|
|
55b999ea9b | ||
|
|
8babc3694f | ||
|
|
6dc0321216 | ||
|
|
3df8c05792 | ||
|
|
e0f8bc9820 | ||
|
|
ebd2e1822c | ||
|
|
31bc2581f3 | ||
|
|
96759a9fda | ||
|
|
ad2864bca5 | ||
|
|
40fbc993d7 | ||
|
|
1f30e56a6d | ||
|
|
58d6268899 | ||
|
|
4e6659ace7 | ||
|
|
769234b6a6 | ||
|
|
53cab61e88 | ||
|
|
3a65acfb32 | ||
|
|
48eed7532a | ||
|
|
f09d6fce85 | ||
|
|
cc82c499db | ||
|
|
f0bc207717 | ||
|
|
151c7f99ee | ||
|
|
61c2c494bf | ||
|
|
7f49816129 | ||
|
|
7845bef826 | ||
|
|
7acb2e4b07 | ||
|
|
abea002e93 | ||
|
|
2db9678bd4 | ||
|
|
05f942c53f | ||
|
|
6c5f0b7aa0 | ||
|
|
fea05383b9 | ||
|
|
247f49f765 | ||
|
|
231714c71b | ||
|
|
73d6f0d0fa |
31
.github/workflows/cron-update-gatsby-fixtures.yml
vendored
Normal file
31
.github/workflows/cron-update-gatsby-fixtures.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Cron Update Gatsby Fixtures
|
||||
|
||||
on:
|
||||
# Allow manual runs
|
||||
workflow_dispatch:
|
||||
# Run once a week https://crontab.guru/once-a-week
|
||||
schedule:
|
||||
- cron: '0 0 * * 0'
|
||||
|
||||
jobs:
|
||||
create-pull-request:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
# 0 means fetch all commits so we can commit and push in the script below
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Enable corepack
|
||||
run: corepack enable pnpm
|
||||
- name: Create Pull Request
|
||||
uses: actions/github-script@v6
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
|
||||
# See https://github.com/actions/github-script#run-a-separate-file-with-an-async-function
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
|
||||
script: |
|
||||
const script = require('./utils/update-gatsby-fixtures.js')
|
||||
await script({ github, context })
|
||||
1
.github/workflows/cron-update-next.yml
vendored
1
.github/workflows/cron-update-next.yml
vendored
@@ -25,6 +25,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
|
||||
# See https://github.com/actions/github-script#run-a-separate-file-with-an-async-function
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
|
||||
script: |
|
||||
const script = require('./utils/update-next.js')
|
||||
await script({ github, context })
|
||||
|
||||
1
.github/workflows/cron-update-turbo.yml
vendored
1
.github/workflows/cron-update-turbo.yml
vendored
@@ -25,6 +25,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
|
||||
# See https://github.com/actions/github-script#run-a-separate-file-with-an-async-function
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
|
||||
script: |
|
||||
const script = require('./utils/update-turbo.js')
|
||||
await script({ github, context })
|
||||
|
||||
5
.github/workflows/publish.yml
vendored
5
.github/workflows/publish.yml
vendored
@@ -16,6 +16,9 @@ jobs:
|
||||
publish:
|
||||
name: Publish
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
@@ -41,6 +44,8 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: install npm@9
|
||||
run: npm i -g npm@9
|
||||
- name: install pnpm@7.24.2
|
||||
run: npm i -g pnpm@7.24.2
|
||||
- name: Install
|
||||
|
||||
28
.github/workflows/update-remix-run-dev.yml
vendored
Normal file
28
.github/workflows/update-remix-run-dev.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Update @remix-run/dev
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
new-version:
|
||||
type: string
|
||||
description: "Optional version to update @remix-run/dev to inside of @vercel/remix"
|
||||
|
||||
jobs:
|
||||
update-remix-run-dev:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
- name: Enable corepack
|
||||
run: corepack enable pnpm
|
||||
- name: Update @remix-run/dev
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
|
||||
script: |
|
||||
const script = require('./utils/update-remix-run-dev.js')
|
||||
await script({ github, context }, "${{ inputs.new-version }}")
|
||||
1
.npmrc
1
.npmrc
@@ -1,3 +1,4 @@
|
||||
provenance=true
|
||||
save-exact=true
|
||||
hoist-pattern[]=!"**/@types/**"
|
||||
hoist-pattern[]=!"**/typedoc"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
|
||||
2515
examples/nextjs/package-lock.json
generated
2515
examples/nextjs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,10 +9,9 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@next/font": "13.1.6",
|
||||
"eslint": "8.32.0",
|
||||
"eslint-config-next": "13.1.6",
|
||||
"next": "13.1.6",
|
||||
"eslint": "8.35.0",
|
||||
"eslint-config-next": "13.2.3",
|
||||
"next": "13.2.3",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Head from 'next/head'
|
||||
import Image from 'next/image'
|
||||
import { Inter } from '@next/font/google'
|
||||
import { Inter } from 'next/font/google'
|
||||
import styles from '@/styles/Home.module.css'
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] })
|
||||
|
||||
@@ -9,6 +9,10 @@ export default function (
|
||||
remixContext: EntryContext
|
||||
) {
|
||||
const remixServer = <RemixServer context={remixContext} url={request.url} />;
|
||||
return handleRequest(request, responseStatusCode, responseHeaders, remixServer)
|
||||
return handleRequest(
|
||||
request,
|
||||
responseStatusCode,
|
||||
responseHeaders,
|
||||
remixServer
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
Scripts,
|
||||
ScrollRestoration,
|
||||
} from "@remix-run/react";
|
||||
import { Analytics } from '@vercel/analytics/react';
|
||||
import { Analytics } from "@vercel/analytics/react";
|
||||
|
||||
export const meta: MetaFunction = () => ({
|
||||
charset: "utf-8",
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
export const config = {
|
||||
runtime: 'edge'
|
||||
};
|
||||
export const config = { runtime: "edge" };
|
||||
|
||||
export default function Edge() {
|
||||
return (
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"source-map-support": "0.5.12",
|
||||
"ts-eager": "2.0.2",
|
||||
"ts-jest": "28.0.5",
|
||||
"turbo": "1.7.4"
|
||||
"turbo": "1.8.3"
|
||||
},
|
||||
"scripts": {
|
||||
"lerna": "lerna",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "6.3.1",
|
||||
"version": "6.3.2",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -25,12 +25,16 @@ export async function readConfigFile<T>(
|
||||
|
||||
if (data) {
|
||||
const str = data.toString('utf8');
|
||||
if (name.endsWith('.json')) {
|
||||
return JSON.parse(str) as T;
|
||||
} else if (name.endsWith('.toml')) {
|
||||
return (toml.parse(str) as unknown) as T;
|
||||
} else if (name.endsWith('.yaml') || name.endsWith('.yml')) {
|
||||
return yaml.safeLoad(str, { filename: name }) as T;
|
||||
try {
|
||||
if (name.endsWith('.json')) {
|
||||
return JSON.parse(str) as T;
|
||||
} else if (name.endsWith('.toml')) {
|
||||
return toml.parse(str) as unknown as T;
|
||||
} else if (name.endsWith('.yaml') || name.endsWith('.yml')) {
|
||||
return yaml.safeLoad(str, { filename: name }) as T;
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.log(`Error while parsing config file: "${name}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
77
packages/build-utils/test/unit.read-config-file.test.ts
vendored
Normal file
77
packages/build-utils/test/unit.read-config-file.test.ts
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
import { join } from 'path';
|
||||
import { writeFile, rm } from 'fs/promises';
|
||||
import { readConfigFile } from '../src';
|
||||
|
||||
describe('Test `readConfigFile()`', () => {
|
||||
let logMessages: string[];
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
beforeEach(() => {
|
||||
logMessages = [];
|
||||
console.log = m => {
|
||||
logMessages.push(m);
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
console.log = originalConsoleLog;
|
||||
});
|
||||
|
||||
const doesnotexist = join(__dirname, 'does-not-exist.json');
|
||||
const tsconfig = join(__dirname, '../tsconfig.json');
|
||||
const invalid = join(__dirname, 'invalid.json');
|
||||
|
||||
it('should return null when file does not exist', async () => {
|
||||
expect(await readConfigFile(doesnotexist)).toBeNull();
|
||||
expect(logMessages).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return parsed object when file exists', async () => {
|
||||
expect(await readConfigFile(tsconfig)).toMatchObject({
|
||||
compilerOptions: {
|
||||
strict: true,
|
||||
},
|
||||
});
|
||||
expect(logMessages).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return parsed object when at least one file exists', async () => {
|
||||
const files = [doesnotexist, tsconfig];
|
||||
expect(await readConfigFile(files)).toMatchObject({
|
||||
compilerOptions: {
|
||||
strict: true,
|
||||
},
|
||||
});
|
||||
expect(logMessages).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return null when parse fails', async () => {
|
||||
try {
|
||||
await writeFile(invalid, 'borked');
|
||||
expect(await readConfigFile(invalid)).toBeNull();
|
||||
} finally {
|
||||
await rm(invalid);
|
||||
}
|
||||
expect(logMessages.length).toBe(1);
|
||||
expect(logMessages[0]).toMatch(
|
||||
/^Error while parsing config file.+invalid.json/
|
||||
);
|
||||
});
|
||||
|
||||
it('should return parsed object when at least one file is valid', async () => {
|
||||
try {
|
||||
await writeFile(invalid, 'borked');
|
||||
expect(await readConfigFile([invalid, tsconfig])).toMatchObject({
|
||||
compilerOptions: {
|
||||
strict: true,
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
await rm(invalid);
|
||||
}
|
||||
expect(logMessages.length).toBe(1);
|
||||
expect(logMessages[0]).toMatch(
|
||||
/^Error while parsing config file.+invalid.json/
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "28.16.5",
|
||||
"version": "28.16.13",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -41,16 +41,16 @@
|
||||
"node": ">= 14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "6.3.1",
|
||||
"@vercel/go": "2.3.8",
|
||||
"@vercel/hydrogen": "0.0.54",
|
||||
"@vercel/next": "3.6.0",
|
||||
"@vercel/node": "2.9.7",
|
||||
"@vercel/python": "3.1.50",
|
||||
"@vercel/redwood": "1.1.6",
|
||||
"@vercel/remix": "1.4.0",
|
||||
"@vercel/ruby": "1.3.67",
|
||||
"@vercel/static-build": "1.3.11"
|
||||
"@vercel/build-utils": "6.3.2",
|
||||
"@vercel/go": "2.3.9",
|
||||
"@vercel/hydrogen": "0.0.55",
|
||||
"@vercel/next": "3.6.4",
|
||||
"@vercel/node": "2.9.10",
|
||||
"@vercel/python": "3.1.51",
|
||||
"@vercel/redwood": "1.1.7",
|
||||
"@vercel/remix": "1.6.0",
|
||||
"@vercel/ruby": "1.3.68",
|
||||
"@vercel/static-build": "1.3.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alex_neo/jest-expect-message": "1.0.5",
|
||||
@@ -93,13 +93,13 @@
|
||||
"@types/which": "1.3.2",
|
||||
"@types/write-json-file": "2.2.1",
|
||||
"@types/yauzl-promise": "2.1.0",
|
||||
"@vercel/client": "12.4.1",
|
||||
"@vercel/client": "12.4.2",
|
||||
"@vercel/error-utils": "1.0.8",
|
||||
"@vercel/frameworks": "1.3.1",
|
||||
"@vercel/fs-detectors": "3.8.1",
|
||||
"@vercel/frameworks": "1.3.2",
|
||||
"@vercel/fs-detectors": "3.8.2",
|
||||
"@vercel/fun": "1.0.4",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/routing-utils": "2.1.9",
|
||||
"@vercel/routing-utils": "2.1.10",
|
||||
"@zeit/source-map-support": "0.6.2",
|
||||
"ajv": "6.12.2",
|
||||
"alpha-sort": "2.0.1",
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ProjectLinkAndSettings } from '../projects/project-settings';
|
||||
import { Output } from '../output';
|
||||
import title from 'title';
|
||||
import { PartialProjectSettings } from '../input/edit-project-settings';
|
||||
import { debug } from '@vercel/build-utils';
|
||||
|
||||
export async function setMonorepoDefaultSettings(
|
||||
cwd: string,
|
||||
@@ -26,8 +27,8 @@ export async function setMonorepoDefaultSettings(
|
||||
value: string
|
||||
) => {
|
||||
if (projectSettings[command]) {
|
||||
output.warn(
|
||||
`Cannot automatically assign ${command} as it is already set via project settings or configuration overrides.`
|
||||
debug(
|
||||
`Skipping auto-assignment of ${command} as it is already set via project settings or configuration overrides.`
|
||||
);
|
||||
} else {
|
||||
projectSettings[command] = value;
|
||||
|
||||
@@ -6,6 +6,4 @@
|
||||
{{?}}
|
||||
<span class="devinfo-line">ID: <code>{{! it.request_id }}</code>
|
||||
</p>
|
||||
|
||||
<a href="https://vercel.link/404"><div class="note">Click here to learn more about this error.</div></a>
|
||||
</main>
|
||||
|
||||
@@ -247,6 +247,14 @@ async function fetchDistTags(name) {
|
||||
});
|
||||
res.on('end', () => {
|
||||
try {
|
||||
if (res.statusCode && res.statusCode >= 400) {
|
||||
return reject(
|
||||
new Error(
|
||||
`Fetch dist-tags failed ${res.statusCode} ${res.statusMessage}`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
resolve(JSON.parse(buf));
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
|
||||
@@ -54,7 +54,7 @@ export default async function list(
|
||||
eraseFinalAnswer = false, // If true, the line with the final answer that inquirer prints will be erased before returning
|
||||
}: ListOptions
|
||||
): Promise<string> {
|
||||
require('./patch-inquirer-legacy');
|
||||
require('./patch-inquirer');
|
||||
|
||||
let biggestLength = 0;
|
||||
let selected: string | undefined;
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import chalk from 'chalk';
|
||||
import inquirer from 'inquirer';
|
||||
import Prompt from 'inquirer/lib/prompts/base';
|
||||
|
||||
// Here we patch inquirer to use a `>` instead of the ugly green `?`
|
||||
|
||||
/* eslint-disable no-multiple-empty-lines, no-var, no-undef, no-eq-null, eqeqeq, semi */
|
||||
const getQuestion = function (this: Prompt) {
|
||||
var message = `${chalk.bold(`> ${this.opt.message}`)} `;
|
||||
|
||||
// Append the default if available, and if question isn't answered
|
||||
if (this.opt.default != null && this.status !== 'answered') {
|
||||
message += chalk.dim(`(${this.opt.default}) `);
|
||||
}
|
||||
|
||||
return message;
|
||||
};
|
||||
/* eslint-enable */
|
||||
|
||||
inquirer.prompt.prompts.input.prototype.getQuestion = getQuestion;
|
||||
inquirer.prompt.prompts.list.prototype.getQuestion = getQuestion;
|
||||
@@ -84,7 +84,10 @@ function listRender(choices: (Choice | Separator)[], pointer: number) {
|
||||
|
||||
let isSelected = i - separatorOffset === pointer;
|
||||
let line = (isSelected ? '● ' : '○ ') + choice.name;
|
||||
line = chalk.cyan(line);
|
||||
|
||||
if (isSelected) {
|
||||
line = chalk.cyan(line);
|
||||
}
|
||||
output += line + ' \n';
|
||||
});
|
||||
|
||||
|
||||
@@ -484,6 +484,7 @@ describe('build', () => {
|
||||
{
|
||||
src: '^/.*$',
|
||||
middlewarePath: 'middleware',
|
||||
middlewareRawSrc: [],
|
||||
override: true,
|
||||
continue: true,
|
||||
},
|
||||
@@ -548,6 +549,7 @@ describe('build', () => {
|
||||
{
|
||||
src: '^/.*$',
|
||||
middlewarePath: 'middleware',
|
||||
middlewareRawSrc: [],
|
||||
override: true,
|
||||
continue: true,
|
||||
},
|
||||
@@ -612,6 +614,7 @@ describe('build', () => {
|
||||
{
|
||||
src: '^\\/about(?:\\/((?:[^\\/#\\?]+?)(?:\\/(?:[^\\/#\\?]+?))*))?[\\/#\\?]?$|^\\/dashboard(?:\\/((?:[^\\/#\\?]+?)(?:\\/(?:[^\\/#\\?]+?))*))?[\\/#\\?]?$',
|
||||
middlewarePath: 'middleware',
|
||||
middlewareRawSrc: ['/about/:path*', '/dashboard/:path*'],
|
||||
override: true,
|
||||
continue: true,
|
||||
},
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('login', () => {
|
||||
const user = useUser();
|
||||
client.setArgv('login');
|
||||
const exitCodePromise = login(client);
|
||||
await expect(client.stderr).toOutput(`> Log in to Vercel`);
|
||||
await expect(client.stderr).toOutput(`? Log in to Vercel`);
|
||||
|
||||
// Move down to "Email" option
|
||||
client.stdin.write('\x1B[B'); // Down arrow
|
||||
@@ -36,7 +36,7 @@ describe('login', () => {
|
||||
client.stdin.write('\x1B[B'); // Down arrow
|
||||
client.stdin.write('\r'); // Return key
|
||||
|
||||
await expect(client.stderr).toOutput('> Enter your email address:');
|
||||
await expect(client.stderr).toOutput('? Enter your email address:');
|
||||
|
||||
client.stdin.write(`${user.email}\n`);
|
||||
|
||||
@@ -51,7 +51,7 @@ describe('login', () => {
|
||||
const user = useUser();
|
||||
client.setArgv('login', '--no-color');
|
||||
const exitCodePromise = login(client);
|
||||
await expect(client.stderr).toOutput(`> Log in to Vercel`);
|
||||
await expect(client.stderr).toOutput(`? Log in to Vercel`);
|
||||
|
||||
// Move down to "Email" option
|
||||
client.stdin.write('\x1B[B'); // Down arrow
|
||||
@@ -59,7 +59,7 @@ describe('login', () => {
|
||||
client.stdin.write('\x1B[B'); // Down arrow
|
||||
client.stdin.write('\r'); // Return key
|
||||
|
||||
await expect(client.stderr).toOutput('> Enter your email address:');
|
||||
await expect(client.stderr).toOutput('? Enter your email address:');
|
||||
|
||||
client.stdin.write(`${user.email}\n`);
|
||||
|
||||
@@ -93,7 +93,7 @@ describe('login', () => {
|
||||
const user = useUser();
|
||||
client.setArgv('login');
|
||||
const exitCodePromise = login(client);
|
||||
await expect(client.stderr).toOutput(`> Log in to Vercel`);
|
||||
await expect(client.stderr).toOutput(`? Log in to Vercel`);
|
||||
|
||||
// Move down to "Email" option
|
||||
client.stdin.write('\x1B[B'); // Down arrow
|
||||
@@ -101,7 +101,7 @@ describe('login', () => {
|
||||
client.stdin.write('\x1B[B'); // Down arrow
|
||||
client.stdin.write('\r'); // Return key
|
||||
|
||||
await expect(client.stderr).toOutput('> Enter your email address:');
|
||||
await expect(client.stderr).toOutput('? Enter your email address:');
|
||||
|
||||
client.stdin.write(`${user.email}\n`);
|
||||
|
||||
@@ -136,7 +136,7 @@ describe('login', () => {
|
||||
const user = useUser();
|
||||
client.setArgv('login');
|
||||
const exitCodePromise = login(client);
|
||||
await expect(client.stderr).toOutput(`> Log in to Vercel`);
|
||||
await expect(client.stderr).toOutput(`? Log in to Vercel`);
|
||||
|
||||
// Move down to "Email" option
|
||||
client.stdin.write('\x1B[B'); // Down arrow
|
||||
@@ -144,7 +144,7 @@ describe('login', () => {
|
||||
client.stdin.write('\x1B[B'); // Down arrow
|
||||
client.stdin.write('\r'); // Return key
|
||||
|
||||
await expect(client.stderr).toOutput('> Enter your email address:');
|
||||
await expect(client.stderr).toOutput('? Enter your email address:');
|
||||
|
||||
client.stdin.write(`${user.email}\n`);
|
||||
|
||||
|
||||
13
packages/cli/turbo.json
Normal file
13
packages/cli/turbo.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://turborepo.org/schema.json",
|
||||
"extends": ["//"],
|
||||
"pipeline": {
|
||||
"build": {
|
||||
"outputs": [
|
||||
"dist/**",
|
||||
"src/util/constants.ts",
|
||||
"src/util/dev/templates/*.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "12.4.1",
|
||||
"version": "12.4.2",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"homepage": "https://vercel.com",
|
||||
@@ -43,8 +43,8 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "6.3.1",
|
||||
"@vercel/routing-utils": "2.1.9",
|
||||
"@vercel/build-utils": "6.3.2",
|
||||
"@vercel/routing-utils": "2.1.10",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "3.0.0",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
- [ExtraResponseInit](interfaces/ExtraResponseInit.md)
|
||||
- [Geo](interfaces/Geo.md)
|
||||
- [ModifiedRequest](interfaces/ModifiedRequest.md)
|
||||
- [RequestContext](interfaces/RequestContext.md)
|
||||
|
||||
### Variables
|
||||
|
||||
|
||||
79
packages/edge/docs/interfaces/RequestContext.md
Normal file
79
packages/edge/docs/interfaces/RequestContext.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Interface: RequestContext
|
||||
|
||||
An extension to the standard `Request` object that is passed to every Edge Function.
|
||||
|
||||
**`Example`**
|
||||
|
||||
```ts
|
||||
import type { RequestContext } from '@vercel/edge';
|
||||
|
||||
export default async function handler(
|
||||
request: Request,
|
||||
ctx: RequestContext
|
||||
): Promise<Response> {
|
||||
// ctx is the RequestContext
|
||||
}
|
||||
```
|
||||
|
||||
## Table of contents
|
||||
|
||||
### Methods
|
||||
|
||||
- [waitUntil](RequestContext.md#waituntil)
|
||||
|
||||
## Methods
|
||||
|
||||
### waitUntil
|
||||
|
||||
▸ **waitUntil**(`promise`): `void`
|
||||
|
||||
A method that can be used to keep the function running after a response has been sent.
|
||||
This is useful when you have an async task that you want to keep running even after the
|
||||
response has been sent and the request has ended.
|
||||
|
||||
**`Example`**
|
||||
|
||||
<caption>Sending an internal error to an error tracking service</caption>
|
||||
|
||||
```ts
|
||||
import type { RequestContext } from '@vercel/edge';
|
||||
|
||||
export async function handleRequest(
|
||||
request: Request,
|
||||
ctx: RequestContext
|
||||
): Promise<Response> {
|
||||
try {
|
||||
return await myFunctionThatReturnsResponse();
|
||||
} catch (e) {
|
||||
ctx.waitUntil(
|
||||
(async () => {
|
||||
// report this error to your error tracking service
|
||||
await fetch('https://my-error-tracking-service.com', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
stack: e.stack,
|
||||
message: e.message,
|
||||
name: e.name,
|
||||
url: request.url,
|
||||
}),
|
||||
});
|
||||
})()
|
||||
);
|
||||
return new Response('Internal Server Error', { status: 500 });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :-------- | :---------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- |
|
||||
| `promise` | [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<`unknown`\> | A promise that will be kept alive until it resolves or rejects. |
|
||||
|
||||
#### Returns
|
||||
|
||||
`void`
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/edge/src/request.ts:47](https://github.com/vercel/vercel/blob/main/packages/edge/src/request.ts#L47)
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/edge",
|
||||
"version": "0.2.7",
|
||||
"version": "0.3.1",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.mjs",
|
||||
|
||||
@@ -4,5 +4,6 @@ export * from './middleware-helpers';
|
||||
export type { Geo } from './edge-headers';
|
||||
export * from './edge-headers';
|
||||
export * from './response';
|
||||
export type { RequestContext } from './request';
|
||||
|
||||
import './published-types.d.ts';
|
||||
|
||||
52
packages/edge/src/request.ts
Normal file
52
packages/edge/src/request.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* An extension to the standard `Request` object that is passed to every Edge Function.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import type { RequestContext } from '@vercel/edge';
|
||||
*
|
||||
* export default async function handler(request: Request, ctx: RequestContext): Promise<Response> {
|
||||
* // ctx is the RequestContext
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export interface RequestContext {
|
||||
/**
|
||||
* A method that can be used to keep the function running after a response has been sent.
|
||||
* This is useful when you have an async task that you want to keep running even after the
|
||||
* response has been sent and the request has ended.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* <caption>Sending an internal error to an error tracking service</caption>
|
||||
*
|
||||
* ```ts
|
||||
* import type { RequestContext } from '@vercel/edge';
|
||||
*
|
||||
* export async function handleRequest(request: Request, ctx: RequestContext): Promise<Response> {
|
||||
* try {
|
||||
* return await myFunctionThatReturnsResponse();
|
||||
* } catch (e) {
|
||||
* ctx.waitUntil((async () => {
|
||||
* // report this error to your error tracking service
|
||||
* await fetch('https://my-error-tracking-service.com', {
|
||||
* method: 'POST',
|
||||
* body: JSON.stringify({
|
||||
* stack: e.stack,
|
||||
* message: e.message,
|
||||
* name: e.name,
|
||||
* url: request.url,
|
||||
* }),
|
||||
* });
|
||||
* })());
|
||||
* return new Response('Internal Server Error', { status: 500 });
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
waitUntil(
|
||||
/**
|
||||
* A promise that will be kept alive until it resolves or rejects.
|
||||
*/ promise: Promise<unknown>
|
||||
): void;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/frameworks",
|
||||
"version": "1.3.1",
|
||||
"version": "1.3.2",
|
||||
"main": "./dist/frameworks.js",
|
||||
"types": "./dist/frameworks.d.ts",
|
||||
"files": [
|
||||
@@ -21,7 +21,7 @@
|
||||
"@types/js-yaml": "3.12.1",
|
||||
"@types/node": "14.18.33",
|
||||
"@types/node-fetch": "2.5.8",
|
||||
"@vercel/routing-utils": "2.1.9",
|
||||
"@vercel/routing-utils": "2.1.10",
|
||||
"ajv": "6.12.2",
|
||||
"typescript": "4.3.4"
|
||||
}
|
||||
|
||||
@@ -202,12 +202,13 @@ export const frameworks = [
|
||||
useRuntime: { src: 'package.json', use: '@vercel/remix' },
|
||||
ignoreRuntimes: ['@vercel/node'],
|
||||
detectors: {
|
||||
every: [
|
||||
// Intentionally does not detect a package name
|
||||
// https://github.com/vercel/vercel/pull/7761
|
||||
some: [
|
||||
{
|
||||
path: 'remix.config.js',
|
||||
},
|
||||
{
|
||||
path: 'remix.config.mjs',
|
||||
},
|
||||
],
|
||||
},
|
||||
settings: {
|
||||
@@ -1698,6 +1699,7 @@ export const frameworks = [
|
||||
description: 'React framework for headless commerce',
|
||||
website: 'https://hydrogen.shopify.dev',
|
||||
useRuntime: { src: 'package.json', use: '@vercel/hydrogen' },
|
||||
envPrefix: 'PUBLIC_',
|
||||
detectors: {
|
||||
some: [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/fs-detectors",
|
||||
"version": "3.8.1",
|
||||
"version": "3.8.2",
|
||||
"description": "Vercel filesystem detectors",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -20,8 +20,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/error-utils": "1.0.8",
|
||||
"@vercel/frameworks": "1.3.1",
|
||||
"@vercel/routing-utils": "2.1.9",
|
||||
"@vercel/frameworks": "1.3.2",
|
||||
"@vercel/routing-utils": "2.1.10",
|
||||
"glob": "8.0.3",
|
||||
"js-yaml": "4.1.0",
|
||||
"json5": "2.2.2",
|
||||
@@ -35,7 +35,7 @@
|
||||
"@types/minimatch": "3.0.5",
|
||||
"@types/node": "14.18.33",
|
||||
"@types/semver": "7.3.10",
|
||||
"@vercel/build-utils": "6.3.1",
|
||||
"@vercel/build-utils": "6.3.2",
|
||||
"typescript": "4.3.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/gatsby-plugin-vercel-analytics",
|
||||
"version": "1.0.7",
|
||||
"version": "1.0.8",
|
||||
"description": "Track Core Web Vitals in Gatsby projects with Vercel Analytics.",
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
|
||||
9
packages/gatsby-plugin-vercel-analytics/turbo.json
Normal file
9
packages/gatsby-plugin-vercel-analytics/turbo.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "https://turborepo.org/schema.json",
|
||||
"extends": ["//"],
|
||||
"pipeline": {
|
||||
"build": {
|
||||
"outputs": ["gatsby-browser.js", "web-vitals.js", "index.js"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/gatsby-plugin-vercel-builder",
|
||||
"version": "1.1.8",
|
||||
"version": "1.1.11",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
"dist",
|
||||
@@ -14,9 +14,9 @@
|
||||
"build:src": "tsc -p tsconfig.src.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "6.3.1",
|
||||
"@vercel/node": "2.9.7",
|
||||
"@vercel/routing-utils": "2.1.9",
|
||||
"@vercel/build-utils": "6.3.2",
|
||||
"@vercel/node": "2.9.10",
|
||||
"@vercel/routing-utils": "2.1.10",
|
||||
"ajv": "8.12.0",
|
||||
"esbuild": "0.14.47",
|
||||
"etag": "1.8.1",
|
||||
|
||||
9
packages/gatsby-plugin-vercel-builder/turbo.json
Normal file
9
packages/gatsby-plugin-vercel-builder/turbo.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "https://turborepo.org/schema.json",
|
||||
"extends": ["//"],
|
||||
"pipeline": {
|
||||
"build": {
|
||||
"outputs": ["dist/**", "gatsby-node.js"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/go",
|
||||
"version": "2.3.8",
|
||||
"version": "2.3.9",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
|
||||
@@ -36,7 +36,7 @@
|
||||
"@types/node": "14.18.33",
|
||||
"@types/node-fetch": "^2.3.0",
|
||||
"@types/tar": "^4.0.0",
|
||||
"@vercel/build-utils": "6.3.1",
|
||||
"@vercel/build-utils": "6.3.2",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"async-retry": "1.3.1",
|
||||
"execa": "^1.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/hydrogen",
|
||||
"version": "0.0.54",
|
||||
"version": "0.0.55",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
@@ -21,7 +21,7 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "27.5.1",
|
||||
"@types/node": "14.18.33",
|
||||
"@vercel/build-utils": "6.3.1",
|
||||
"@vercel/build-utils": "6.3.2",
|
||||
"@vercel/static-config": "2.0.13",
|
||||
"execa": "3.2.0",
|
||||
"fs-extra": "11.1.0",
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
execCommand,
|
||||
getEnvForPackageManager,
|
||||
getNodeVersion,
|
||||
getPrefixedEnvVars,
|
||||
getSpawnOptions,
|
||||
glob,
|
||||
readConfigFile,
|
||||
@@ -29,6 +30,15 @@ export const build: BuildV2 = async ({
|
||||
|
||||
await download(files, workPath, meta);
|
||||
|
||||
const prefixedEnvs = getPrefixedEnvVars({
|
||||
envPrefix: 'PUBLIC_',
|
||||
envs: process.env,
|
||||
});
|
||||
|
||||
for (const [key, value] of Object.entries(prefixedEnvs)) {
|
||||
process.env[key] = value;
|
||||
}
|
||||
|
||||
const mountpoint = dirname(entrypoint);
|
||||
const entrypointDir = join(workPath, mountpoint);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/next",
|
||||
"version": "3.6.0",
|
||||
"version": "3.6.4",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
|
||||
@@ -45,9 +45,9 @@
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/text-table": "0.2.1",
|
||||
"@types/webpack-sources": "3.2.0",
|
||||
"@vercel/build-utils": "6.3.1",
|
||||
"@vercel/build-utils": "6.3.2",
|
||||
"@vercel/nft": "0.22.5",
|
||||
"@vercel/routing-utils": "2.1.9",
|
||||
"@vercel/routing-utils": "2.1.10",
|
||||
"async-sema": "3.0.1",
|
||||
"buffer-crc32": "0.2.13",
|
||||
"bytes": "3.1.2",
|
||||
|
||||
@@ -150,6 +150,19 @@ export async function serverBuild({
|
||||
nextVersion,
|
||||
EMPTY_ALLOW_QUERY_FOR_PRERENDERED_VERSION
|
||||
);
|
||||
const projectDir = requiredServerFilesManifest.relativeAppDir
|
||||
? path.join(baseDir, requiredServerFilesManifest.relativeAppDir)
|
||||
: requiredServerFilesManifest.appDir || entryPath;
|
||||
|
||||
// allow looking up original route from normalized route
|
||||
const inversedAppPathManifest: Record<string, string> = {};
|
||||
|
||||
if (appPathRoutesManifest) {
|
||||
for (const ogRoute of Object.keys(appPathRoutesManifest)) {
|
||||
inversedAppPathManifest[appPathRoutesManifest[ogRoute]] = ogRoute;
|
||||
}
|
||||
}
|
||||
|
||||
let appBuildTraces: UnwrapPromise<ReturnType<typeof glob>> = {};
|
||||
let appDir: string | null = null;
|
||||
|
||||
@@ -182,8 +195,9 @@ export async function serverBuild({
|
||||
}
|
||||
|
||||
const pageMatchesApi = (page: string) => {
|
||||
const normalizedPage = `/${page.replace(/\.js$/, '')}`;
|
||||
return (
|
||||
!appPathRoutesManifest?.[page] &&
|
||||
!inversedAppPathManifest[normalizedPage] &&
|
||||
(page.startsWith('api/') || page === 'api.js')
|
||||
);
|
||||
};
|
||||
@@ -269,9 +283,10 @@ export async function serverBuild({
|
||||
let initialFileList: string[];
|
||||
let initialFileReasons: NodeFileTraceReasons;
|
||||
let nextServerBuildTrace;
|
||||
let instrumentationHookBuildTrace;
|
||||
|
||||
const nextServerFile = resolveFrom(
|
||||
requiredServerFilesManifest.appDir || entryPath,
|
||||
projectDir,
|
||||
`${getNextServerPath(nextVersion)}/next-server.js`
|
||||
);
|
||||
|
||||
@@ -287,6 +302,22 @@ export async function serverBuild({
|
||||
// if the trace is unavailable we trace inside the runtime
|
||||
}
|
||||
|
||||
try {
|
||||
instrumentationHookBuildTrace = JSON.parse(
|
||||
await fs.readFile(
|
||||
path.join(
|
||||
entryPath,
|
||||
outputDirectory,
|
||||
'server',
|
||||
'instrumentation.js.nft.json'
|
||||
),
|
||||
'utf8'
|
||||
)
|
||||
);
|
||||
} catch (_) {
|
||||
// if the trace is unavailable it means `instrumentation.js` wasn't used
|
||||
}
|
||||
|
||||
if (nextServerBuildTrace) {
|
||||
initialFileList = nextServerBuildTrace.files.map((file: string) => {
|
||||
return path.relative(
|
||||
@@ -321,6 +352,18 @@ export async function serverBuild({
|
||||
initialFileReasons = result.reasons;
|
||||
}
|
||||
|
||||
if (instrumentationHookBuildTrace) {
|
||||
initialFileList = initialFileList.concat(
|
||||
instrumentationHookBuildTrace.files.map((file: string) => {
|
||||
return path.relative(
|
||||
baseDir,
|
||||
path.join(entryPath, outputDirectory, 'server', file)
|
||||
);
|
||||
})
|
||||
);
|
||||
debug('Using instrumentation.js.nft.json trace from build');
|
||||
}
|
||||
|
||||
debug('collecting initial Next.js server files');
|
||||
await Promise.all(
|
||||
initialFileList.map(
|
||||
@@ -437,8 +480,8 @@ export async function serverBuild({
|
||||
file
|
||||
);
|
||||
|
||||
if (requiredServerFilesManifest.appDir) {
|
||||
fsPath = path.join(requiredServerFilesManifest.appDir, file);
|
||||
if (projectDir) {
|
||||
fsPath = path.join(projectDir, file);
|
||||
}
|
||||
|
||||
const relativePath = path.relative(baseDir, fsPath);
|
||||
@@ -516,7 +559,7 @@ export async function serverBuild({
|
||||
`conf: ${JSON.stringify({
|
||||
...requiredServerFilesManifest.config,
|
||||
distDir: path.relative(
|
||||
requiredServerFilesManifest.appDir || entryPath,
|
||||
projectDir,
|
||||
path.join(entryPath, outputDirectory)
|
||||
),
|
||||
compress: false,
|
||||
@@ -543,10 +586,8 @@ export async function serverBuild({
|
||||
}
|
||||
|
||||
const launcherFiles: { [name: string]: FileFsRef | FileBlob } = {
|
||||
[path.join(
|
||||
path.relative(baseDir, requiredServerFilesManifest.appDir || entryPath),
|
||||
'___next_launcher.cjs'
|
||||
)]: new FileBlob({ data: launcher }),
|
||||
[path.join(path.relative(baseDir, projectDir), '___next_launcher.cjs')]:
|
||||
new FileBlob({ data: launcher }),
|
||||
};
|
||||
const pageTraces: {
|
||||
[page: string]: { [key: string]: FileFsRef };
|
||||
@@ -594,7 +635,7 @@ export async function serverBuild({
|
||||
traceResult = await nodeFileTrace(pathsToTrace, {
|
||||
base: baseDir,
|
||||
cache: traceCache,
|
||||
processCwd: requiredServerFilesManifest.appDir || entryPath,
|
||||
processCwd: projectDir,
|
||||
});
|
||||
traceResult.esmFileList.forEach(file => traceResult?.fileList.add(file));
|
||||
parentFilesMap = getFilesMapFromReasons(
|
||||
@@ -703,7 +744,7 @@ export async function serverBuild({
|
||||
const pageExtensions = requiredServerFilesManifest.config?.pageExtensions;
|
||||
|
||||
const pageLambdaGroups = await getPageLambdaGroups({
|
||||
entryPath: requiredServerFilesManifest.appDir || entryPath,
|
||||
entryPath: projectDir,
|
||||
config,
|
||||
pages: nonApiPages,
|
||||
prerenderRoutes,
|
||||
@@ -718,7 +759,7 @@ export async function serverBuild({
|
||||
});
|
||||
|
||||
const streamingPageLambdaGroups = await getPageLambdaGroups({
|
||||
entryPath: requiredServerFilesManifest.appDir || entryPath,
|
||||
entryPath: projectDir,
|
||||
config,
|
||||
pages: streamingPages,
|
||||
prerenderRoutes,
|
||||
@@ -739,7 +780,7 @@ export async function serverBuild({
|
||||
}
|
||||
|
||||
const apiLambdaGroups = await getPageLambdaGroups({
|
||||
entryPath: requiredServerFilesManifest.appDir || entryPath,
|
||||
entryPath: projectDir,
|
||||
config,
|
||||
pages: apiPages,
|
||||
prerenderRoutes,
|
||||
@@ -873,10 +914,7 @@ export async function serverBuild({
|
||||
},
|
||||
layers: [group.pseudoLayer, groupPageFiles],
|
||||
handler: path.join(
|
||||
path.relative(
|
||||
baseDir,
|
||||
requiredServerFilesManifest.appDir || entryPath
|
||||
),
|
||||
path.relative(baseDir, projectDir),
|
||||
'___next_launcher.cjs'
|
||||
),
|
||||
operationType,
|
||||
@@ -1139,15 +1177,9 @@ 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];
|
||||
const ogRoute = inversedAppPathManifest[route];
|
||||
|
||||
if (ogRoute.endsWith('/route')) {
|
||||
continue;
|
||||
@@ -1528,7 +1560,7 @@ export async function serverBuild({
|
||||
{
|
||||
src: `^${path.posix.join('/', entryDirectory)}/?(?:${i18n.locales
|
||||
.map(locale => escapeStringRegexp(locale))
|
||||
.join('|')})/(.*)`,
|
||||
.join('|')})/?(.*)`,
|
||||
dest: `${path.posix.join('/', entryDirectory, '/')}$1`,
|
||||
check: true,
|
||||
},
|
||||
|
||||
@@ -811,6 +811,7 @@ export async function createLambdaFromPseudoLayers({
|
||||
|
||||
export type NextRequiredServerFilesManifest = {
|
||||
appDir?: string;
|
||||
relativeAppDir?: string;
|
||||
files: string[];
|
||||
ignore: string[];
|
||||
config: Record<string, any>;
|
||||
@@ -954,6 +955,7 @@ export async function getRequiredServerFilesManifest(
|
||||
ignore: [],
|
||||
config: {},
|
||||
appDir: manifestData.appDir,
|
||||
relativeAppDir: manifestData.relativeAppDir,
|
||||
};
|
||||
|
||||
switch (manifestData.version) {
|
||||
|
||||
1
packages/next/test/fixtures/00-app-dir-i18n/.gitignore
vendored
Normal file
1
packages/next/test/fixtures/00-app-dir-i18n/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.vercel
|
||||
3
packages/next/test/fixtures/00-app-dir-i18n/app/enter/page.js
vendored
Normal file
3
packages/next/test/fixtures/00-app-dir-i18n/app/enter/page.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Page() {
|
||||
return <p>My Enter Page</p>;
|
||||
}
|
||||
10
packages/next/test/fixtures/00-app-dir-i18n/app/layout.js
vendored
Normal file
10
packages/next/test/fixtures/00-app-dir-i18n/app/layout.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
export default function Root({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>My Title</title>
|
||||
</head>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
3
packages/next/test/fixtures/00-app-dir-i18n/app/other/page.js
vendored
Normal file
3
packages/next/test/fixtures/00-app-dir-i18n/app/other/page.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Page() {
|
||||
return <p>My Other Page</p>;
|
||||
}
|
||||
3
packages/next/test/fixtures/00-app-dir-i18n/app/page.js
vendored
Normal file
3
packages/next/test/fixtures/00-app-dir-i18n/app/page.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Page() {
|
||||
return <p>My Index Page</p>;
|
||||
}
|
||||
12
packages/next/test/fixtures/00-app-dir-i18n/index.test.js
vendored
Normal file
12
packages/next/test/fixtures/00-app-dir-i18n/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);
|
||||
});
|
||||
});
|
||||
9
packages/next/test/fixtures/00-app-dir-i18n/next.config.js
vendored
Normal file
9
packages/next/test/fixtures/00-app-dir-i18n/next.config.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
experimental: {
|
||||
appDir: true,
|
||||
},
|
||||
i18n: {
|
||||
locales: ['en'],
|
||||
defaultLocale: 'en',
|
||||
},
|
||||
};
|
||||
13
packages/next/test/fixtures/00-app-dir-i18n/package.json
vendored
Normal file
13
packages/next/test/fixtures/00-app-dir-i18n/package.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "experimental",
|
||||
"react-dom": "experimental"
|
||||
}
|
||||
}
|
||||
34
packages/next/test/fixtures/00-app-dir-i18n/probes.json
vendored
Normal file
34
packages/next/test/fixtures/00-app-dir-i18n/probes.json
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"probes": [
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/",
|
||||
"status": 200,
|
||||
"mustContain": "My Index Page"
|
||||
},
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/en",
|
||||
"status": 200,
|
||||
"mustContain": "My Index Page"
|
||||
},
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/enter",
|
||||
"status": 200,
|
||||
"mustContain": "My Enter Page"
|
||||
},
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/other",
|
||||
"status": 200,
|
||||
"mustContain": "My Other Page"
|
||||
},
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/en/other",
|
||||
"status": 200,
|
||||
"mustContain": "My Other Page"
|
||||
}
|
||||
]
|
||||
}
|
||||
4
packages/next/test/fixtures/00-app-dir/app/api/hello-again/route.js
vendored
Normal file
4
packages/next/test/fixtures/00-app-dir/app/api/hello-again/route.js
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export const GET = req => {
|
||||
console.log(req.url);
|
||||
return new Response('hello world');
|
||||
};
|
||||
1
packages/next/test/fixtures/00-cached-build/.gitignore
vendored
Normal file
1
packages/next/test/fixtures/00-cached-build/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.vercel
|
||||
7
packages/next/test/fixtures/00-cached-build/apps/web/package.json
vendored
Normal file
7
packages/next/test/fixtures/00-cached-build/apps/web/package.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "web",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "next build && rm -rf .next/cache && node post-build.js"
|
||||
}
|
||||
}
|
||||
3
packages/next/test/fixtures/00-cached-build/apps/web/pages/index.js
vendored
Normal file
3
packages/next/test/fixtures/00-cached-build/apps/web/pages/index.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Page() {
|
||||
return <p>index page</p>;
|
||||
}
|
||||
12
packages/next/test/fixtures/00-cached-build/apps/web/pages/ssg.js
vendored
Normal file
12
packages/next/test/fixtures/00-cached-build/apps/web/pages/ssg.js
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
export default function Page() {
|
||||
return <p>ssg page</p>;
|
||||
}
|
||||
|
||||
export function getStaticProps() {
|
||||
return {
|
||||
props: {
|
||||
now: Date.now(),
|
||||
},
|
||||
revalidate: 3,
|
||||
};
|
||||
}
|
||||
11
packages/next/test/fixtures/00-cached-build/apps/web/pages/ssp.js
vendored
Normal file
11
packages/next/test/fixtures/00-cached-build/apps/web/pages/ssp.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
export default function Page() {
|
||||
return <p>ssp page</p>;
|
||||
}
|
||||
|
||||
export function getServerSideProps() {
|
||||
return {
|
||||
props: {
|
||||
now: Date.now(),
|
||||
},
|
||||
};
|
||||
}
|
||||
19
packages/next/test/fixtures/00-cached-build/apps/web/post-build.js
vendored
Normal file
19
packages/next/test/fixtures/00-cached-build/apps/web/post-build.js
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const requiredFilesManifestPath = path.join(
|
||||
__dirname,
|
||||
'.next/required-server-files.json'
|
||||
);
|
||||
|
||||
const requiredFilesManifest = JSON.parse(
|
||||
fs.readFileSync(requiredFilesManifestPath, 'utf8')
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
requiredFilesManifestPath,
|
||||
JSON.stringify({
|
||||
...requiredFilesManifest,
|
||||
appDir: '/non-existent/apps/web',
|
||||
})
|
||||
);
|
||||
12
packages/next/test/fixtures/00-cached-build/index.test.js
vendored
Normal file
12
packages/next/test/fixtures/00-cached-build/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, { skipForceNew: true });
|
||||
Object.assign(ctx, info);
|
||||
});
|
||||
});
|
||||
15
packages/next/test/fixtures/00-cached-build/package.json
vendored
Normal file
15
packages/next/test/fixtures/00-cached-build/package.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"workspaces": [
|
||||
"apps/*"
|
||||
],
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"next": "13.2.2-canary.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"turbo": "1.8.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "turbo build --cache-dir=turbo-cache"
|
||||
}
|
||||
}
|
||||
1
packages/next/test/fixtures/00-cached-build/turbo-cache/3d4a11a80ae91055-meta.json
vendored
Normal file
1
packages/next/test/fixtures/00-cached-build/turbo-cache/3d4a11a80ae91055-meta.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{ "hash": "3d4a11a80ae91055", "duration": 5510 }
|
||||
BIN
packages/next/test/fixtures/00-cached-build/turbo-cache/3d4a11a80ae91055.tar.zst
vendored
Normal file
BIN
packages/next/test/fixtures/00-cached-build/turbo-cache/3d4a11a80ae91055.tar.zst
vendored
Normal file
Binary file not shown.
8
packages/next/test/fixtures/00-cached-build/turbo.json
vendored
Normal file
8
packages/next/test/fixtures/00-cached-build/turbo.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"pipeline": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": [".next/**"]
|
||||
}
|
||||
}
|
||||
}
|
||||
30
packages/next/test/fixtures/00-cached-build/vercel.json
vendored
Normal file
30
packages/next/test/fixtures/00-cached-build/vercel.json
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next",
|
||||
"config": {
|
||||
"rootDirectory": "apps/web",
|
||||
"buildCommand": "cd ../../ && yarn build",
|
||||
"installCommand": "yarn"
|
||||
}
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/",
|
||||
"status": 200,
|
||||
"mustContain": "index page"
|
||||
},
|
||||
{
|
||||
"path": "/ssg",
|
||||
"status": 200,
|
||||
"mustContain": "ssg page"
|
||||
},
|
||||
{
|
||||
"path": "/ssp",
|
||||
"status": 200,
|
||||
"mustContain": "ssp page"
|
||||
}
|
||||
]
|
||||
}
|
||||
227
packages/next/test/fixtures/00-cached-build/yarn.lock
vendored
Normal file
227
packages/next/test/fixtures/00-cached-build/yarn.lock
vendored
Normal file
@@ -0,0 +1,227 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@next/env@13.2.2-canary.0":
|
||||
version "13.2.2-canary.0"
|
||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-13.2.2-canary.0.tgz#0f843ef602ff25441f17f10d09a93942baa8232b"
|
||||
integrity sha512-EDy4UF4oXGmWgMq9w8P7Wg7JoYbrGY7EVnZcNoyMDU8o9KeiviYjK2IR3yFBulG0/1I2UbVu2wIM1xPYsQIonQ==
|
||||
|
||||
"@next/swc-android-arm-eabi@13.2.2-canary.0":
|
||||
version "13.2.2-canary.0"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.2.2-canary.0.tgz#62510cc5b11b677a63363788848efdc8aeda9cf8"
|
||||
integrity sha512-/si1jk3wtrarhdVPQloSubTJjLeuTpgT7V2R2w+acWzvBBsrs2ThhZodLz0fJRKcYKmeDZebhtYGUkxkcm48Tw==
|
||||
|
||||
"@next/swc-android-arm64@13.2.2-canary.0":
|
||||
version "13.2.2-canary.0"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.2.2-canary.0.tgz#967fe8a63d59bdd22f57056ef83005242b559c1a"
|
||||
integrity sha512-MKImVjggMFvPJju48fvz/KqjiqXaKoimGz3Vmc3c12WaSIEa5O1sevw0dQKPI6sv+1Mf5MuP7XlLQ9bWJo+72Q==
|
||||
|
||||
"@next/swc-darwin-arm64@13.2.2-canary.0":
|
||||
version "13.2.2-canary.0"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.2.2-canary.0.tgz#1250cf70dc129b765ae11bc596cd4d298a129afe"
|
||||
integrity sha512-Sj+hCut5c6K2lOIJpV9KDsDJNe1dVacAE8WWmvotoeu4ab1W0//axZOxksq0S3240oF9CJ8QPZo+q5lPV6Gn2Q==
|
||||
|
||||
"@next/swc-darwin-x64@13.2.2-canary.0":
|
||||
version "13.2.2-canary.0"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.2.2-canary.0.tgz#13dde75f037194d46799542111608679c73dc8dc"
|
||||
integrity sha512-/lv1J5ts5UhQ5V2V0PpIkQJw8kJywMgvPegZ/yf0fy9QOOdGCAw1dXtZsKUISrJmuzPcCZo0F56Zjbyb6lCLsw==
|
||||
|
||||
"@next/swc-freebsd-x64@13.2.2-canary.0":
|
||||
version "13.2.2-canary.0"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.2.2-canary.0.tgz#f9709e88c60207fb17244895cfe23a4a2d40b099"
|
||||
integrity sha512-ECQyYOYd1AKu/oTNeI5pWDXebgKTIWXCs84IN9rXsM7f+FOQmVU8V+gK6Oisw3jya68B1vmfMqLDGQClDDIMAg==
|
||||
|
||||
"@next/swc-linux-arm-gnueabihf@13.2.2-canary.0":
|
||||
version "13.2.2-canary.0"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.2.2-canary.0.tgz#bdcef78c71820ff64c1225796dc383c8d72fcaed"
|
||||
integrity sha512-/UQf0yoIwJJhgV8dpDmgWq/Q3/IxqFRsUHfBiy2M6kGVYwB9CnsjZzHwUKmg7sASdv+atW+oc4PIXXN6W9CCgg==
|
||||
|
||||
"@next/swc-linux-arm64-gnu@13.2.2-canary.0":
|
||||
version "13.2.2-canary.0"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.2.2-canary.0.tgz#e77b30092c22eb0af16a7a6c150d51fa84f0ceb0"
|
||||
integrity sha512-pv0m+4hnKyq0MwEVAvrJScoYme4GKoPieP0Tj32oRK6P1gafFK4uJxF6zMVPlO0D/tGI7EkoHZjv/7sU5pSYSA==
|
||||
|
||||
"@next/swc-linux-arm64-musl@13.2.2-canary.0":
|
||||
version "13.2.2-canary.0"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.2.2-canary.0.tgz#779514bb2d0e7feced48fac1da0bc344180c8511"
|
||||
integrity sha512-Bpc7nLJoP6dVPG9lOqCGqWkfiMo143YRx7+cvxSl1FVupkGP7Ntldvl3zS/m2CnU/5egQEjC2C5jEgQRY92Zhw==
|
||||
|
||||
"@next/swc-linux-x64-gnu@13.2.2-canary.0":
|
||||
version "13.2.2-canary.0"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.2.2-canary.0.tgz#d9022f44e0b243d32d39eeac683b1f189ff41487"
|
||||
integrity sha512-2RPr5AxawTUVaQGrkhWWpjVdExPiA8wEJL6E1itI2gNav8LU3FEsIr9juQURv47Xn2KE286fw8214D6+D0RExg==
|
||||
|
||||
"@next/swc-linux-x64-musl@13.2.2-canary.0":
|
||||
version "13.2.2-canary.0"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.2.2-canary.0.tgz#8868f650f11594862fa183e0a0dac66d9c72973b"
|
||||
integrity sha512-gKr5tJoJKGSlmDdpmoO1fe6oUeVMz2TluspKPM4ulc0rqyXjrRmQ9pd/oqEoypUKTUkqL49lt4hTL98MkhvXWg==
|
||||
|
||||
"@next/swc-win32-arm64-msvc@13.2.2-canary.0":
|
||||
version "13.2.2-canary.0"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.2.2-canary.0.tgz#29faaa4b760128c648f153964e94a8191bb0b2d7"
|
||||
integrity sha512-0N7+lcV8ycqzgJIye40+Vl0iUk/mu3919T2kfnt20WU7gP+rpRJkPfP44yxBdt7U7XYtnqldM2Ox969y+0qJVw==
|
||||
|
||||
"@next/swc-win32-ia32-msvc@13.2.2-canary.0":
|
||||
version "13.2.2-canary.0"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.2.2-canary.0.tgz#078350e676d1a146442dbc7b1f5c4e03cb1345a5"
|
||||
integrity sha512-vqdsKfJcfvf37P7YcJeIaPP5E7iSR7yTaHwgBpvY6Q0tDRxPv0gr4nRXElBs/V1VcxMaSx9PyyiQkKYB6UzEhg==
|
||||
|
||||
"@next/swc-win32-x64-msvc@13.2.2-canary.0":
|
||||
version "13.2.2-canary.0"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.2.2-canary.0.tgz#7ca59776b5440e6de3aee341d571a8a4f7cbb40a"
|
||||
integrity sha512-Hf1XQaP/hpi9ddS128u8+npotRzX5EOt9y4nxzHRKn6BmjgAKY7CEBsFQMsjVHWqit2Jt3jXspgaCVIFOCE9Ng==
|
||||
|
||||
"@swc/helpers@0.4.14":
|
||||
version "0.4.14"
|
||||
resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74"
|
||||
integrity sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
caniuse-lite@^1.0.30001406:
|
||||
version "1.0.30001457"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz#6af34bb5d720074e2099432aa522c21555a18301"
|
||||
integrity sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==
|
||||
|
||||
client-only@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
|
||||
integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||
|
||||
loose-envify@^1.1.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||
dependencies:
|
||||
js-tokens "^3.0.0 || ^4.0.0"
|
||||
|
||||
nanoid@^3.3.4:
|
||||
version "3.3.4"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
|
||||
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
|
||||
|
||||
next@13.2.2-canary.0:
|
||||
version "13.2.2-canary.0"
|
||||
resolved "https://registry.yarnpkg.com/next/-/next-13.2.2-canary.0.tgz#7b8c9e2391d947fb66636fe988af644f2e930db2"
|
||||
integrity sha512-sAzQCPI06df+TY7NI6txOkc7sOC7sErnOGF7vXA28GcQ0r4V09s4GDe8XTlPckxJ7o2Ky4OU1eN4NJ3vnDCcZQ==
|
||||
dependencies:
|
||||
"@next/env" "13.2.2-canary.0"
|
||||
"@swc/helpers" "0.4.14"
|
||||
caniuse-lite "^1.0.30001406"
|
||||
postcss "8.4.14"
|
||||
styled-jsx "5.1.1"
|
||||
optionalDependencies:
|
||||
"@next/swc-android-arm-eabi" "13.2.2-canary.0"
|
||||
"@next/swc-android-arm64" "13.2.2-canary.0"
|
||||
"@next/swc-darwin-arm64" "13.2.2-canary.0"
|
||||
"@next/swc-darwin-x64" "13.2.2-canary.0"
|
||||
"@next/swc-freebsd-x64" "13.2.2-canary.0"
|
||||
"@next/swc-linux-arm-gnueabihf" "13.2.2-canary.0"
|
||||
"@next/swc-linux-arm64-gnu" "13.2.2-canary.0"
|
||||
"@next/swc-linux-arm64-musl" "13.2.2-canary.0"
|
||||
"@next/swc-linux-x64-gnu" "13.2.2-canary.0"
|
||||
"@next/swc-linux-x64-musl" "13.2.2-canary.0"
|
||||
"@next/swc-win32-arm64-msvc" "13.2.2-canary.0"
|
||||
"@next/swc-win32-ia32-msvc" "13.2.2-canary.0"
|
||||
"@next/swc-win32-x64-msvc" "13.2.2-canary.0"
|
||||
|
||||
picocolors@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
|
||||
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
|
||||
|
||||
postcss@8.4.14:
|
||||
version "8.4.14"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf"
|
||||
integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==
|
||||
dependencies:
|
||||
nanoid "^3.3.4"
|
||||
picocolors "^1.0.0"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
react-dom@18.2.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
|
||||
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
scheduler "^0.23.0"
|
||||
|
||||
react@18.2.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
||||
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
|
||||
scheduler@^0.23.0:
|
||||
version "0.23.0"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
|
||||
integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
|
||||
source-map-js@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
||||
|
||||
styled-jsx@5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f"
|
||||
integrity sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==
|
||||
dependencies:
|
||||
client-only "0.0.1"
|
||||
|
||||
tslib@^2.4.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
|
||||
integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
|
||||
|
||||
turbo-darwin-64@1.8.2:
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-1.8.2.tgz#14c52e97d128c63fd3c7b2f15963123f02b9aa0e"
|
||||
integrity sha512-j77U0uOeppENexFsIvvzExADSqMBEeCHnm+6LSNQfaajHSrbUVSTsuD6ZMYHamT6bslc+ZZm21jdecWkwZFBbw==
|
||||
|
||||
turbo-darwin-arm64@1.8.2:
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-1.8.2.tgz#ae5efdb89cbdd667feacd3fe8e9c8110691a4d95"
|
||||
integrity sha512-1NoAvjlwt2wycsAFJouauy9epn9DptSMy6BoGqxJVc4jiibsLepp9qYc4f1/ln0zjd3FR1IvhGOiBfdpqMN7hg==
|
||||
|
||||
turbo-linux-64@1.8.2:
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-1.8.2.tgz#02442a48104db83c1e53409c85744fdeacbded56"
|
||||
integrity sha512-TcT3CRYnBYA46kLGGbGC2jDyCEAvMgVpUdpIZGTmod48EKpZaEfVgTkpa4GJde8W68yRFogPZjPVL3yJHFpXSA==
|
||||
|
||||
turbo-linux-arm64@1.8.2:
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-1.8.2.tgz#890ad0691671cb252e756dcd56295895f61d369a"
|
||||
integrity sha512-Mb9+KBy4YJzPMZ6WGoMzMVZ6EtueCSvOvgmNpVFgkwbtabfBuaBOvN+irtg4RRSWvJQTDTziLABieocEEXZImQ==
|
||||
|
||||
turbo-windows-64@1.8.2:
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-1.8.2.tgz#a57b902cdccdb69d1efa18772bee9e277e079583"
|
||||
integrity sha512-/+R5ikRrw2w2w38JtNPubGLIQHgUC70m783DI9aPgaM5c8P5D/Y0k6HgjuC/uXgiaz2h3R7p7YWlr+2/E0bqyA==
|
||||
|
||||
turbo-windows-arm64@1.8.2:
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-1.8.2.tgz#0f976a2c6b8a46447fc277d5a9f7d8615792bdde"
|
||||
integrity sha512-s07viz5nXSx4kyiksuPM4FGLRkoaGMaw0BpwFjdRQsl1p+WclUN1IPdokVPKOmFpu5pNCVYlG/raP/mXAEzDCg==
|
||||
|
||||
turbo@1.8.2:
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo/-/turbo-1.8.2.tgz#869e674a524cde4f449ae4458f4651818e2ffe00"
|
||||
integrity sha512-G/uJx6bZK5RwTWHsRN/MP0MvXFznmCaL3MQXdSf+OG/q0o8GE7+yivyyWEplWI1Asc8AEN909A/wlIkoz2FKTg==
|
||||
optionalDependencies:
|
||||
turbo-darwin-64 "1.8.2"
|
||||
turbo-darwin-arm64 "1.8.2"
|
||||
turbo-linux-64 "1.8.2"
|
||||
turbo-linux-arm64 "1.8.2"
|
||||
turbo-windows-64 "1.8.2"
|
||||
turbo-windows-arm64 "1.8.2"
|
||||
8
packages/next/test/fixtures/36-instrumentation-hook/index.test.js
vendored
Normal file
8
packages/next/test/fixtures/36-instrumentation-hook/index.test.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
const path = require('path');
|
||||
const { deployAndTest } = require('../../utils');
|
||||
|
||||
describe(`${__dirname.split(path.sep).pop()}`, () => {
|
||||
it('should deploy and pass probe checks', async () => {
|
||||
await deployAndTest(__dirname);
|
||||
});
|
||||
});
|
||||
3
packages/next/test/fixtures/36-instrumentation-hook/instrumentation.js
vendored
Normal file
3
packages/next/test/fixtures/36-instrumentation-hook/instrumentation.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export function register() {
|
||||
globalThis.isOdd = require('is-odd');
|
||||
}
|
||||
5
packages/next/test/fixtures/36-instrumentation-hook/next.config.js
vendored
Normal file
5
packages/next/test/fixtures/36-instrumentation-hook/next.config.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
experimental: {
|
||||
instrumentationHook: true,
|
||||
},
|
||||
};
|
||||
16
packages/next/test/fixtures/36-instrumentation-hook/package.json
vendored
Normal file
16
packages/next/test/fixtures/36-instrumentation-hook/package.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "instrumentation-hook",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "latest",
|
||||
"react-dom": "latest",
|
||||
"is-odd": "3.0.1"
|
||||
}
|
||||
}
|
||||
5
packages/next/test/fixtures/36-instrumentation-hook/pages/api/hello.js
vendored
Normal file
5
packages/next/test/fixtures/36-instrumentation-hook/pages/api/hello.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export default async (req, res) => {
|
||||
res.status(200).json({
|
||||
payload: `isOdd: ${globalThis.isOdd(3)}`,
|
||||
});
|
||||
};
|
||||
11
packages/next/test/fixtures/36-instrumentation-hook/pages/index.js
vendored
Normal file
11
packages/next/test/fixtures/36-instrumentation-hook/pages/index.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
export default function Home(props) {
|
||||
return `isOdd: ${props.isOdd}`;
|
||||
}
|
||||
|
||||
export async function getServerSideProps() {
|
||||
return {
|
||||
props: {
|
||||
isOdd: globalThis.isOdd(2),
|
||||
},
|
||||
};
|
||||
}
|
||||
14
packages/next/test/fixtures/36-instrumentation-hook/probes.json
vendored
Normal file
14
packages/next/test/fixtures/36-instrumentation-hook/probes.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"probes": [
|
||||
{
|
||||
"path": "/",
|
||||
"status": 200,
|
||||
"mustContain": "isOdd: false"
|
||||
},
|
||||
{
|
||||
"path": "/api/hello",
|
||||
"status": 200,
|
||||
"mustContain": "isOdd: true"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -26,12 +26,18 @@ if (parseInt(process.versions.node.split('.')[0], 10) >= 16) {
|
||||
}
|
||||
}
|
||||
|
||||
expect(lambdas.size).toBe(1);
|
||||
expect(lambdas.size).toBe(2);
|
||||
expect(buildResult.output['dashboard']).toBeDefined();
|
||||
expect(buildResult.output['dashboard/another']).toBeDefined();
|
||||
expect(buildResult.output['dashboard/changelog']).toBeDefined();
|
||||
expect(buildResult.output['dashboard/deployments/[id]']).toBeDefined();
|
||||
|
||||
expect(buildResult.output['api/hello-again']).toBeDefined();
|
||||
expect(buildResult.output['api/hello-again'].type).toBe('Lambda');
|
||||
expect(
|
||||
buildResult.output['api/hello-again'].experimentalResponseStreaming
|
||||
).toBe(true);
|
||||
|
||||
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();
|
||||
|
||||
7
packages/next/test/utils.ts
vendored
7
packages/next/test/utils.ts
vendored
@@ -71,8 +71,11 @@ export const createLoggerServer = async (): Promise<LoggerServer> => {
|
||||
|
||||
process.env.NEXT_TELEMETRY_DISABLED = '1';
|
||||
|
||||
export async function deployAndTest(fixtureDir) {
|
||||
const { deploymentId, deploymentUrl } = await testDeployment(fixtureDir);
|
||||
export async function deployAndTest(fixtureDir, opts) {
|
||||
const { deploymentId, deploymentUrl } = await testDeployment(
|
||||
fixtureDir,
|
||||
opts
|
||||
);
|
||||
|
||||
return {
|
||||
deploymentId,
|
||||
|
||||
@@ -435,7 +435,13 @@ function getStreamResponseCallback({ url, socket, cipher, resolve, reject }) {
|
||||
headers += `x-vercel-status-code: ${response.statusCode || 200}${CRLF}`;
|
||||
for (const [name, value] of getHeadersIterator(response.headers)) {
|
||||
if (!['connection', 'transfer-encoding'].includes(name)) {
|
||||
headers += `x-vercel-header-${name}: ${value}${CRLF}`;
|
||||
if (typeof value === 'string') {
|
||||
headers += `x-vercel-header-${name}: ${value}${CRLF}`;
|
||||
} else {
|
||||
for (const val of value) {
|
||||
headers += `x-vercel-header-${name}: ${val}${CRLF}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/node-bridge",
|
||||
"version": "3.1.11",
|
||||
"version": "3.1.14",
|
||||
"license": "MIT",
|
||||
"main": "./index.js",
|
||||
"repository": {
|
||||
|
||||
9
packages/node-bridge/turbo.json
Normal file
9
packages/node-bridge/turbo.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "https://turborepo.org/schema.json",
|
||||
"extends": ["//"],
|
||||
"pipeline": {
|
||||
"build": {
|
||||
"outputs": ["helpers.js", "source-map-support.js"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/node",
|
||||
"version": "2.9.7",
|
||||
"version": "2.9.10",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
||||
@@ -31,8 +31,8 @@
|
||||
"dependencies": {
|
||||
"@edge-runtime/vm": "2.0.0",
|
||||
"@types/node": "14.18.33",
|
||||
"@vercel/build-utils": "6.3.1",
|
||||
"@vercel/node-bridge": "3.1.11",
|
||||
"@vercel/build-utils": "6.3.2",
|
||||
"@vercel/node-bridge": "3.1.14",
|
||||
"@vercel/static-config": "2.0.13",
|
||||
"edge-runtime": "2.0.0",
|
||||
"esbuild": "0.14.47",
|
||||
|
||||
@@ -449,9 +449,19 @@ export const build: BuildV3 = async ({
|
||||
// Middleware is a catch-all for all paths unless a `matcher` property is defined
|
||||
const src = getRegExpFromMatchers(staticConfig?.matcher);
|
||||
|
||||
const middlewareRawSrc: string[] = [];
|
||||
if (staticConfig?.matcher) {
|
||||
if (Array.isArray(staticConfig.matcher)) {
|
||||
middlewareRawSrc.push(...staticConfig.matcher);
|
||||
} else {
|
||||
middlewareRawSrc.push(staticConfig.matcher as string);
|
||||
}
|
||||
}
|
||||
|
||||
routes = [
|
||||
{
|
||||
src,
|
||||
middlewareRawSrc,
|
||||
middlewarePath: outputPath,
|
||||
continue: true,
|
||||
override: true,
|
||||
|
||||
32
packages/node/test/dev.test.ts
vendored
32
packages/node/test/dev.test.ts
vendored
@@ -1,22 +1,29 @@
|
||||
import { forkDevServer, readMessage } from '../src/fork-dev-server';
|
||||
import { resolve } from 'path';
|
||||
import { resolve, extname } from 'path';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
jest.setTimeout(10 * 1000);
|
||||
|
||||
test('runs a mjs endpoint', async () => {
|
||||
const child = forkDevServer({
|
||||
function testForkDevServer(entrypoint: string) {
|
||||
const ext = extname(entrypoint);
|
||||
const isTypeScript = ext === '.ts';
|
||||
const isEsm = ext === '.mjs';
|
||||
return forkDevServer({
|
||||
maybeTranspile: true,
|
||||
config: {},
|
||||
isEsm: true,
|
||||
isTypeScript: false,
|
||||
isEsm,
|
||||
isTypeScript,
|
||||
meta: {},
|
||||
require_: require,
|
||||
tsConfig: undefined,
|
||||
workPath: resolve(__dirname, './dev-fixtures'),
|
||||
entrypoint: './esm-module.mjs',
|
||||
entrypoint,
|
||||
devServerPath: resolve(__dirname, '../dist/dev-server.js'),
|
||||
});
|
||||
}
|
||||
|
||||
test('runs a mjs endpoint', async () => {
|
||||
const child = testForkDevServer('./esm-module.mjs');
|
||||
|
||||
try {
|
||||
const result = await readMessage(child);
|
||||
@@ -44,18 +51,7 @@ test('runs a mjs endpoint', async () => {
|
||||
});
|
||||
|
||||
test('runs a esm typescript endpoint', async () => {
|
||||
const child = forkDevServer({
|
||||
maybeTranspile: true,
|
||||
config: {},
|
||||
isEsm: true,
|
||||
isTypeScript: true,
|
||||
meta: {},
|
||||
require_: require,
|
||||
tsConfig: undefined,
|
||||
workPath: resolve(__dirname, './dev-fixtures'),
|
||||
entrypoint: './esm-module.ts',
|
||||
devServerPath: resolve(__dirname, '../dist/dev-server.js'),
|
||||
});
|
||||
const child = testForkDevServer('./esm-module.ts');
|
||||
|
||||
try {
|
||||
const result = await readMessage(child);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/python",
|
||||
"version": "3.1.50",
|
||||
"version": "3.1.51",
|
||||
"main": "./dist/index.js",
|
||||
"license": "MIT",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
|
||||
@@ -23,7 +23,7 @@
|
||||
"@types/execa": "^0.9.0",
|
||||
"@types/jest": "27.4.1",
|
||||
"@types/node": "14.18.33",
|
||||
"@vercel/build-utils": "6.3.1",
|
||||
"@vercel/build-utils": "6.3.2",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"execa": "^1.0.0",
|
||||
"typescript": "4.3.4"
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
{
|
||||
"path": "/cookie_wsgi.py",
|
||||
"responseHeaders": {
|
||||
"set-cookie": ["one=first", "two=second"]
|
||||
"set-cookie": ["one=first; Path=/", "two=second; Path=/"]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/redwood",
|
||||
"version": "1.1.6",
|
||||
"version": "1.1.7",
|
||||
"main": "./dist/index.js",
|
||||
"license": "MIT",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
@@ -20,14 +20,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/nft": "0.22.5",
|
||||
"@vercel/routing-utils": "2.1.9",
|
||||
"@vercel/routing-utils": "2.1.10",
|
||||
"semver": "6.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/aws-lambda": "8.10.19",
|
||||
"@types/node": "14.18.33",
|
||||
"@types/semver": "6.0.0",
|
||||
"@vercel/build-utils": "6.3.1",
|
||||
"@vercel/build-utils": "6.3.2",
|
||||
"execa": "3.2.0",
|
||||
"fs-extra": "11.1.0",
|
||||
"typescript": "4.3.4"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/remix",
|
||||
"version": "1.4.0",
|
||||
"version": "1.6.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
@@ -18,10 +18,11 @@
|
||||
"files": [
|
||||
"dist",
|
||||
"server-node.mjs",
|
||||
"server-edge.mjs"
|
||||
"server-edge.mjs",
|
||||
"vercel-edge-entrypoint.js"
|
||||
],
|
||||
"dependencies": {
|
||||
"@remix-run/dev": "1.12.0",
|
||||
"@remix-run/dev": "npm:@vercel/remix-run-dev@1.14.0",
|
||||
"@vercel/nft": "0.22.5",
|
||||
"@vercel/static-config": "2.0.13",
|
||||
"path-to-regexp": "6.2.1",
|
||||
@@ -30,7 +31,7 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "27.5.1",
|
||||
"@types/node": "14.18.33",
|
||||
"@vercel/build-utils": "6.3.1",
|
||||
"@vercel/build-utils": "6.3.2",
|
||||
"typescript": "4.9.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { createRequestHandler } from '@remix-run/server-runtime';
|
||||
import build from './index.js';
|
||||
import build from '@remix-run/dev/server-build';
|
||||
export default createRequestHandler(build);
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
|
||||
installGlobals();
|
||||
|
||||
import build from './index.js';
|
||||
import build from '@remix-run/dev/server-build';
|
||||
|
||||
const handleRequest = createRemixRequestHandler(build, process.env.NODE_ENV);
|
||||
|
||||
@@ -54,11 +54,13 @@ function createRemixRequest(req, res) {
|
||||
}
|
||||
|
||||
async function sendRemixResponse(res, nodeResponse) {
|
||||
res.statusCode = nodeResponse.status;
|
||||
res.statusMessage = nodeResponse.statusText;
|
||||
for (const [name, value] of nodeResponse.headers.entries()) {
|
||||
res.setHeader(name, value);
|
||||
}
|
||||
let multiValueHeaders = nodeResponse.headers.raw();
|
||||
res.writeHead(
|
||||
nodeResponse.status,
|
||||
nodeResponse.statusText,
|
||||
multiValueHeaders
|
||||
);
|
||||
|
||||
if (nodeResponse.body) {
|
||||
await writeReadableStreamToWritable(nodeResponse.body, res);
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
debug,
|
||||
download,
|
||||
execCommand,
|
||||
FileBlob,
|
||||
FileFsRef,
|
||||
getEnvForPackageManager,
|
||||
getNodeVersion,
|
||||
@@ -18,7 +19,7 @@ import {
|
||||
scanParentDirs,
|
||||
walkParentDirs,
|
||||
} from '@vercel/build-utils';
|
||||
import { getConfig, BaseFunctionConfig } from '@vercel/static-config';
|
||||
import { getConfig } from '@vercel/static-config';
|
||||
import { nodeFileTrace } from '@vercel/nft';
|
||||
import { readConfig } from '@remix-run/dev/dist/config';
|
||||
import type {
|
||||
@@ -28,17 +29,36 @@ import type {
|
||||
PackageJson,
|
||||
BuildResultV2Typical,
|
||||
} from '@vercel/build-utils';
|
||||
import type { BaseFunctionConfig } from '@vercel/static-config';
|
||||
import type { AppConfig, RemixConfig } from '@remix-run/dev/dist/config';
|
||||
import type { ConfigRoute } from '@remix-run/dev/dist/config/routes';
|
||||
import {
|
||||
calculateRouteConfigHash,
|
||||
findConfig,
|
||||
getPathFromRoute,
|
||||
getRegExpFromPath,
|
||||
getRouteIterator,
|
||||
getResolvedRouteConfig,
|
||||
isLayoutRoute,
|
||||
ResolvedRouteConfig,
|
||||
ResolvedNodeRouteConfig,
|
||||
ResolvedEdgeRouteConfig,
|
||||
} from './utils';
|
||||
|
||||
const _require: typeof require = eval('require');
|
||||
|
||||
const REMIX_RUN_DEV_PATH = dirname(
|
||||
_require.resolve('@remix-run/dev/package.json')
|
||||
);
|
||||
|
||||
const edgeServerSrcPromise = fs.readFile(
|
||||
join(__dirname, '../server-edge.mjs'),
|
||||
'utf-8'
|
||||
);
|
||||
const nodeServerSrcPromise = fs.readFile(
|
||||
join(__dirname, '../server-node.mjs'),
|
||||
'utf-8'
|
||||
);
|
||||
|
||||
export const build: BuildV2 = async ({
|
||||
entrypoint,
|
||||
files,
|
||||
@@ -91,54 +111,131 @@ export const build: BuildV2 = async ({
|
||||
await runNpmInstall(entrypointFsDirname, [], spawnOpts, meta, nodeVersion);
|
||||
}
|
||||
|
||||
// Make `remix build` output production mode
|
||||
spawnOpts.env.NODE_ENV = 'production';
|
||||
// Make our version of `remix` CLI available to the project's build
|
||||
// command by creating a symlink to the copy in our node modules,
|
||||
// so that `serverBundles` works: https://github.com/remix-run/remix/pull/5479
|
||||
const remixRunDevPath = await ensureResolvable(
|
||||
entrypointFsDirname,
|
||||
repoRootPath,
|
||||
'@remix-run/dev'
|
||||
);
|
||||
|
||||
let remixConfig = await readConfig(entrypointFsDirname);
|
||||
const { serverEntryPoint } = remixConfig;
|
||||
const remixVersion = JSON.parse(
|
||||
await fs.readFile(join(remixRunDevPath, 'package.json'), 'utf8')
|
||||
).version;
|
||||
|
||||
// We need to patch the `remix.config.js` file to force some values necessary
|
||||
// for a build that works on either Node.js or the Edge runtime
|
||||
let remixConfigWrapped = false;
|
||||
const remixConfigPath = findConfig(entrypointFsDirname, 'remix.config');
|
||||
const renamedRemixConfigPath = remixConfigPath
|
||||
? `${remixConfigPath}.original${extname(remixConfigPath)}`
|
||||
: undefined;
|
||||
if (remixConfigPath && renamedRemixConfigPath) {
|
||||
await fs.rename(remixConfigPath, renamedRemixConfigPath);
|
||||
|
||||
// Figure out if the `remix.config` file is using ESM syntax
|
||||
let isESM = false;
|
||||
try {
|
||||
_require(renamedRemixConfigPath);
|
||||
} catch (err: any) {
|
||||
isESM = err.code === 'ERR_REQUIRE_ESM';
|
||||
}
|
||||
const backupRemixRunDevPath = `${remixRunDevPath}.__vercel_backup`;
|
||||
await fs.rename(remixRunDevPath, backupRemixRunDevPath);
|
||||
await fs.symlink(REMIX_RUN_DEV_PATH, remixRunDevPath);
|
||||
|
||||
let patchedConfig: string;
|
||||
if (isESM) {
|
||||
patchedConfig = `import config from './${basename(
|
||||
renamedRemixConfigPath
|
||||
)}';
|
||||
config.serverBuildTarget = undefined;
|
||||
config.serverModuleFormat = 'cjs';
|
||||
config.serverPlatform = 'node';
|
||||
config.serverBuildPath = 'build/index.js';
|
||||
export default config;`;
|
||||
} else {
|
||||
patchedConfig = `const config = require('./${basename(
|
||||
renamedRemixConfigPath
|
||||
)}');
|
||||
config.serverBuildTarget = undefined;
|
||||
config.serverModuleFormat = 'cjs';
|
||||
config.serverPlatform = 'node';
|
||||
config.serverBuildPath = 'build/index.js';
|
||||
module.exports = config;`;
|
||||
}
|
||||
await fs.writeFile(remixConfigPath, patchedConfig);
|
||||
}
|
||||
// These get populated inside the try/catch below
|
||||
let serverBundles: AppConfig['serverBundles'];
|
||||
let remixConfig: RemixConfig;
|
||||
let remixRoutes: ConfigRoute[];
|
||||
const serverBundlesMap = new Map<string, ConfigRoute[]>();
|
||||
const resolvedConfigsMap = new Map<ConfigRoute, ResolvedRouteConfig>();
|
||||
|
||||
// Run "Build Command"
|
||||
try {
|
||||
// Make `remix build` output production mode
|
||||
spawnOpts.env.NODE_ENV = 'production';
|
||||
|
||||
remixConfig = await chdirAndReadConfig(entrypointFsDirname);
|
||||
remixRoutes = Object.values(remixConfig.routes);
|
||||
|
||||
// Read the `export const config` (if any) for each route
|
||||
const project = new Project();
|
||||
const staticConfigsMap = new Map<ConfigRoute, BaseFunctionConfig | null>();
|
||||
for (const route of remixRoutes) {
|
||||
const routePath = join(remixConfig.appDirectory, route.file);
|
||||
const staticConfig = getConfig(project, routePath);
|
||||
staticConfigsMap.set(route, staticConfig);
|
||||
}
|
||||
|
||||
for (const route of remixRoutes) {
|
||||
const config = getResolvedRouteConfig(
|
||||
route,
|
||||
remixConfig.routes,
|
||||
staticConfigsMap
|
||||
);
|
||||
resolvedConfigsMap.set(route, config);
|
||||
}
|
||||
|
||||
// Figure out which routes belong to which server bundles
|
||||
// based on having common static config properties
|
||||
for (const route of remixRoutes) {
|
||||
if (isLayoutRoute(route.id, remixRoutes)) continue;
|
||||
|
||||
const config = resolvedConfigsMap.get(route);
|
||||
if (!config) {
|
||||
throw new Error(`Expected resolved config for "${route.id}"`);
|
||||
}
|
||||
const hash = calculateRouteConfigHash(config);
|
||||
|
||||
let routesForHash = serverBundlesMap.get(hash);
|
||||
if (!Array.isArray(routesForHash)) {
|
||||
routesForHash = [];
|
||||
serverBundlesMap.set(hash, routesForHash);
|
||||
}
|
||||
|
||||
routesForHash.push(route);
|
||||
}
|
||||
|
||||
serverBundles = Array.from(serverBundlesMap.entries()).map(
|
||||
([hash, routes]) => {
|
||||
const runtime = resolvedConfigsMap.get(routes[0])?.runtime ?? 'nodejs';
|
||||
return {
|
||||
serverBuildPath: `build/build-${runtime}-${hash}.js`,
|
||||
routes: routes.map(r => r.id),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// We need to patch the `remix.config.js` file to force some values necessary
|
||||
// for a build that works on either Node.js or the Edge runtime
|
||||
if (remixConfigPath && renamedRemixConfigPath) {
|
||||
await fs.rename(remixConfigPath, renamedRemixConfigPath);
|
||||
|
||||
// Figure out if the `remix.config` file is using ESM syntax
|
||||
let isESM = false;
|
||||
try {
|
||||
_require(renamedRemixConfigPath);
|
||||
} catch (err: any) {
|
||||
isESM = err.code === 'ERR_REQUIRE_ESM';
|
||||
}
|
||||
|
||||
let patchedConfig: string;
|
||||
if (isESM) {
|
||||
patchedConfig = `import config from './${basename(
|
||||
renamedRemixConfigPath
|
||||
)}';
|
||||
config.serverBuildTarget = undefined;
|
||||
config.serverModuleFormat = 'cjs';
|
||||
config.serverPlatform = 'node';
|
||||
config.serverBuildPath = undefined;
|
||||
config.serverBundles = ${JSON.stringify(serverBundles)};
|
||||
export default config;`;
|
||||
} else {
|
||||
patchedConfig = `const config = require('./${basename(
|
||||
renamedRemixConfigPath
|
||||
)}');
|
||||
config.serverBuildTarget = undefined;
|
||||
config.serverModuleFormat = 'cjs';
|
||||
config.serverPlatform = 'node';
|
||||
config.serverBuildPath = undefined;
|
||||
config.serverBundles = ${JSON.stringify(serverBundles)};
|
||||
module.exports = config;`;
|
||||
}
|
||||
await fs.writeFile(remixConfigPath, patchedConfig);
|
||||
remixConfigWrapped = true;
|
||||
}
|
||||
|
||||
// Run "Build Command"
|
||||
if (buildCommand) {
|
||||
debug(`Executing build command "${buildCommand}"`);
|
||||
await execCommand(buildCommand, {
|
||||
@@ -166,30 +263,15 @@ module.exports = config;`;
|
||||
});
|
||||
}
|
||||
}
|
||||
remixConfig = await readConfig(entrypointFsDirname);
|
||||
} finally {
|
||||
// Clean up our patched `remix.config.js` to be polite
|
||||
if (remixConfigPath && renamedRemixConfigPath) {
|
||||
if (remixConfigWrapped && remixConfigPath && renamedRemixConfigPath) {
|
||||
await fs.rename(renamedRemixConfigPath, remixConfigPath);
|
||||
}
|
||||
}
|
||||
|
||||
const { serverBuildPath } = remixConfig;
|
||||
const remixRoutes = Object.values(remixConfig.routes);
|
||||
|
||||
// Figure out which pages should be edge functions
|
||||
let hasEdgeRoute = false;
|
||||
const staticConfigsMap = new Map<ConfigRoute, BaseFunctionConfig>();
|
||||
const project = new Project();
|
||||
for (const route of remixRoutes) {
|
||||
const routePath = join(remixConfig.appDirectory, route.file);
|
||||
const staticConfig = getConfig(project, routePath);
|
||||
if (staticConfig) {
|
||||
staticConfigsMap.set(route, staticConfig);
|
||||
}
|
||||
if (staticConfig?.runtime && isEdgeRuntime(staticConfig.runtime)) {
|
||||
hasEdgeRoute = true;
|
||||
}
|
||||
// Remove `@remix-run/dev` symlink
|
||||
await fs.unlink(remixRunDevPath);
|
||||
await fs.rename(backupRemixRunDevPath, remixRunDevPath);
|
||||
}
|
||||
|
||||
// This needs to happen before we run NFT to create the Node/Edge functions
|
||||
@@ -202,23 +284,35 @@ module.exports = config;`;
|
||||
ensureResolvable(entrypointFsDirname, repoRootPath, '@remix-run/node'),
|
||||
]);
|
||||
|
||||
const [staticFiles, nodeFunction, edgeFunction] = await Promise.all([
|
||||
const [staticFiles, ...functions] = await Promise.all([
|
||||
glob('**', join(entrypointFsDirname, 'public')),
|
||||
createRenderNodeFunction(
|
||||
entrypointFsDirname,
|
||||
repoRootPath,
|
||||
serverBuildPath,
|
||||
serverEntryPoint,
|
||||
nodeVersion
|
||||
),
|
||||
hasEdgeRoute
|
||||
? createRenderEdgeFunction(
|
||||
...serverBundles.map(bundle => {
|
||||
const firstRoute = remixConfig.routes[bundle.routes[0]];
|
||||
const config = resolvedConfigsMap.get(firstRoute) ?? {
|
||||
runtime: 'nodejs',
|
||||
};
|
||||
|
||||
if (config.runtime === 'edge') {
|
||||
return createRenderEdgeFunction(
|
||||
entrypointFsDirname,
|
||||
repoRootPath,
|
||||
serverBuildPath,
|
||||
serverEntryPoint
|
||||
)
|
||||
: undefined,
|
||||
join(entrypointFsDirname, bundle.serverBuildPath),
|
||||
remixConfig.serverEntryPoint,
|
||||
remixVersion,
|
||||
config
|
||||
);
|
||||
}
|
||||
|
||||
return createRenderNodeFunction(
|
||||
nodeVersion,
|
||||
entrypointFsDirname,
|
||||
repoRootPath,
|
||||
join(entrypointFsDirname, bundle.serverBuildPath),
|
||||
remixConfig.serverEntryPoint,
|
||||
remixVersion,
|
||||
config
|
||||
);
|
||||
}),
|
||||
]);
|
||||
|
||||
const output: BuildResultV2Typical['output'] = staticFiles;
|
||||
@@ -237,32 +331,37 @@ module.exports = config;`;
|
||||
// Layout routes don't get a function / route added
|
||||
if (isLayoutRoute(route.id, remixRoutes)) continue;
|
||||
|
||||
const path = getPathFromRoute(route, remixConfig.routes);
|
||||
const { path, rePath } = getPathFromRoute(route, remixConfig.routes);
|
||||
|
||||
let isEdge = false;
|
||||
for (const currentRoute of getRouteIterator(route, remixConfig.routes)) {
|
||||
const staticConfig = staticConfigsMap.get(currentRoute);
|
||||
if (staticConfig?.runtime) {
|
||||
isEdge = isEdgeRuntime(staticConfig.runtime);
|
||||
break;
|
||||
}
|
||||
// If the route is a pathless layout route (at the root level)
|
||||
// and doesn't have any sub-routes, then a function should not be created.
|
||||
if (!path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fn =
|
||||
isEdge && edgeFunction
|
||||
const funcIndex = serverBundles.findIndex(bundle => {
|
||||
return bundle.routes.includes(route.id);
|
||||
});
|
||||
const func = functions[funcIndex];
|
||||
|
||||
if (!func) {
|
||||
throw new Error(`Could not determine server bundle for "${route.id}"`);
|
||||
}
|
||||
|
||||
output[path] =
|
||||
func instanceof EdgeFunction
|
||||
? // `EdgeFunction` currently requires the "name" property to be set.
|
||||
// Ideally this property will be removed, at which point we can
|
||||
// return the same `edgeFunction` instance instead of creating a
|
||||
// new one for each page.
|
||||
new EdgeFunction({
|
||||
...edgeFunction,
|
||||
...func,
|
||||
name: path,
|
||||
})
|
||||
: nodeFunction;
|
||||
output[path] = fn;
|
||||
: func;
|
||||
|
||||
// If this is a dynamic route then add a Vercel route
|
||||
const re = getRegExpFromPath(path);
|
||||
const re = getRegExpFromPath(rePath);
|
||||
if (re) {
|
||||
routes.push({
|
||||
src: re.source,
|
||||
@@ -272,18 +371,27 @@ module.exports = config;`;
|
||||
}
|
||||
|
||||
// Add a 404 path for not found pages to be server-side rendered by Remix.
|
||||
// Use the edge function if one was generated, otherwise use Node.js.
|
||||
// Use an edge function bundle if one was generated, otherwise use Node.js.
|
||||
if (!output['404']) {
|
||||
output['404'] = edgeFunction
|
||||
? new EdgeFunction({ ...edgeFunction, name: '404' })
|
||||
: nodeFunction;
|
||||
const edgeFunctionIndex = Array.from(serverBundlesMap.values()).findIndex(
|
||||
routes => {
|
||||
const runtime = resolvedConfigsMap.get(routes[0])?.runtime;
|
||||
return runtime === 'edge';
|
||||
}
|
||||
);
|
||||
const func =
|
||||
edgeFunctionIndex !== -1 ? functions[edgeFunctionIndex] : functions[0];
|
||||
output['404'] =
|
||||
func instanceof EdgeFunction
|
||||
? new EdgeFunction({ ...func, name: '404' })
|
||||
: func;
|
||||
}
|
||||
routes.push({
|
||||
src: '/(.*)',
|
||||
dest: '/404',
|
||||
});
|
||||
|
||||
return { routes, output };
|
||||
return { routes, output, framework: { version: remixVersion } };
|
||||
};
|
||||
|
||||
function hasScript(scriptName: string, pkg: PackageJson | null) {
|
||||
@@ -292,23 +400,33 @@ function hasScript(scriptName: string, pkg: PackageJson | null) {
|
||||
}
|
||||
|
||||
async function createRenderNodeFunction(
|
||||
nodeVersion: NodeVersion,
|
||||
entrypointDir: string,
|
||||
rootDir: string,
|
||||
serverBuildPath: string,
|
||||
serverEntryPoint: string | undefined,
|
||||
nodeVersion: NodeVersion
|
||||
remixVersion: string,
|
||||
config: ResolvedNodeRouteConfig
|
||||
): Promise<NodejsLambda> {
|
||||
const files: Files = {};
|
||||
|
||||
let handler = relative(rootDir, serverBuildPath);
|
||||
let handlerPath = join(rootDir, handler);
|
||||
if (!serverEntryPoint) {
|
||||
handler = join(dirname(handler), 'server-node.mjs');
|
||||
const baseServerBuildPath = basename(serverBuildPath, '.js');
|
||||
handler = join(dirname(handler), `server-${baseServerBuildPath}.mjs`);
|
||||
handlerPath = join(rootDir, handler);
|
||||
|
||||
// Copy the `server-node.mjs` file into the "build" directory
|
||||
const sourceHandlerPath = join(__dirname, '../server-node.mjs');
|
||||
await fs.copyFile(sourceHandlerPath, handlerPath);
|
||||
const nodeServerSrc = await nodeServerSrcPromise;
|
||||
await writeEntrypointFile(
|
||||
handlerPath,
|
||||
nodeServerSrc.replace(
|
||||
'@remix-run/dev/server-build',
|
||||
`./${baseServerBuildPath}.js`
|
||||
),
|
||||
rootDir
|
||||
);
|
||||
}
|
||||
|
||||
// Trace the handler with `@vercel/nft`
|
||||
@@ -333,6 +451,13 @@ async function createRenderNodeFunction(
|
||||
shouldAddSourcemapSupport: false,
|
||||
operationType: 'SSR',
|
||||
experimentalResponseStreaming: true,
|
||||
regions: config.regions,
|
||||
memory: config.memory,
|
||||
maxDuration: config.maxDuration,
|
||||
framework: {
|
||||
slug: 'remix',
|
||||
version: remixVersion,
|
||||
},
|
||||
});
|
||||
|
||||
return fn;
|
||||
@@ -342,21 +467,33 @@ async function createRenderEdgeFunction(
|
||||
entrypointDir: string,
|
||||
rootDir: string,
|
||||
serverBuildPath: string,
|
||||
serverEntryPoint: string | undefined
|
||||
serverEntryPoint: string | undefined,
|
||||
remixVersion: string,
|
||||
config: ResolvedEdgeRouteConfig
|
||||
): Promise<EdgeFunction> {
|
||||
const files: Files = {};
|
||||
|
||||
let handler = relative(rootDir, serverBuildPath);
|
||||
let handlerPath = join(rootDir, handler);
|
||||
if (!serverEntryPoint) {
|
||||
handler = join(dirname(handler), 'server-edge.mjs');
|
||||
const baseServerBuildPath = basename(serverBuildPath, '.js');
|
||||
handler = join(dirname(handler), `server-${baseServerBuildPath}.mjs`);
|
||||
handlerPath = join(rootDir, handler);
|
||||
|
||||
// Copy the `server-edge.mjs` file into the "build" directory
|
||||
const sourceHandlerPath = join(__dirname, '../server-edge.mjs');
|
||||
await fs.copyFile(sourceHandlerPath, handlerPath);
|
||||
const edgeServerSrc = await edgeServerSrcPromise;
|
||||
await writeEntrypointFile(
|
||||
handlerPath,
|
||||
edgeServerSrc.replace(
|
||||
'@remix-run/dev/server-build',
|
||||
`./${baseServerBuildPath}.js`
|
||||
),
|
||||
rootDir
|
||||
);
|
||||
}
|
||||
|
||||
let remixRunVercelPkgJson: string | undefined;
|
||||
|
||||
// Trace the handler with `@vercel/nft`
|
||||
const trace = await nodeFileTrace([handlerPath], {
|
||||
base: rootDir,
|
||||
@@ -375,6 +512,35 @@ async function createRenderEdgeFunction(
|
||||
if (basename(fsPath) === 'package.json') {
|
||||
// For Edge Functions, patch "main" field to prefer "browser" or "module"
|
||||
const pkgJson = JSON.parse(source.toString());
|
||||
|
||||
// When `@remix-run/vercel` is detected, we need to modify the `package.json`
|
||||
// to include the "browser" field so that the proper Edge entrypoint file
|
||||
// is used. This is a temporary stop gap until this PR is merged:
|
||||
// https://github.com/remix-run/remix/pull/5537
|
||||
if (pkgJson.name === '@remix-run/vercel') {
|
||||
pkgJson.browser = 'dist/edge.js';
|
||||
pkgJson.dependencies['@remix-run/server-runtime'] =
|
||||
pkgJson.dependencies['@remix-run/node'];
|
||||
|
||||
if (!remixRunVercelPkgJson) {
|
||||
remixRunVercelPkgJson = JSON.stringify(pkgJson, null, 2) + '\n';
|
||||
|
||||
// Copy in the edge entrypoint so that NFT can properly resolve it
|
||||
const vercelEdgeEntrypointPath = join(
|
||||
__dirname,
|
||||
'../vercel-edge-entrypoint.js'
|
||||
);
|
||||
const vercelEdgeEntrypointDest = join(
|
||||
dirname(fsPath),
|
||||
'dist/edge.js'
|
||||
);
|
||||
await fs.copyFile(
|
||||
vercelEdgeEntrypointPath,
|
||||
vercelEdgeEntrypointDest
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const prop of ['browser', 'module']) {
|
||||
const val = pkgJson[prop];
|
||||
if (typeof val === 'string') {
|
||||
@@ -395,7 +561,15 @@ async function createRenderEdgeFunction(
|
||||
}
|
||||
|
||||
for (const file of trace.fileList) {
|
||||
files[file] = await FileFsRef.fromFsPath({ fsPath: join(rootDir, file) });
|
||||
if (
|
||||
remixRunVercelPkgJson &&
|
||||
file.endsWith(`@remix-run${sep}vercel${sep}package.json`)
|
||||
) {
|
||||
// Use the modified `@remix-run/vercel` package.json which contains "browser" field
|
||||
files[file] = new FileBlob({ data: remixRunVercelPkgJson });
|
||||
} else {
|
||||
files[file] = await FileFsRef.fromFsPath({ fsPath: join(rootDir, file) });
|
||||
}
|
||||
}
|
||||
|
||||
const fn = new EdgeFunction({
|
||||
@@ -403,18 +577,30 @@ async function createRenderEdgeFunction(
|
||||
deploymentTarget: 'v8-worker',
|
||||
name: 'render',
|
||||
entrypoint: handler,
|
||||
regions: config.regions,
|
||||
framework: {
|
||||
slug: 'remix',
|
||||
version: remixVersion,
|
||||
},
|
||||
});
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
async function ensureResolvable(start: string, base: string, pkgName: string) {
|
||||
async function ensureResolvable(
|
||||
start: string,
|
||||
base: string,
|
||||
pkgName: string
|
||||
): Promise<string> {
|
||||
try {
|
||||
const resolvedPath = _require.resolve(pkgName, { paths: [start] });
|
||||
const resolvedPkgPath = _require.resolve(`${pkgName}/package.json`, {
|
||||
paths: [start],
|
||||
});
|
||||
const resolvedPath = dirname(resolvedPkgPath);
|
||||
if (!relative(base, resolvedPath).startsWith(`..${sep}`)) {
|
||||
// Resolved path is within the root of the project, so all good
|
||||
debug(`"${pkgName}" resolved to '${resolvedPath}'`);
|
||||
return;
|
||||
return resolvedPath;
|
||||
}
|
||||
} catch (err: any) {
|
||||
if (err.code !== 'MODULE_NOT_FOUND') {
|
||||
@@ -437,15 +623,8 @@ async function ensureResolvable(start: string, base: string, pkgName: string) {
|
||||
const match = packages.find(p => p.startsWith(prefix));
|
||||
if (match) {
|
||||
const pkgDir = join(pnpmDir, match, 'node_modules', pkgName);
|
||||
const symlinkPath = join(pnpmDir, '..', pkgName);
|
||||
const symlinkDir = dirname(symlinkPath);
|
||||
const symlinkTarget = relative(symlinkDir, pkgDir);
|
||||
await fs.mkdir(symlinkDir, { recursive: true });
|
||||
await fs.symlink(symlinkTarget, symlinkPath);
|
||||
console.warn(
|
||||
`WARN: Created symlink for "${pkgName}". To silence this warning, add "${pkgName}" to "dependencies" in your \`package.json\` file.`
|
||||
);
|
||||
return;
|
||||
await ensureSymlink(pkgDir, join(pnpmDir, '..'), pkgName);
|
||||
return pkgDir;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,15 +641,8 @@ async function ensureResolvable(start: string, base: string, pkgName: string) {
|
||||
const match = packages.find(p => p.startsWith(prefix));
|
||||
if (match) {
|
||||
const pkgDir = join(prefixDir, match, 'node_modules', pkgName);
|
||||
const symlinkPath = join(npmDir, '..', pkgName);
|
||||
const symlinkDir = dirname(symlinkPath);
|
||||
const symlinkTarget = relative(symlinkDir, pkgDir);
|
||||
await fs.mkdir(symlinkDir, { recursive: true });
|
||||
await fs.symlink(symlinkTarget, symlinkPath);
|
||||
console.warn(
|
||||
`WARN: Created symlink for "${pkgName}". To silence this warning, add "${pkgName}" to "dependencies" in your \`package.json\` file.`
|
||||
);
|
||||
return;
|
||||
await ensureSymlink(pkgDir, join(npmDir, '..'), pkgName);
|
||||
return pkgDir;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,6 +651,65 @@ async function ensureResolvable(start: string, base: string, pkgName: string) {
|
||||
);
|
||||
}
|
||||
|
||||
function isEdgeRuntime(runtime: string): boolean {
|
||||
return runtime === 'edge' || runtime === 'experimental-edge';
|
||||
async function ensureSymlink(
|
||||
target: string,
|
||||
nodeModulesDir: string,
|
||||
pkgName: string
|
||||
) {
|
||||
const symlinkPath = join(nodeModulesDir, pkgName);
|
||||
const symlinkDir = dirname(symlinkPath);
|
||||
const relativeTarget = relative(symlinkDir, target);
|
||||
|
||||
try {
|
||||
const existingTarget = await fs.readlink(symlinkPath);
|
||||
if (existingTarget === relativeTarget) {
|
||||
// Symlink is already the expected value, so do nothing
|
||||
return;
|
||||
} else {
|
||||
// If a symlink already exists then delete it if the target doesn't match
|
||||
await fs.unlink(symlinkPath);
|
||||
}
|
||||
} catch (err: any) {
|
||||
// Ignore when path does not exist or is not a symlink
|
||||
if (err.code !== 'ENOENT' && err.code !== 'EINVAL') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
await fs.symlink(relativeTarget, symlinkPath);
|
||||
console.warn(
|
||||
`WARN: Created symlink for "${pkgName}". To silence this warning, add "${pkgName}" to "dependencies" in your \`package.json\` file.`
|
||||
);
|
||||
}
|
||||
|
||||
async function chdirAndReadConfig(dir: string) {
|
||||
const originalCwd = process.cwd();
|
||||
let remixConfig: RemixConfig;
|
||||
try {
|
||||
process.chdir(dir);
|
||||
remixConfig = await readConfig(dir);
|
||||
} finally {
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
return remixConfig;
|
||||
}
|
||||
|
||||
async function writeEntrypointFile(
|
||||
path: string,
|
||||
data: string,
|
||||
rootDir: string
|
||||
) {
|
||||
try {
|
||||
await fs.writeFile(path, data);
|
||||
} catch (err: any) {
|
||||
if (err.code === 'ENOENT') {
|
||||
throw new Error(
|
||||
`The "${relative(
|
||||
rootDir,
|
||||
dirname(path)
|
||||
)}" directory does not exist. Please contact support@vercel.com.`
|
||||
);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,37 @@ import type {
|
||||
ConfigRoute,
|
||||
RouteManifest,
|
||||
} from '@remix-run/dev/dist/config/routes';
|
||||
import type { BaseFunctionConfig } from '@vercel/static-config';
|
||||
|
||||
export interface ResolvedNodeRouteConfig {
|
||||
runtime: 'nodejs';
|
||||
regions?: string[];
|
||||
maxDuration?: number;
|
||||
memory?: number;
|
||||
}
|
||||
export interface ResolvedEdgeRouteConfig {
|
||||
runtime: 'edge';
|
||||
regions?: BaseFunctionConfig['regions'];
|
||||
}
|
||||
|
||||
export type ResolvedRouteConfig =
|
||||
| ResolvedNodeRouteConfig
|
||||
| ResolvedEdgeRouteConfig;
|
||||
|
||||
export interface ResolvedRoutePaths {
|
||||
/**
|
||||
* The full URL path of the route, as will be shown
|
||||
* on the Functions tab in the deployment inspector.
|
||||
*/
|
||||
path: string;
|
||||
/**
|
||||
* The full URL path of the route, but with syntax that
|
||||
* is compatible with the `path-to-regexp` module.
|
||||
*/
|
||||
rePath: string;
|
||||
}
|
||||
|
||||
const SPLAT_PATH = '/:params+';
|
||||
|
||||
const configExts = ['.js', '.cjs', '.mjs'];
|
||||
|
||||
@@ -18,6 +49,60 @@ export function findConfig(dir: string, basename: string): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isEdgeRuntime(runtime: string): boolean {
|
||||
return runtime === 'edge' || runtime === 'experimental-edge';
|
||||
}
|
||||
|
||||
export function getResolvedRouteConfig(
|
||||
route: ConfigRoute,
|
||||
routes: RouteManifest,
|
||||
configs: Map<ConfigRoute, BaseFunctionConfig | null>
|
||||
): ResolvedRouteConfig {
|
||||
let runtime: ResolvedRouteConfig['runtime'] | undefined;
|
||||
let regions: ResolvedRouteConfig['regions'];
|
||||
let maxDuration: ResolvedNodeRouteConfig['maxDuration'];
|
||||
let memory: ResolvedNodeRouteConfig['memory'];
|
||||
|
||||
for (const currentRoute of getRouteIterator(route, routes)) {
|
||||
const staticConfig = configs.get(currentRoute);
|
||||
if (staticConfig) {
|
||||
if (typeof runtime === 'undefined' && staticConfig.runtime) {
|
||||
runtime = isEdgeRuntime(staticConfig.runtime) ? 'edge' : 'nodejs';
|
||||
}
|
||||
if (typeof regions === 'undefined') {
|
||||
regions = staticConfig.regions;
|
||||
}
|
||||
if (typeof maxDuration === 'undefined') {
|
||||
maxDuration = staticConfig.maxDuration;
|
||||
}
|
||||
if (typeof memory === 'undefined') {
|
||||
memory = staticConfig.memory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(regions)) {
|
||||
regions = Array.from(new Set(regions)).sort();
|
||||
}
|
||||
|
||||
if (runtime === 'edge') {
|
||||
return { runtime, regions };
|
||||
}
|
||||
|
||||
if (regions && !Array.isArray(regions)) {
|
||||
throw new Error(
|
||||
`"regions" for route "${route.id}" must be an array of strings`
|
||||
);
|
||||
}
|
||||
|
||||
return { runtime: 'nodejs', regions, maxDuration, memory };
|
||||
}
|
||||
|
||||
export function calculateRouteConfigHash(config: ResolvedRouteConfig): string {
|
||||
const str = JSON.stringify(config);
|
||||
return Buffer.from(str).toString('base64url');
|
||||
}
|
||||
|
||||
export function isLayoutRoute(
|
||||
routeId: string,
|
||||
routes: Pick<ConfigRoute, 'id' | 'parentId'>[]
|
||||
@@ -40,22 +125,39 @@ export function* getRouteIterator(route: ConfigRoute, routes: RouteManifest) {
|
||||
export function getPathFromRoute(
|
||||
route: ConfigRoute,
|
||||
routes: RouteManifest
|
||||
): string {
|
||||
const pathParts: string[] = [];
|
||||
for (const currentRoute of getRouteIterator(route, routes)) {
|
||||
if (currentRoute.index) pathParts.push('index');
|
||||
if (currentRoute.path) pathParts.push(currentRoute.path);
|
||||
): ResolvedRoutePaths {
|
||||
if (
|
||||
route.id === 'root' ||
|
||||
(route.parentId === 'root' && !route.path && route.index)
|
||||
) {
|
||||
return { path: 'index', rePath: '/index' };
|
||||
}
|
||||
|
||||
const pathParts: string[] = [];
|
||||
const rePathParts: string[] = [];
|
||||
|
||||
for (const currentRoute of getRouteIterator(route, routes)) {
|
||||
if (!currentRoute.path) continue;
|
||||
|
||||
pathParts.push(
|
||||
currentRoute.path.replace(/:(.+)\?/g, (_, name) => `(:${name})`)
|
||||
);
|
||||
|
||||
rePathParts.push(currentRoute.path);
|
||||
}
|
||||
|
||||
const path = pathParts.reverse().join('/');
|
||||
return path;
|
||||
|
||||
// Replace "/*" at the end to handle "splat routes"
|
||||
let rePath = rePathParts.reverse().join('/');
|
||||
rePath =
|
||||
rePath === '*' ? SPLAT_PATH : `/${rePath.replace(/\/\*$/, SPLAT_PATH)}`;
|
||||
|
||||
return { path, rePath };
|
||||
}
|
||||
|
||||
export function getRegExpFromPath(path: string): RegExp | false {
|
||||
export function getRegExpFromPath(rePath: 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;
|
||||
}
|
||||
|
||||
20
packages/remix/test/fixtures/01-remix-basics/app/b.server.ts
vendored
Normal file
20
packages/remix/test/fixtures/01-remix-basics/app/b.server.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// Edge functions can not use child processes, but this is route
|
||||
// uses Node.js. So this is here to verify that bundle splitting
|
||||
// is working correctly (because this route should not exist in
|
||||
// the Edge bundle).
|
||||
import { exec } from 'child_process';
|
||||
|
||||
import { json } from '@remix-run/node';
|
||||
|
||||
export async function loader() {
|
||||
const hi = await new Promise<string>((resolve, reject) => {
|
||||
exec(
|
||||
`echo hi from the B page running in ${process.env.VERCEL_REGION}`,
|
||||
(err, stdout) => {
|
||||
if (err) return reject(err);
|
||||
resolve(stdout);
|
||||
}
|
||||
);
|
||||
});
|
||||
return json({ hi });
|
||||
}
|
||||
4
packages/remix/test/fixtures/01-remix-basics/app/routes/__pathless.tsx
vendored
Normal file
4
packages/remix/test/fixtures/01-remix-basics/app/routes/__pathless.tsx
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
// This is a pathless layout route at the root level,
|
||||
// but there are no child routes, so a function should
|
||||
// not be created for this route.
|
||||
export default () => <div>pathless layout route</div>;
|
||||
@@ -1,7 +1,16 @@
|
||||
import { loader } from '~/b.server';
|
||||
import { useLoaderData } from '@remix-run/react';
|
||||
|
||||
export const config = { regions: ['sfo1'] };
|
||||
|
||||
export { loader };
|
||||
|
||||
export default function B() {
|
||||
const { hi } = useLoaderData<typeof loader>();
|
||||
return (
|
||||
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
|
||||
<h1>B page</h1>
|
||||
<p>{hi}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
export const config = {
|
||||
runtime: 'edge'
|
||||
};
|
||||
export const config = { runtime: 'edge' };
|
||||
|
||||
export default function Edge() {
|
||||
return (
|
||||
|
||||
3
packages/remix/test/fixtures/01-remix-basics/app/routes/nested2/__pathless.tsx
vendored
Normal file
3
packages/remix/test/fixtures/01-remix-basics/app/routes/nested2/__pathless.tsx
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// This is a pathless layout route without any child routes,
|
||||
// but Remix will serve this since its not at the root level.
|
||||
export default () => <div>nested2 pathless layout route</div>;
|
||||
10
packages/remix/test/fixtures/01-remix-basics/app/routes/set-cookie-edge.tsx
vendored
Normal file
10
packages/remix/test/fixtures/01-remix-basics/app/routes/set-cookie-edge.tsx
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { LoaderFunction } from '@remix-run/server-runtime';
|
||||
|
||||
export const config = { runtime: 'edge' };
|
||||
|
||||
export const loader: LoaderFunction = () => {
|
||||
const headers = new Headers();
|
||||
headers.append('Set-Cookie', 'hello=world');
|
||||
headers.append('Set-Cookie', 'foo=bar');
|
||||
return new Response(null, { headers });
|
||||
};
|
||||
8
packages/remix/test/fixtures/01-remix-basics/app/routes/set-cookie-node.tsx
vendored
Normal file
8
packages/remix/test/fixtures/01-remix-basics/app/routes/set-cookie-node.tsx
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import type { LoaderFunction } from '@remix-run/server-runtime';
|
||||
|
||||
export const loader: LoaderFunction = () => {
|
||||
const headers = new Headers();
|
||||
headers.append('Set-Cookie', 'hello=world');
|
||||
headers.append('Set-Cookie', 'foo=bar');
|
||||
return new Response(null, { headers });
|
||||
};
|
||||
8
packages/remix/test/fixtures/01-remix-basics/app/routes/tex[t.t]xt.tsx
vendored
Normal file
8
packages/remix/test/fixtures/01-remix-basics/app/routes/tex[t.t]xt.tsx
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import { LoaderFunction } from '@remix-run/node';
|
||||
|
||||
export const loader: LoaderFunction = ({ request }) => {
|
||||
const { pathname } = new URL(request.url);
|
||||
return new Response(`this is a text file served at: ${pathname}`, {
|
||||
headers: { 'content-type': 'text/plain' },
|
||||
});
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user