mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-25 19:00:01 +00:00
Compare commits
7 Commits
@vercel/no
...
Fix-Main-P
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
191ce739a1 | ||
|
|
d51832edf1 | ||
|
|
dcab764ef1 | ||
|
|
ba80da4957 | ||
|
|
89e5ce7256 | ||
|
|
a244493778 | ||
|
|
52472dbd43 |
2
.github/workflows/test-integration-cli.yml
vendored
2
.github/workflows/test-integration-cli.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
run: npm i -g pnpm@7.24.2
|
||||
- run: pnpm install
|
||||
- run: pnpm run build
|
||||
- run: pnpm test-cli
|
||||
- run: pnpm test-integration-cli
|
||||
env:
|
||||
VERCEL_TEST_TOKEN: ${{ secrets.VERCEL_TEST_TOKEN }}
|
||||
VERCEL_TEST_REGISTRATION_URL: ${{ secrets.VERCEL_TEST_REGISTRATION_URL }}
|
||||
|
||||
@@ -11,4 +11,4 @@ Remove the `functions` config from your `now.json` or `vercel.json` to take adva
|
||||
|
||||
### Useful Links
|
||||
|
||||
- [Functions Config Documentation](https://vercel.com/docs/concepts/projects/project-configuration#functions)
|
||||
- [Functions Config Documentation](https://vercel.com/docs/configuration?query=functions#project/functions)
|
||||
|
||||
@@ -11,6 +11,6 @@ Migrate from using legacy `routes` to the new `rewrites`, `redirects`, and `head
|
||||
|
||||
### Useful Links
|
||||
|
||||
- [Rewrites Documentation](https://vercel.com/docs/concepts/projects/project-configuration#rewrites)
|
||||
- [Redirects Documentation](https://vercel.com/docs/concepts/projects/project-configuration#redirects)
|
||||
- [Headers Documentation](https://vercel.com/docs/concepts/projects/project-configuration#headers)
|
||||
- [Rewrites Documentation](https://vercel.com/docs/configuration?query=rewrites#project/rewrites)
|
||||
- [Redirects Documentation](https://vercel.com/docs/configuration?query=rewrites#project/redirects)
|
||||
- [Headers Documentation](https://vercel.com/docs/configuration?query=rewrites#project/headers)
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
#### Why This Error Occurred
|
||||
|
||||
This error is often caused by a misconfigured "Build Command" or "Output Directory" for your Next.js project.
|
||||
This could be caused by a misconfigured "Build Command" or "Output Directory" for your Next.js project.
|
||||
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
In the Vercel dashboard, open your "Project Settings" and draw attention to "Build & Development Settings":
|
||||
|
||||
1. Ensure that the "Build Command" setting is not overridden, or that it calls `next build`. If this command is not overridden but you are seeing this error, double check that your `build` script in `package.json` calls `next build`. If `buildCommand` exists in `vercel.json`, make sure it calls `next build`.
|
||||
2. Ensure that the "Output Directory" setting is not overridden. This value almost never needs to be configured, and is only necessary if you override `distDir` in `next.config.js`. If `outputDirectory` exists in `vercel.json`, remove that property.
|
||||
3. For `next export` users: **do not override the "Output Directory"**, even if you customized the `next export` output directory. It will automatically detects the correct output.
|
||||
1. Ensure that the "Build Command" setting is not changed, or that it calls `next build`. If this command is not changed but you are seeing this error, double check that your `build` script in `package.json` calls `next build`.
|
||||
2. Ensure that the "Output Directory" setting is not changed. This value almost never needs to be configured, and is only necessary if you override `distDir` in `next.config.js`.
|
||||
3. For `next export` users: **do not override the "Output Directory"**. Next.js automatically detects what folder you outputted `next export` to.
|
||||
|
||||
In rare scenarios, this error message can also be caused by a Next.js build failure (if your "Build Command" accidentally returns an exit code that is not 0).
|
||||
Double check for any error messages above the Routes Manifest error, which may provide additional details.
|
||||
|
||||
2
examples/package.json
vendored
2
examples/package.json
vendored
@@ -4,7 +4,7 @@
|
||||
"description": "Each subdirectory is an example boilerplate for a framework. This package.json only exists for testing purposes.",
|
||||
"scripts": {
|
||||
"test-unit": "pnpm test __tests__/unit/",
|
||||
"test-e2e": "pnpm test __tests__/integration/",
|
||||
"test-integration-once": "pnpm test __tests__/integration/",
|
||||
"test": "jest --env node --verbose --runInBand --bail"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -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.7.0"
|
||||
},
|
||||
"scripts": {
|
||||
"lerna": "lerna",
|
||||
@@ -47,9 +47,9 @@
|
||||
"pre-commit": "lint-staged",
|
||||
"test": "jest --rootDir=\"test\" --testPathPattern=\"\\.test.js\"",
|
||||
"test-unit": "pnpm test && node utils/gen.js && turbo run test-unit",
|
||||
"test-cli": "node utils/gen.js && turbo run test-cli",
|
||||
"test-e2e": "node utils/gen.js && turbo run test-e2e",
|
||||
"test-dev": "node utils/gen.js && turbo run test-dev",
|
||||
"test-integration-cli": "node utils/gen.js && turbo run test-integration-cli",
|
||||
"test-integration-once": "node utils/gen.js && turbo run test-integration-once",
|
||||
"test-integration-dev": "node utils/gen.js && turbo run test-integration-dev",
|
||||
"lint": "eslint . --cache --ext .ts,.js",
|
||||
"prepare": "husky install",
|
||||
"pack": "cd utils && node -r ts-eager/register ./pack.ts"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "6.2.3",
|
||||
"version": "6.2.2",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
@@ -14,7 +14,7 @@
|
||||
"build": "node build",
|
||||
"test": "jest --env node --verbose --runInBand --bail",
|
||||
"test-unit": "pnpm test test/unit.*test.*",
|
||||
"test-e2e": "pnpm test test/integration.test.ts"
|
||||
"test-integration-once": "pnpm test test/integration.test.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iarna/toml": "2.2.3",
|
||||
|
||||
@@ -44,8 +44,7 @@ export function getPrettyError(obj: {
|
||||
message?: string;
|
||||
params: any;
|
||||
}): NowBuildError {
|
||||
const docsUrl =
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration';
|
||||
const docsUrl = 'https://vercel.com/docs/configuration';
|
||||
try {
|
||||
const { dataPath, params, message: ajvMessage } = obj;
|
||||
const prop = getTopLevelPropertyName(dataPath);
|
||||
@@ -64,7 +63,7 @@ export function getPrettyError(obj: {
|
||||
return new NowBuildError({
|
||||
code: 'INVALID_VERCEL_CONFIG',
|
||||
message: message,
|
||||
link: prop ? `${docsUrl}#${prop.toLowerCase()}` : docsUrl,
|
||||
link: prop ? `${docsUrl}#project/${prop.toLowerCase()}` : docsUrl,
|
||||
action: 'View Documentation',
|
||||
});
|
||||
} catch (e) {
|
||||
|
||||
@@ -13,8 +13,10 @@ export const functionsSchema = {
|
||||
maxLength: 256,
|
||||
},
|
||||
memory: {
|
||||
minimum: 128,
|
||||
maximum: 3008,
|
||||
// Number between 128 and 3008 in steps of 64
|
||||
enum: Object.keys(Array.from({ length: 50 }))
|
||||
.slice(2, 48)
|
||||
.map(x => Number(x) * 64),
|
||||
},
|
||||
maxDuration: {
|
||||
type: 'number',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "28.15.5",
|
||||
"version": "28.15.4",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -14,8 +14,8 @@
|
||||
"preinstall": "node ./scripts/preinstall.js",
|
||||
"test": "jest --env node --verbose --bail",
|
||||
"test-unit": "pnpm test test/unit/",
|
||||
"test-cli": "rimraf test/fixtures/integration && ava test/integration.js --serial --fail-fast --verbose",
|
||||
"test-dev": "pnpm test test/dev/",
|
||||
"test-integration-cli": "rimraf test/fixtures/integration && ava test/integration.js --serial --fail-fast --verbose",
|
||||
"test-integration-dev": "pnpm test test/dev/",
|
||||
"coverage": "codecov",
|
||||
"build": "ts-node ./scripts/build.ts",
|
||||
"dev": "ts-node ./src/index.ts"
|
||||
@@ -41,16 +41,16 @@
|
||||
"node": ">= 14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "6.2.3",
|
||||
"@vercel/go": "2.3.5",
|
||||
"@vercel/hydrogen": "0.0.51",
|
||||
"@vercel/next": "3.4.4",
|
||||
"@vercel/node": "2.9.4",
|
||||
"@vercel/python": "3.1.47",
|
||||
"@vercel/redwood": "1.1.3",
|
||||
"@vercel/remix": "1.3.0",
|
||||
"@vercel/ruby": "1.3.63",
|
||||
"@vercel/static-build": "1.3.7"
|
||||
"@vercel/build-utils": "6.2.2",
|
||||
"@vercel/go": "2.3.4",
|
||||
"@vercel/hydrogen": "0.0.50",
|
||||
"@vercel/next": "3.4.3",
|
||||
"@vercel/node": "2.9.3",
|
||||
"@vercel/python": "3.1.46",
|
||||
"@vercel/redwood": "1.1.2",
|
||||
"@vercel/remix": "1.2.13",
|
||||
"@vercel/ruby": "1.3.62",
|
||||
"@vercel/static-build": "1.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alex_neo/jest-expect-message": "1.0.5",
|
||||
@@ -93,10 +93,10 @@
|
||||
"@types/which": "1.3.2",
|
||||
"@types/write-json-file": "2.2.1",
|
||||
"@types/yauzl-promise": "2.1.0",
|
||||
"@vercel/client": "12.3.9",
|
||||
"@vercel/client": "12.3.8",
|
||||
"@vercel/error-utils": "1.0.8",
|
||||
"@vercel/frameworks": "1.3.0",
|
||||
"@vercel/fs-detectors": "3.7.12",
|
||||
"@vercel/fs-detectors": "3.7.11",
|
||||
"@vercel/fun": "1.0.4",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/routing-utils": "2.1.8",
|
||||
|
||||
@@ -701,7 +701,7 @@ function expandBuild(files: string[], build: Builder): Builder[] {
|
||||
throw new NowBuildError({
|
||||
code: `invalid_build_specification`,
|
||||
message: 'Field `use` is missing in build specification',
|
||||
link: 'https://vercel.com/docs/concepts/projects/project-configuration#builds',
|
||||
link: 'https://vercel.com/docs/configuration#project/builds',
|
||||
action: 'View Documentation',
|
||||
});
|
||||
}
|
||||
@@ -711,7 +711,7 @@ function expandBuild(files: string[], build: Builder): Builder[] {
|
||||
throw new NowBuildError({
|
||||
code: `invalid_build_specification`,
|
||||
message: 'A build `src` path resolves to an empty string',
|
||||
link: 'https://vercel.com/docs/concepts/projects/project-configuration#builds',
|
||||
link: 'https://vercel.com/docs/configuration#project/builds',
|
||||
action: 'View Documentation',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
{
|
||||
"name": "app-2",
|
||||
"version": "0.0.1",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "node script.js"
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ module.exports = async function prepare(session, binaryPath, tmpFixturesDir) {
|
||||
},
|
||||
}),
|
||||
},
|
||||
'lambda-with-123-memory': {
|
||||
'lambda-with-200-memory': {
|
||||
'api/memory.js': `
|
||||
module.exports = (req, res) => {
|
||||
res.json({ memory: parseInt(process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE) });
|
||||
@@ -280,7 +280,7 @@ module.exports = async function prepare(session, binaryPath, tmpFixturesDir) {
|
||||
'now.json': JSON.stringify({
|
||||
functions: {
|
||||
'api/**/*.js': {
|
||||
memory: 123,
|
||||
memory: 200,
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
24
packages/cli/test/integration.js
vendored
24
packages/cli/test/integration.js
vendored
@@ -1967,11 +1967,7 @@ test('try to create a builds deployments with wrong now.json', async t => {
|
||||
'Error: Invalid now.json - should NOT have additional property `builder`. Did you mean `builds`?'
|
||||
)
|
||||
);
|
||||
t.true(
|
||||
stderr.includes(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration'
|
||||
)
|
||||
);
|
||||
t.true(stderr.includes('https://vercel.com/docs/configuration'));
|
||||
});
|
||||
|
||||
test('try to create a builds deployments with wrong vercel.json', async t => {
|
||||
@@ -1995,11 +1991,7 @@ test('try to create a builds deployments with wrong vercel.json', async t => {
|
||||
'Error: Invalid vercel.json - should NOT have additional property `fake`. Please remove it.'
|
||||
)
|
||||
);
|
||||
t.true(
|
||||
stderr.includes(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration'
|
||||
)
|
||||
);
|
||||
t.true(stderr.includes('https://vercel.com/docs/configuration'));
|
||||
});
|
||||
|
||||
test('try to create a builds deployments with wrong `build.env` property', async t => {
|
||||
@@ -2022,9 +2014,7 @@ test('try to create a builds deployments with wrong `build.env` property', async
|
||||
formatOutput({ stdout, stderr })
|
||||
);
|
||||
t.true(
|
||||
stderr.includes(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration'
|
||||
),
|
||||
stderr.includes('https://vercel.com/docs/configuration'),
|
||||
formatOutput({ stdout, stderr })
|
||||
);
|
||||
});
|
||||
@@ -2729,15 +2719,11 @@ test('deploy a Lambda with 128MB of memory', async t => {
|
||||
});
|
||||
|
||||
test('fail to deploy a Lambda with an incorrect value for of memory', async t => {
|
||||
const directory = fixture('lambda-with-123-memory');
|
||||
const directory = fixture('lambda-with-200-memory');
|
||||
const output = await execute([directory, '--yes']);
|
||||
|
||||
t.is(output.exitCode, 1, formatOutput(output));
|
||||
t.regex(
|
||||
output.stderr,
|
||||
/Serverless Functions.+memory/gm,
|
||||
formatOutput(output)
|
||||
);
|
||||
t.regex(output.stderr, /steps of 64/gm, formatOutput(output));
|
||||
t.regex(output.stderr, /Learn More/gm, formatOutput(output));
|
||||
});
|
||||
|
||||
|
||||
@@ -1080,7 +1080,7 @@ describe('build', () => {
|
||||
await expect(client.stderr).toOutput(
|
||||
'Error: Invalid vercel.json - `rewrites[2]` should NOT have additional property `src`. Did you mean `source`?' +
|
||||
'\n' +
|
||||
'View Documentation: https://vercel.com/docs/concepts/projects/project-configuration#rewrites'
|
||||
'View Documentation: https://vercel.com/docs/configuration#project/rewrites'
|
||||
);
|
||||
const builds = await fs.readJSON(join(output, 'builds.json'));
|
||||
expect(builds.builds).toBeUndefined();
|
||||
@@ -1091,7 +1091,7 @@ describe('build', () => {
|
||||
stack: expect.stringContaining('at validateConfig'),
|
||||
hideStackTrace: true,
|
||||
code: 'INVALID_VERCEL_CONFIG',
|
||||
link: 'https://vercel.com/docs/concepts/projects/project-configuration#rewrites',
|
||||
link: 'https://vercel.com/docs/configuration#project/rewrites',
|
||||
action: 'View Documentation',
|
||||
});
|
||||
const configJson = await fs.readJSON(join(output, 'config.json'));
|
||||
|
||||
@@ -41,7 +41,7 @@ describe('validateConfig', () => {
|
||||
'Invalid vercel.json - `rewrites[0]` should NOT have additional property `src`. Did you mean `source`?'
|
||||
);
|
||||
expect(error!.link).toEqual(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration#rewrites'
|
||||
'https://vercel.com/docs/configuration#project/rewrites'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -54,7 +54,7 @@ describe('validateConfig', () => {
|
||||
'Invalid vercel.json - `routes[0]` should NOT have additional property `source`. Did you mean `src`?'
|
||||
);
|
||||
expect(error!.link).toEqual(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration#routes'
|
||||
'https://vercel.com/docs/configuration#project/routes'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -67,7 +67,7 @@ describe('validateConfig', () => {
|
||||
'Invalid vercel.json - `routes` should be array.'
|
||||
);
|
||||
expect(error!.link).toEqual(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration#routes'
|
||||
'https://vercel.com/docs/configuration#project/routes'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -84,7 +84,7 @@ describe('validateConfig', () => {
|
||||
'Invalid vercel.json - `redirects[0]` missing required property `source`.'
|
||||
);
|
||||
expect(error!.link).toEqual(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration#redirects'
|
||||
'https://vercel.com/docs/configuration#project/redirects'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -97,7 +97,7 @@ describe('validateConfig', () => {
|
||||
'Invalid vercel.json - `redirects[0].permanent` should be boolean.'
|
||||
);
|
||||
expect(error!.link).toEqual(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration#redirects'
|
||||
'https://vercel.com/docs/configuration#project/redirects'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -110,7 +110,7 @@ describe('validateConfig', () => {
|
||||
'Invalid vercel.json - `cleanUrls` should be boolean.'
|
||||
);
|
||||
expect(error!.link).toEqual(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration#cleanurls'
|
||||
'https://vercel.com/docs/configuration#project/cleanurls'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -123,7 +123,7 @@ describe('validateConfig', () => {
|
||||
'Invalid vercel.json - `trailingSlash` should be boolean.'
|
||||
);
|
||||
expect(error!.link).toEqual(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration#trailingslash'
|
||||
'https://vercel.com/docs/configuration#project/trailingslash'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -136,7 +136,7 @@ describe('validateConfig', () => {
|
||||
'Invalid vercel.json - `headers[0]` should NOT have additional property `Content-Type`. Please remove it.'
|
||||
);
|
||||
expect(error!.link).toEqual(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration#headers'
|
||||
'https://vercel.com/docs/configuration#project/headers'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -149,7 +149,7 @@ describe('validateConfig', () => {
|
||||
'Invalid vercel.json - `headers[0].source` should be string.'
|
||||
);
|
||||
expect(error!.link).toEqual(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration#headers'
|
||||
'https://vercel.com/docs/configuration#project/headers'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -162,7 +162,7 @@ describe('validateConfig', () => {
|
||||
'Invalid vercel.json - `headers[0]` should NOT have additional property `stuff`. Please remove it.'
|
||||
);
|
||||
expect(error!.link).toEqual(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration#headers'
|
||||
'https://vercel.com/docs/configuration#project/headers'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -175,7 +175,7 @@ describe('validateConfig', () => {
|
||||
'Invalid vercel.json - `headers[0].headers[0]` should NOT have additional property `Content-Type`. Please remove it.'
|
||||
);
|
||||
expect(error!.link).toEqual(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration#headers'
|
||||
'https://vercel.com/docs/configuration#project/headers'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -190,7 +190,7 @@ describe('validateConfig', () => {
|
||||
'Invalid vercel.json - `headers[0].headers[0]` should NOT have additional property `val`. Please remove it.'
|
||||
);
|
||||
expect(error!.link).toEqual(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration#headers'
|
||||
'https://vercel.com/docs/configuration#project/headers'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -205,7 +205,7 @@ describe('validateConfig', () => {
|
||||
'Invalid vercel.json - `redirects` should NOT have more than 1024 items.'
|
||||
);
|
||||
expect(error!.link).toEqual(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration#redirects'
|
||||
'https://vercel.com/docs/configuration#project/redirects'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -229,39 +229,7 @@ describe('validateConfig', () => {
|
||||
'Invalid vercel.json - `headers[1].headers` should NOT have more than 1024 items.'
|
||||
);
|
||||
expect(error!.link).toEqual(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration#headers'
|
||||
);
|
||||
});
|
||||
|
||||
it('should error with too low memory value', async () => {
|
||||
const error = validateConfig({
|
||||
functions: {
|
||||
'api/test.js': {
|
||||
memory: 127,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(error!.message).toEqual(
|
||||
"Invalid vercel.json - `functions['api/test.js'].memory` should be >= 128."
|
||||
);
|
||||
expect(error!.link).toEqual(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration#functions'
|
||||
);
|
||||
});
|
||||
|
||||
it('should error with too high memory value', async () => {
|
||||
const error = validateConfig({
|
||||
functions: {
|
||||
'api/test.js': {
|
||||
memory: 3009,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(error!.message).toEqual(
|
||||
"Invalid vercel.json - `functions['api/test.js'].memory` should be <= 3008."
|
||||
);
|
||||
expect(error!.link).toEqual(
|
||||
'https://vercel.com/docs/concepts/projects/project-configuration#functions'
|
||||
'https://vercel.com/docs/configuration#project/headers'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "12.3.9",
|
||||
"version": "12.3.8",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"homepage": "https://vercel.com",
|
||||
@@ -15,7 +15,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test-e2e": "pnpm test tests/create-deployment.test.ts tests/create-legacy-deployment.test.ts tests/paths.test.ts",
|
||||
"test-integration-once": "pnpm test tests/create-deployment.test.ts tests/create-legacy-deployment.test.ts tests/paths.test.ts",
|
||||
"test": "jest --env node --verbose --runInBand --bail",
|
||||
"test-unit": "pnpm test tests/unit.*test.*"
|
||||
},
|
||||
@@ -43,7 +43,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "6.2.3",
|
||||
"@vercel/build-utils": "6.2.2",
|
||||
"@vercel/routing-utils": "2.1.8",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/fs-detectors",
|
||||
"version": "3.7.12",
|
||||
"version": "3.7.11",
|
||||
"description": "Vercel filesystem detectors",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -35,7 +35,7 @@
|
||||
"@types/minimatch": "3.0.5",
|
||||
"@types/node": "14.18.33",
|
||||
"@types/semver": "7.3.10",
|
||||
"@vercel/build-utils": "6.2.3",
|
||||
"@vercel/build-utils": "6.2.2",
|
||||
"typescript": "4.3.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/gatsby-plugin-vercel-builder",
|
||||
"version": "1.1.5",
|
||||
"version": "1.1.4",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
"dist",
|
||||
@@ -14,8 +14,8 @@
|
||||
"build:src": "tsc -p tsconfig.src.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "6.2.3",
|
||||
"@vercel/node": "2.9.4",
|
||||
"@vercel/build-utils": "6.2.2",
|
||||
"@vercel/node": "2.9.3",
|
||||
"@vercel/routing-utils": "2.1.8",
|
||||
"ajv": "8.12.0",
|
||||
"esbuild": "0.14.47",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/go",
|
||||
"version": "2.3.5",
|
||||
"version": "2.3.4",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
|
||||
@@ -12,7 +12,7 @@
|
||||
"scripts": {
|
||||
"build": "node build",
|
||||
"test": "jest --env node --verbose --runInBand --bail",
|
||||
"test-e2e": "pnpm test"
|
||||
"test-integration-once": "pnpm test"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
@@ -36,7 +36,7 @@
|
||||
"@types/node": "14.18.33",
|
||||
"@types/node-fetch": "^2.3.0",
|
||||
"@types/tar": "^4.0.0",
|
||||
"@vercel/build-utils": "6.2.3",
|
||||
"@vercel/build-utils": "6.2.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.51",
|
||||
"version": "0.0.50",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
@@ -11,7 +11,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node build.js",
|
||||
"test-e2e": "pnpm test test/test.js",
|
||||
"test-integration-once": "pnpm test test/test.js",
|
||||
"test": "jest --env node --verbose --bail --runInBand"
|
||||
},
|
||||
"files": [
|
||||
@@ -21,7 +21,7 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "27.5.1",
|
||||
"@types/node": "14.18.33",
|
||||
"@vercel/build-utils": "6.2.3",
|
||||
"@vercel/build-utils": "6.2.2",
|
||||
"@vercel/static-config": "2.0.12",
|
||||
"execa": "3.2.0",
|
||||
"fs-extra": "11.1.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/next",
|
||||
"version": "3.4.4",
|
||||
"version": "3.4.3",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
|
||||
@@ -11,7 +11,7 @@
|
||||
"test-unit": "pnpm test test/unit/",
|
||||
"test-next-local": "pnpm test test/integration/*.test.js test/integration/*.test.ts",
|
||||
"test-next-local:middleware": "pnpm test test/integration/middleware.test.ts",
|
||||
"test-e2e": "rm -f test/builder-info.json; pnpm test test/fixtures/**/*.test.js"
|
||||
"test-integration-once": "rm -f test/builder-info.json; pnpm test test/fixtures/**/*.test.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -45,7 +45,7 @@
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/text-table": "0.2.1",
|
||||
"@types/webpack-sources": "3.2.0",
|
||||
"@vercel/build-utils": "6.2.3",
|
||||
"@vercel/build-utils": "6.2.2",
|
||||
"@vercel/nft": "0.22.5",
|
||||
"@vercel/routing-utils": "2.1.8",
|
||||
"async-sema": "3.0.1",
|
||||
|
||||
@@ -269,7 +269,12 @@ export async function getRoutesManifest(
|
||||
|
||||
if (shouldHaveManifest && !hasRoutesManifest) {
|
||||
throw new NowBuildError({
|
||||
message: `The file "${pathRoutesManifest}" couldn't be found. This is often caused by a misconfiguration in your project.`,
|
||||
message:
|
||||
`The file "${pathRoutesManifest}" couldn't be found. This is normally caused by a misconfiguration in your project.\n` +
|
||||
'Please check the following, and reach out to support if you cannot resolve the problem:\n' +
|
||||
' 1. If present, be sure your `build` script in "package.json" calls `next build`.' +
|
||||
' 2. Navigate to your project\'s settings in the Vercel dashboard, and verify that the "Build Command" is not overridden, or that it calls `next build`.' +
|
||||
' 3. Navigate to your project\'s settings in the Vercel dashboard, and verify that the "Output Directory" is not overridden. Note that `next export` does **not** require you change this setting, even if you customize the `next export` output directory.',
|
||||
link: 'https://err.sh/vercel/vercel/now-next-routes-manifest',
|
||||
code: 'NEXT_NO_ROUTES_MANIFEST',
|
||||
});
|
||||
@@ -2026,8 +2031,7 @@ export const onPrerenderRoute =
|
||||
const rscVaryHeader =
|
||||
routesManifest?.rsc?.varyHeader ||
|
||||
'__rsc__, __next_router_state_tree__, __next_router_prefetch__';
|
||||
const rscContentTypeHeader =
|
||||
routesManifest?.rsc?.contentTypeHeader || 'application/octet-stream';
|
||||
const rscContentTypeHeader = routesManifest?.rsc?.contentTypeHeader || 'application/octet-stream';
|
||||
|
||||
prerenders[outputPathPage] = new Prerender({
|
||||
expiration: initialRevalidate,
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"RSC": "1"
|
||||
},
|
||||
"responseHeaders": {
|
||||
"content-type": "text/x-component"
|
||||
"content-type": "application/octet-stream"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -51,7 +51,7 @@
|
||||
"RSC": "1"
|
||||
},
|
||||
"responseHeaders": {
|
||||
"content-type": "text/x-component"
|
||||
"content-type": "application/octet-stream"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"RSC": "1"
|
||||
},
|
||||
"responseHeaders": {
|
||||
"content-type": "text/x-component",
|
||||
"content-type": "application/octet-stream",
|
||||
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
"RSC": "1"
|
||||
},
|
||||
"responseHeaders": {
|
||||
"content-type": "text/x-component",
|
||||
"content-type": "application/octet-stream",
|
||||
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -119,7 +119,7 @@
|
||||
"RSC": "1"
|
||||
},
|
||||
"responseHeaders": {
|
||||
"content-type": "text/x-component",
|
||||
"content-type": "application/octet-stream",
|
||||
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/node",
|
||||
"version": "2.9.4",
|
||||
"version": "2.9.3",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
||||
@@ -13,7 +13,7 @@
|
||||
"build": "node build",
|
||||
"test": "jest --env node --verbose --bail --runInBand",
|
||||
"test-unit": "pnpm test test/prepare-cache.test.ts test/utils.test.ts",
|
||||
"test-e2e": "pnpm test test/integration-*.test.js"
|
||||
"test-integration-once": "pnpm test test/integration-*.test.js"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
@@ -31,7 +31,7 @@
|
||||
"dependencies": {
|
||||
"@edge-runtime/vm": "2.0.0",
|
||||
"@types/node": "14.18.33",
|
||||
"@vercel/build-utils": "6.2.3",
|
||||
"@vercel/build-utils": "6.2.2",
|
||||
"@vercel/node-bridge": "3.1.11",
|
||||
"@vercel/static-config": "2.0.12",
|
||||
"edge-runtime": "2.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/python",
|
||||
"version": "3.1.47",
|
||||
"version": "3.1.46",
|
||||
"main": "./dist/index.js",
|
||||
"license": "MIT",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
|
||||
@@ -17,13 +17,13 @@
|
||||
"build": "node build",
|
||||
"test": "jest --env node --verbose --runInBand --bail",
|
||||
"test-unit": "pnpm test test/unit.test.ts",
|
||||
"test-e2e": "pnpm test test/integration.test.ts"
|
||||
"test-integration-once": "pnpm test test/integration.test.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/execa": "^0.9.0",
|
||||
"@types/jest": "27.4.1",
|
||||
"@types/node": "14.18.33",
|
||||
"@vercel/build-utils": "6.2.3",
|
||||
"@vercel/build-utils": "6.2.2",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"execa": "^1.0.0",
|
||||
"typescript": "4.3.4"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/redwood",
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.2",
|
||||
"main": "./dist/index.js",
|
||||
"license": "MIT",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
@@ -14,7 +14,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node build.js",
|
||||
"test-e2e": "pnpm test test/test.js",
|
||||
"test-integration-once": "pnpm test test/test.js",
|
||||
"test": "jest --env node --verbose --bail --runInBand",
|
||||
"test-unit": "pnpm test test/prepare-cache.test.js"
|
||||
},
|
||||
@@ -27,7 +27,7 @@
|
||||
"@types/aws-lambda": "8.10.19",
|
||||
"@types/node": "14.18.33",
|
||||
"@types/semver": "6.0.0",
|
||||
"@vercel/build-utils": "6.2.3",
|
||||
"@vercel/build-utils": "6.2.2",
|
||||
"execa": "3.2.0",
|
||||
"fs-extra": "11.1.0",
|
||||
"typescript": "4.3.4"
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
# `@vercel/remix-entry-server`
|
||||
|
||||
This package is meant for use within Remix applications when deploying to Vercel. It provides implementations for the `app/entry.server.tsx` file for both the Node.js Serverless Runtime and the Edge Runtime. The implementations are configured to [handle streaming responses](https://remix.run/docs/en/v1/guides/streaming).
|
||||
|
||||
## Usage
|
||||
|
||||
Make sure `@vercel/remix-entry-server` is installed with your package manager of choice, then replace your `app/entry.server.tsx` file with the following:
|
||||
|
||||
```tsx
|
||||
// `app/entry.server.tsx`
|
||||
|
||||
import handleRequest from '@vercel/remix-entry-server';
|
||||
import { RemixServer } from '@remix-run/react';
|
||||
import type { EntryContext } from '@remix-run/server-runtime';
|
||||
|
||||
export default function (
|
||||
request: Request,
|
||||
responseStatusCode: number,
|
||||
responseHeaders: Headers,
|
||||
remixContext: EntryContext
|
||||
) {
|
||||
const remixServer = <RemixServer context={remixContext} url={request.url} />;
|
||||
return handleRequest(
|
||||
request,
|
||||
responseStatusCode,
|
||||
responseHeaders,
|
||||
remixServer
|
||||
);
|
||||
}
|
||||
```
|
||||
@@ -1,12 +0,0 @@
|
||||
const execa = require('execa');
|
||||
const { remove } = require('fs-extra');
|
||||
|
||||
async function main() {
|
||||
await remove('dist');
|
||||
await execa('tsc', [], { stdio: 'inherit' });
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"name": "@vercel/remix-entry-server",
|
||||
"version": "0.1.0",
|
||||
"description": "Isomorphic `entry.server` implementation for Vercel's Serverless and Edge runtimes",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vercel/vercel.git",
|
||||
"directory": "packages/remix-entry-server"
|
||||
},
|
||||
"main": "./dist/entry.server.node.js",
|
||||
"browser": "./dist/entry.server.edge.js",
|
||||
"scripts": {
|
||||
"build": "node build.js"
|
||||
},
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"@remix-run/node": "1.12.0",
|
||||
"isbot": "3.6.5",
|
||||
"react-dom": "18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react-dom": "18.0.10",
|
||||
"execa": "3.2.0",
|
||||
"fs-extra": "11.1.0",
|
||||
"typescript": "4.9.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0"
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import isbot from 'isbot';
|
||||
import { renderToReadableStream } from 'react-dom/server';
|
||||
|
||||
export default async function handleRequest(
|
||||
request: Request,
|
||||
responseStatusCode: number,
|
||||
responseHeaders: Headers,
|
||||
remixServer: JSX.Element
|
||||
) {
|
||||
const body = await renderToReadableStream(remixServer, {
|
||||
signal: request.signal,
|
||||
onError(error) {
|
||||
console.error(error);
|
||||
responseStatusCode = 500;
|
||||
},
|
||||
});
|
||||
|
||||
if (isbot(request.headers.get('user-agent'))) {
|
||||
await body.allReady;
|
||||
}
|
||||
|
||||
responseHeaders.set('Content-Type', 'text/html');
|
||||
return new Response(body, {
|
||||
headers: responseHeaders,
|
||||
status: responseStatusCode,
|
||||
});
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
import { PassThrough } from 'stream';
|
||||
import { renderToPipeableStream } from 'react-dom/server';
|
||||
import { Response } from '@remix-run/node';
|
||||
import isbot from 'isbot';
|
||||
|
||||
const ABORT_DELAY = 5000;
|
||||
|
||||
export default function handleRequest(
|
||||
request: Request,
|
||||
responseStatusCode: number,
|
||||
responseHeaders: Headers,
|
||||
remixServer: JSX.Element
|
||||
) {
|
||||
// If the request is from a bot, we want to wait for the full
|
||||
// response to render before sending it to the client. This
|
||||
// ensures that bots can see the full page content.
|
||||
if (isbot(request.headers.get('user-agent'))) {
|
||||
return serveTheBots(responseStatusCode, responseHeaders, remixServer);
|
||||
}
|
||||
|
||||
return serveBrowsers(responseStatusCode, responseHeaders, remixServer);
|
||||
}
|
||||
|
||||
function serveTheBots(
|
||||
responseStatusCode: number,
|
||||
responseHeaders: Headers,
|
||||
remixServer: any
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { pipe, abort } = renderToPipeableStream(remixServer, {
|
||||
// Use onAllReady to wait for the entire document to be ready
|
||||
onAllReady() {
|
||||
responseHeaders.set('Content-Type', 'text/html');
|
||||
const body = new PassThrough();
|
||||
pipe(body);
|
||||
resolve(
|
||||
new Response(body, {
|
||||
status: responseStatusCode,
|
||||
headers: responseHeaders,
|
||||
})
|
||||
);
|
||||
},
|
||||
onShellError(err) {
|
||||
reject(err);
|
||||
},
|
||||
});
|
||||
setTimeout(abort, ABORT_DELAY);
|
||||
});
|
||||
}
|
||||
|
||||
function serveBrowsers(
|
||||
responseStatusCode: number,
|
||||
responseHeaders: Headers,
|
||||
remixServer: any
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let didError = false;
|
||||
const { pipe, abort } = renderToPipeableStream(remixServer, {
|
||||
// use onShellReady to wait until a suspense boundary is triggered
|
||||
onShellReady() {
|
||||
responseHeaders.set('Content-Type', 'text/html');
|
||||
const body = new PassThrough();
|
||||
pipe(body);
|
||||
resolve(
|
||||
new Response(body, {
|
||||
status: didError ? 500 : responseStatusCode,
|
||||
headers: responseHeaders,
|
||||
})
|
||||
);
|
||||
},
|
||||
onShellError(err) {
|
||||
reject(err);
|
||||
},
|
||||
onError(err) {
|
||||
didError = true;
|
||||
console.error(err);
|
||||
},
|
||||
});
|
||||
setTimeout(abort, ABORT_DELAY);
|
||||
});
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["ES2020", "DOM"],
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"noEmitOnError": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"outDir": "./dist",
|
||||
"types": ["node", "jest"],
|
||||
"strict": true,
|
||||
"target": "ES2020",
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
3
packages/remix/default-server.js
Normal file
3
packages/remix/default-server.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const { createRequestHandler } = require('@remix-run/vercel');
|
||||
const build = require('./');
|
||||
module.exports = createRequestHandler({ build });
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/remix",
|
||||
"version": "1.3.0",
|
||||
"version": "1.2.13",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
@@ -11,25 +11,20 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node build.js",
|
||||
"test-e2e": "pnpm test test/integration.test.ts",
|
||||
"test-integration-once": "pnpm test test/integration.test.ts",
|
||||
"test": "jest --env node --verbose --bail --runInBand"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"server-node.mjs",
|
||||
"server-edge.mjs"
|
||||
"default-server.js"
|
||||
],
|
||||
"dependencies": {
|
||||
"@remix-run/dev": "1.12.0",
|
||||
"@vercel/nft": "0.22.5",
|
||||
"@vercel/static-config": "2.0.12",
|
||||
"path-to-regexp": "6.2.1",
|
||||
"ts-morph": "12.0.0"
|
||||
"@vercel/nft": "0.22.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "27.5.1",
|
||||
"@types/node": "14.18.33",
|
||||
"@vercel/build-utils": "6.2.3",
|
||||
"@vercel/build-utils": "6.2.2",
|
||||
"typescript": "4.9.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import { createRequestHandler } from '@remix-run/server-runtime';
|
||||
import build from './index.js';
|
||||
export default createRequestHandler(build);
|
||||
@@ -1,70 +0,0 @@
|
||||
import {
|
||||
AbortController as NodeAbortController,
|
||||
createRequestHandler as createRemixRequestHandler,
|
||||
Headers as NodeHeaders,
|
||||
Request as NodeRequest,
|
||||
writeReadableStreamToWritable,
|
||||
} from '@remix-run/node';
|
||||
import build from './index.js';
|
||||
|
||||
const handleRequest = createRemixRequestHandler(build, process.env.NODE_ENV);
|
||||
|
||||
function createRemixHeaders(requestHeaders) {
|
||||
const headers = new NodeHeaders();
|
||||
|
||||
for (const key in requestHeaders) {
|
||||
const header = requestHeaders[key];
|
||||
// set-cookie is an array (maybe others)
|
||||
if (Array.isArray(header)) {
|
||||
for (const value of header) {
|
||||
headers.append(key, value);
|
||||
}
|
||||
} else {
|
||||
headers.append(key, header);
|
||||
}
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
function createRemixRequest(req, res) {
|
||||
const host = req.headers['x-forwarded-host'] || req.headers['host'];
|
||||
const protocol = req.headers['x-forwarded-proto'] || 'https';
|
||||
const url = new URL(req.url, `${protocol}://${host}`);
|
||||
|
||||
// Abort action/loaders once we can no longer write a response
|
||||
const controller = new NodeAbortController();
|
||||
res.on('close', () => controller.abort());
|
||||
|
||||
const init = {
|
||||
method: req.method,
|
||||
headers: createRemixHeaders(req.headers),
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
||||
init.body = req;
|
||||
}
|
||||
|
||||
return new NodeRequest(url.href, init);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (nodeResponse.body) {
|
||||
await writeReadableStreamToWritable(nodeResponse.body, res);
|
||||
} else {
|
||||
res.end();
|
||||
}
|
||||
}
|
||||
|
||||
export default async (req, res) => {
|
||||
const request = createRemixRequest(req, res);
|
||||
const response = await handleRequest(request);
|
||||
await sendRemixResponse(res, response);
|
||||
};
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Project } from 'ts-morph';
|
||||
import { promises as fs } from 'fs';
|
||||
import { basename, dirname, extname, join, relative, sep } from 'path';
|
||||
import { pathToRegexp, Key } from 'path-to-regexp';
|
||||
import { runInContext, createContext } from 'vm';
|
||||
import { dirname, join, relative } from 'path';
|
||||
import {
|
||||
debug,
|
||||
download,
|
||||
@@ -11,7 +10,6 @@ import {
|
||||
getNodeVersion,
|
||||
getSpawnOptions,
|
||||
glob,
|
||||
EdgeFunction,
|
||||
NodejsLambda,
|
||||
readConfigFile,
|
||||
runNpmInstall,
|
||||
@@ -19,20 +17,22 @@ import {
|
||||
scanParentDirs,
|
||||
walkParentDirs,
|
||||
} from '@vercel/build-utils';
|
||||
import { getConfig } from '@vercel/static-config';
|
||||
import { nodeFileTrace } from '@vercel/nft';
|
||||
import { readConfig, RemixConfig } from '@remix-run/dev/dist/config';
|
||||
import type {
|
||||
BuildV2,
|
||||
Files,
|
||||
NodeVersion,
|
||||
PackageJson,
|
||||
BuildResultV2Typical,
|
||||
} from '@vercel/build-utils';
|
||||
import type { ConfigRoute } from '@remix-run/dev/dist/config/routes';
|
||||
import { nodeFileTrace } from '@vercel/nft';
|
||||
import { findConfig } from './utils';
|
||||
import type { AppConfig, RemixBuildManifest } from './types';
|
||||
import type { BuildResultV2Typical } from '@vercel/build-utils';
|
||||
|
||||
const _require: typeof require = eval('require');
|
||||
// Name of the Remix runtime adapter npm package for Vercel
|
||||
const REMIX_RUNTIME_ADAPTER_NAME = '@remix-run/vercel';
|
||||
|
||||
// Pinned version of the last verified working version of the adapter
|
||||
const REMIX_RUNTIME_ADAPTER_VERSION = '1.6.1';
|
||||
|
||||
export const build: BuildV2 = async ({
|
||||
entrypoint,
|
||||
@@ -72,6 +72,51 @@ export const build: BuildV2 = async ({
|
||||
env: spawnOpts.env || {},
|
||||
});
|
||||
|
||||
// Ensure `@remix-run/vercel` is in the project's `package.json`
|
||||
const packageJsonPath = await walkParentDirs({
|
||||
base: repoRootPath,
|
||||
start: workPath,
|
||||
filename: 'package.json',
|
||||
});
|
||||
if (packageJsonPath) {
|
||||
const packageJson: PackageJson = JSON.parse(
|
||||
await fs.readFile(packageJsonPath, 'utf8')
|
||||
);
|
||||
const { dependencies = {}, devDependencies = {} } = packageJson;
|
||||
|
||||
let modified = false;
|
||||
if (REMIX_RUNTIME_ADAPTER_NAME in devDependencies) {
|
||||
dependencies[REMIX_RUNTIME_ADAPTER_NAME] =
|
||||
devDependencies[REMIX_RUNTIME_ADAPTER_NAME];
|
||||
delete devDependencies[REMIX_RUNTIME_ADAPTER_NAME];
|
||||
console.log(
|
||||
`Warning: Moving "${REMIX_RUNTIME_ADAPTER_NAME}" from \`devDependencies\` to \`dependencies\`. You should commit this change.`
|
||||
);
|
||||
modified = true;
|
||||
} else if (!(REMIX_RUNTIME_ADAPTER_NAME in dependencies)) {
|
||||
dependencies[REMIX_RUNTIME_ADAPTER_NAME] = REMIX_RUNTIME_ADAPTER_VERSION;
|
||||
console.log(
|
||||
`Warning: Adding "${REMIX_RUNTIME_ADAPTER_NAME}" v${REMIX_RUNTIME_ADAPTER_VERSION} to \`dependencies\`. You should commit this change.`
|
||||
);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
const packageJsonString = JSON.stringify(
|
||||
{
|
||||
...packageJson,
|
||||
dependencies,
|
||||
devDependencies,
|
||||
},
|
||||
null,
|
||||
2
|
||||
);
|
||||
await fs.writeFile(packageJsonPath, `${packageJsonString}\n`);
|
||||
}
|
||||
} else {
|
||||
debug(`Failed to find "package.json" file in project`);
|
||||
}
|
||||
|
||||
if (typeof installCommand === 'string') {
|
||||
if (installCommand.trim()) {
|
||||
console.log(`Running "install" command: \`${installCommand}\`...`);
|
||||
@@ -89,206 +134,116 @@ export const build: BuildV2 = async ({
|
||||
// Make `remix build` output production mode
|
||||
spawnOpts.env.NODE_ENV = 'production';
|
||||
|
||||
// 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
|
||||
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) {
|
||||
if (err.code === 'ERR_REQUIRE_ESM') {
|
||||
isESM = true;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
let patchedConfig: string;
|
||||
if (isESM) {
|
||||
patchedConfig = `import config from './${basename(
|
||||
renamedRemixConfigPath
|
||||
)}';
|
||||
config.serverBuildTarget = undefined;
|
||||
config.server = 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.server = undefined;
|
||||
config.serverModuleFormat = 'cjs';
|
||||
config.serverPlatform = 'node';
|
||||
config.serverBuildPath = 'build/index.js';
|
||||
module.exports = config;`;
|
||||
}
|
||||
await fs.writeFile(remixConfigPath, patchedConfig);
|
||||
}
|
||||
|
||||
// Run "Build Command"
|
||||
let remixConfig: RemixConfig;
|
||||
try {
|
||||
if (buildCommand) {
|
||||
debug(`Executing build command "${buildCommand}"`);
|
||||
await execCommand(buildCommand, {
|
||||
if (buildCommand) {
|
||||
debug(`Executing build command "${buildCommand}"`);
|
||||
await execCommand(buildCommand, {
|
||||
...spawnOpts,
|
||||
cwd: entrypointFsDirname,
|
||||
});
|
||||
} else {
|
||||
const pkg = await readConfigFile<PackageJson>(
|
||||
join(entrypointFsDirname, 'package.json')
|
||||
);
|
||||
if (hasScript('vercel-build', pkg)) {
|
||||
debug(`Executing "yarn vercel-build"`);
|
||||
await runPackageJsonScript(
|
||||
entrypointFsDirname,
|
||||
'vercel-build',
|
||||
spawnOpts
|
||||
);
|
||||
} else if (hasScript('build', pkg)) {
|
||||
debug(`Executing "yarn build"`);
|
||||
await runPackageJsonScript(entrypointFsDirname, 'build', spawnOpts);
|
||||
} else {
|
||||
await execCommand('remix build', {
|
||||
...spawnOpts,
|
||||
cwd: entrypointFsDirname,
|
||||
});
|
||||
} else {
|
||||
const pkg = await readConfigFile<PackageJson>(
|
||||
join(entrypointFsDirname, 'package.json')
|
||||
);
|
||||
if (hasScript('vercel-build', pkg)) {
|
||||
debug(`Executing "yarn vercel-build"`);
|
||||
await runPackageJsonScript(
|
||||
entrypointFsDirname,
|
||||
'vercel-build',
|
||||
spawnOpts
|
||||
);
|
||||
} else if (hasScript('build', pkg)) {
|
||||
debug(`Executing "yarn build"`);
|
||||
await runPackageJsonScript(entrypointFsDirname, 'build', spawnOpts);
|
||||
} else {
|
||||
await execCommand('remix build', {
|
||||
...spawnOpts,
|
||||
cwd: entrypointFsDirname,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let serverBuildPath = 'build/index.js';
|
||||
let needsHandler = true;
|
||||
|
||||
const remixConfigFile = findConfig(entrypointFsDirname, 'remix.config');
|
||||
|
||||
try {
|
||||
if (remixConfigFile) {
|
||||
const remixConfigModule = await eval('import(remixConfigFile)');
|
||||
const remixConfig: AppConfig = remixConfigModule?.default || {};
|
||||
|
||||
// If `serverBuildTarget === 'vercel'` then Remix will output a handler
|
||||
// that is already in Vercel (req, res) format, so don't inject the handler
|
||||
if (remixConfig.serverBuildTarget) {
|
||||
if (remixConfig.serverBuildTarget !== 'vercel') {
|
||||
throw new Error(
|
||||
`\`serverBuildTarget\` in Remix config must be "vercel" (got "${remixConfig.serverBuildTarget}")`
|
||||
);
|
||||
}
|
||||
serverBuildPath = 'api/index.js';
|
||||
needsHandler = false;
|
||||
}
|
||||
|
||||
if (remixConfig.serverBuildPath) {
|
||||
// Explicit file path where the server output file will be
|
||||
serverBuildPath = remixConfig.serverBuildPath;
|
||||
} else if (remixConfig.serverBuildDirectory) {
|
||||
// Explicit directory path the server output will be
|
||||
serverBuildPath = join(remixConfig.serverBuildDirectory, 'index.js');
|
||||
}
|
||||
|
||||
// Also check for whether were in a monorepo.
|
||||
// If we are, prepend the app root directory from config onto the build path.
|
||||
// e.g. `/apps/my-remix-app/api/index.js`
|
||||
const isMonorepo = repoRootPath && repoRootPath !== workPath;
|
||||
if (isMonorepo) {
|
||||
const rootDirectory = relative(repoRootPath, workPath);
|
||||
serverBuildPath = join(rootDirectory, serverBuildPath);
|
||||
}
|
||||
}
|
||||
remixConfig = await readConfig(entrypointFsDirname);
|
||||
} finally {
|
||||
// Clean up our patched `remix.config.js` to be polite
|
||||
if (remixConfigPath && renamedRemixConfigPath) {
|
||||
await fs.rename(renamedRemixConfigPath, remixConfigPath);
|
||||
}
|
||||
} catch (err: any) {
|
||||
// Ignore error if `remix.config.js` does not exist
|
||||
if (err.code !== 'MODULE_NOT_FOUND') throw err;
|
||||
}
|
||||
|
||||
const { serverBuildPath, routes: remixRoutes } = remixConfig;
|
||||
|
||||
// Figure out which pages should be edge functions
|
||||
const edgePages = new Set<ConfigRoute>();
|
||||
const project = new Project();
|
||||
for (const route of Object.values(remixRoutes)) {
|
||||
const routePath = join(remixConfig.appDirectory, route.file);
|
||||
const staticConfig = getConfig(project, routePath);
|
||||
const isEdge =
|
||||
staticConfig?.runtime === 'edge' ||
|
||||
staticConfig?.runtime === 'experimental-edge';
|
||||
if (isEdge) {
|
||||
edgePages.add(route);
|
||||
}
|
||||
}
|
||||
|
||||
// This needs to happen before we run NFT to create the Node/Edge functions
|
||||
await Promise.all([
|
||||
ensureResolvable(
|
||||
entrypointFsDirname,
|
||||
repoRootPath,
|
||||
'@remix-run/server-runtime'
|
||||
),
|
||||
ensureResolvable(entrypointFsDirname, repoRootPath, '@remix-run/node'),
|
||||
]);
|
||||
|
||||
const [staticFiles, nodeFunction, edgeFunction] = await Promise.all([
|
||||
const [staticFiles, renderFunction, ssrRoutes] = await Promise.all([
|
||||
glob('**', join(entrypointFsDirname, 'public')),
|
||||
createRenderNodeFunction(
|
||||
createRenderFunction(
|
||||
entrypointFsDirname,
|
||||
repoRootPath,
|
||||
serverBuildPath,
|
||||
needsHandler,
|
||||
nodeVersion
|
||||
),
|
||||
edgePages.size > 0
|
||||
? createRenderEdgeFunction(
|
||||
entrypointFsDirname,
|
||||
repoRootPath,
|
||||
serverBuildPath
|
||||
)
|
||||
: undefined,
|
||||
getSsrRoutes(entrypointFsDirname),
|
||||
]);
|
||||
|
||||
const output: BuildResultV2Typical['output'] = staticFiles;
|
||||
const routes: any[] = [
|
||||
{
|
||||
src: '^/build/(.*)$',
|
||||
headers: { 'cache-control': 'public, max-age=31536000, immutable' },
|
||||
continue: true,
|
||||
},
|
||||
{
|
||||
handle: 'filesystem',
|
||||
},
|
||||
];
|
||||
|
||||
for (const route of Object.values(remixRoutes)) {
|
||||
// Layout routes don't get a function / route added
|
||||
const isLayoutRoute = Object.values(remixRoutes).some(
|
||||
r => r.parentId === route.id
|
||||
);
|
||||
if (isLayoutRoute) continue;
|
||||
|
||||
// Build up the full request path
|
||||
let currentRoute: ConfigRoute | undefined = route;
|
||||
const pathParts: string[] = [];
|
||||
do {
|
||||
if (currentRoute.index) pathParts.push('index');
|
||||
if (currentRoute.path) pathParts.push(currentRoute.path);
|
||||
if (currentRoute.parentId) {
|
||||
currentRoute = remixRoutes[currentRoute.parentId];
|
||||
} else {
|
||||
currentRoute = undefined;
|
||||
}
|
||||
} while (currentRoute);
|
||||
const path = join(...pathParts.reverse());
|
||||
|
||||
const isEdge = edgePages.has(route);
|
||||
const fn =
|
||||
isEdge && 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,
|
||||
name: path,
|
||||
})
|
||||
: nodeFunction;
|
||||
output[path] = fn;
|
||||
|
||||
// If this is a dynamic route then add a Vercel route
|
||||
const keys: Key[] = [];
|
||||
// Replace "/*" at the end to handle "splat routes"
|
||||
const rePath = `/${path.replace(/\/\*$/, '/:params+')}`;
|
||||
const re = pathToRegexp(rePath, keys);
|
||||
if (keys.length > 0) {
|
||||
routes.push({
|
||||
src: re.source,
|
||||
dest: path,
|
||||
});
|
||||
}
|
||||
for (const path of ssrRoutes) {
|
||||
output[path] = renderFunction;
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (!output['404']) {
|
||||
output['404'] = edgeFunction
|
||||
? new EdgeFunction({ ...edgeFunction, name: '404' })
|
||||
: nodeFunction;
|
||||
}
|
||||
routes.push({
|
||||
src: '/(.*)',
|
||||
dest: '/404',
|
||||
});
|
||||
// Add a 404 path for not found pages to be server-side rendered by Remix
|
||||
output['404'] = renderFunction;
|
||||
|
||||
return { routes, output };
|
||||
return {
|
||||
routes: [
|
||||
{
|
||||
src: '^/build/(.*)$',
|
||||
headers: { 'cache-control': 'public, max-age=31536000, immutable' },
|
||||
continue: true,
|
||||
},
|
||||
{
|
||||
handle: 'filesystem',
|
||||
},
|
||||
{
|
||||
src: '/(.*)',
|
||||
dest: '/404',
|
||||
},
|
||||
],
|
||||
output,
|
||||
};
|
||||
};
|
||||
|
||||
function hasScript(scriptName: string, pkg: PackageJson | null) {
|
||||
@@ -296,21 +251,24 @@ function hasScript(scriptName: string, pkg: PackageJson | null) {
|
||||
return typeof scripts[scriptName] === 'string';
|
||||
}
|
||||
|
||||
async function createRenderNodeFunction(
|
||||
async function createRenderFunction(
|
||||
entrypointDir: string,
|
||||
rootDir: string,
|
||||
serverBuildPath: string,
|
||||
needsHandler: boolean,
|
||||
nodeVersion: NodeVersion
|
||||
): Promise<NodejsLambda> {
|
||||
const files: Files = {};
|
||||
|
||||
const relativeServerBuildPath = relative(rootDir, serverBuildPath);
|
||||
const handler = join(dirname(relativeServerBuildPath), 'server-node.mjs');
|
||||
const handler = needsHandler
|
||||
? join(dirname(serverBuildPath), '__vc_handler.js')
|
||||
: serverBuildPath;
|
||||
const 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);
|
||||
if (needsHandler) {
|
||||
// Copy the `default-server.js` file into the "build" directory
|
||||
const sourceHandlerPath = join(__dirname, '../default-server.js');
|
||||
await fs.copyFile(sourceHandlerPath, handlerPath);
|
||||
}
|
||||
|
||||
// Trace the handler with `@vercel/nft`
|
||||
const trace = await nodeFileTrace([handlerPath], {
|
||||
@@ -326,152 +284,41 @@ async function createRenderNodeFunction(
|
||||
files[file] = await FileFsRef.fromFsPath({ fsPath: join(rootDir, file) });
|
||||
}
|
||||
|
||||
const fn = new NodejsLambda({
|
||||
const lambda = new NodejsLambda({
|
||||
files,
|
||||
handler,
|
||||
runtime: nodeVersion.runtime,
|
||||
shouldAddHelpers: false,
|
||||
shouldAddSourcemapSupport: false,
|
||||
operationType: 'SSR',
|
||||
experimentalResponseStreaming: true,
|
||||
});
|
||||
|
||||
return fn;
|
||||
return lambda;
|
||||
}
|
||||
|
||||
async function createRenderEdgeFunction(
|
||||
entrypointDir: string,
|
||||
rootDir: string,
|
||||
serverBuildPath: string
|
||||
): Promise<EdgeFunction> {
|
||||
const files: Files = {};
|
||||
|
||||
const relativeServerBuildPath = relative(rootDir, serverBuildPath);
|
||||
const handler = join(dirname(relativeServerBuildPath), 'server-edge.mjs');
|
||||
const 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);
|
||||
|
||||
// Trace the handler with `@vercel/nft`
|
||||
const trace = await nodeFileTrace([handlerPath], {
|
||||
base: rootDir,
|
||||
processCwd: entrypointDir,
|
||||
conditions: ['worker', 'browser'],
|
||||
async readFile(fsPath) {
|
||||
let source: Buffer | string;
|
||||
try {
|
||||
source = await fs.readFile(fsPath);
|
||||
} catch (err: any) {
|
||||
if (err.code === 'ENOENT' || err.code === 'EISDIR') {
|
||||
return null;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
if (basename(fsPath) === 'package.json') {
|
||||
// For Edge Functions, patch "main" field to prefer "browser" or "module"
|
||||
const pkgJson = JSON.parse(source.toString());
|
||||
for (const prop of ['browser', 'module']) {
|
||||
const val = pkgJson[prop];
|
||||
if (typeof val === 'string') {
|
||||
pkgJson.main = val;
|
||||
|
||||
// Return the modified `package.json` to nft
|
||||
source = JSON.stringify(pkgJson);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return source;
|
||||
},
|
||||
});
|
||||
|
||||
for (const warning of trace.warnings) {
|
||||
debug(`Warning from trace: ${warning.message}`);
|
||||
}
|
||||
|
||||
for (const file of trace.fileList) {
|
||||
files[file] = await FileFsRef.fromFsPath({ fsPath: join(rootDir, file) });
|
||||
}
|
||||
|
||||
const fn = new EdgeFunction({
|
||||
files,
|
||||
deploymentTarget: 'v8-worker',
|
||||
name: 'render',
|
||||
entrypoint: handler,
|
||||
});
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
async function ensureResolvable(start: string, base: string, pkgName: string) {
|
||||
try {
|
||||
const resolvedPath = _require.resolve(pkgName, { paths: [start] });
|
||||
if (!relative(base, resolvedPath).startsWith(`..${sep}`)) {
|
||||
// Resolved path is within the root of the project, so all good
|
||||
debug(`"${pkgName}" resolved to '${resolvedPath}'`);
|
||||
return;
|
||||
}
|
||||
} catch (err: any) {
|
||||
if (err.code !== 'MODULE_NOT_FOUND') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// If we got to here then `pkgName` was not resolvable up to the root
|
||||
// of the project. Try a couple symlink tricks, otherwise we'll bail.
|
||||
|
||||
// Attempt to find the package in `node_modules/.pnpm` (pnpm)
|
||||
const pnpmDir = await walkParentDirs({
|
||||
base,
|
||||
start,
|
||||
filename: 'node_modules/.pnpm',
|
||||
});
|
||||
if (pnpmDir) {
|
||||
const prefix = `${pkgName.replace('/', '+')}@`;
|
||||
const packages = await fs.readdir(pnpmDir);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to find the package in `node_modules/.store` (npm 9+ linked mode)
|
||||
const npmDir = await walkParentDirs({
|
||||
base,
|
||||
start,
|
||||
filename: 'node_modules/.store',
|
||||
});
|
||||
if (npmDir) {
|
||||
const prefix = `${basename(pkgName)}@`;
|
||||
const prefixDir = join(npmDir, dirname(pkgName));
|
||||
const packages = await fs.readdir(prefixDir);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Failed to resolve "${pkgName}". To fix this error, add "${pkgName}" to "dependencies" in your \`package.json\` file.`
|
||||
async function getSsrRoutes(entrypointDir: string): Promise<string[]> {
|
||||
// Find the name of the manifest file
|
||||
const buildDir = join(entrypointDir, 'public/build');
|
||||
const manifestFileName = (await fs.readdir(buildDir)).find(n =>
|
||||
n.startsWith('manifest-')
|
||||
);
|
||||
|
||||
if (!manifestFileName) {
|
||||
throw new Error(`Failed to find manifest file in "${buildDir}"`);
|
||||
}
|
||||
|
||||
const context: { window: { __remixManifest?: RemixBuildManifest } } = {
|
||||
window: {},
|
||||
};
|
||||
createContext(context);
|
||||
|
||||
const code = await fs.readFile(join(buildDir, manifestFileName), 'utf8');
|
||||
runInContext(code, context);
|
||||
|
||||
const routes = context.window.__remixManifest?.routes || {};
|
||||
return Object.keys(routes)
|
||||
.filter(id => id !== 'root')
|
||||
.map(id => {
|
||||
return routes[id].path || 'index';
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,23 +1,39 @@
|
||||
import { glob } from '@vercel/build-utils';
|
||||
import { dirname, join, relative } from 'path';
|
||||
import { readConfig } from '@remix-run/dev/dist/config';
|
||||
import { glob } from '@vercel/build-utils';
|
||||
import type { PrepareCache } from '@vercel/build-utils';
|
||||
import type { AppConfig } from './types';
|
||||
import { findConfig } from './utils';
|
||||
|
||||
export const prepareCache: PrepareCache = async ({
|
||||
entrypoint,
|
||||
repoRootPath,
|
||||
workPath,
|
||||
}) => {
|
||||
const root = repoRootPath || workPath;
|
||||
let cacheDirectory = '.cache';
|
||||
const mountpoint = dirname(entrypoint);
|
||||
const entrypointFsDirname = join(workPath, mountpoint);
|
||||
const remixConfig = await readConfig(entrypointFsDirname);
|
||||
try {
|
||||
const remixConfigFile = findConfig(entrypointFsDirname, 'remix.config');
|
||||
if (remixConfigFile) {
|
||||
const remixConfigModule = await eval('import(remixConfigFile)');
|
||||
const remixConfig: AppConfig = remixConfigModule?.default || {};
|
||||
if (remixConfig.cacheDirectory) {
|
||||
cacheDirectory = remixConfig.cacheDirectory;
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
// Ignore error if `remix.config.js` does not exist
|
||||
if (err.code !== 'MODULE_NOT_FOUND') throw err;
|
||||
}
|
||||
|
||||
const root = repoRootPath || workPath;
|
||||
|
||||
const [nodeModulesFiles, cacheDirFiles] = await Promise.all([
|
||||
// Cache `node_modules`
|
||||
glob('**/node_modules/**', root),
|
||||
|
||||
// Cache the Remix "cacheDirectory" (typically `.cache`)
|
||||
glob(relative(root, join(remixConfig.cacheDirectory, '**')), root),
|
||||
glob(relative(root, join(entrypointFsDirname, cacheDirectory, '**')), root),
|
||||
]);
|
||||
|
||||
return { ...nodeModulesFiles, ...cacheDirFiles };
|
||||
|
||||
15
packages/remix/src/types.ts
Normal file
15
packages/remix/src/types.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
// Stripped down version of `@remix-run/dev` AppConfig
|
||||
export interface AppConfig {
|
||||
cacheDirectory?: string;
|
||||
serverBuildDirectory?: string;
|
||||
serverBuildPath?: string;
|
||||
serverBuildTarget?: string;
|
||||
}
|
||||
|
||||
export interface RemixBuildManifest {
|
||||
routes: {
|
||||
[id: string]: {
|
||||
path: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
export const config = {
|
||||
runtime: 'edge'
|
||||
};
|
||||
|
||||
export default function Edge() {
|
||||
return (
|
||||
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
|
||||
<h1>Welcome to Remix@Edge</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -13,8 +13,8 @@
|
||||
"@remix-run/node": "^1.7.4",
|
||||
"@remix-run/react": "^1.7.4",
|
||||
"@remix-run/serve": "^1.7.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@remix-run/dev": "^1.7.4",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "Welcome to Remix" },
|
||||
{ "path": "/edge", "mustContain": "Welcome to Remix@Edge" },
|
||||
{ "path": "/b", "mustContain": "B page" },
|
||||
{ "path": "/nested", "mustContain": "Nested index page" },
|
||||
{ "path": "/nested/another", "mustContain": "Nested another page" },
|
||||
|
||||
@@ -4733,6 +4733,11 @@ normalize-url@^6.0.1:
|
||||
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
|
||||
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
|
||||
|
||||
object-assign@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||
|
||||
object-copy@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
|
||||
@@ -5120,13 +5125,14 @@ raw-body@2.5.1, raw-body@^2.2.0:
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
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==
|
||||
react-dom@^17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
|
||||
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
scheduler "^0.23.0"
|
||||
object-assign "^4.1.1"
|
||||
scheduler "^0.20.2"
|
||||
|
||||
react-router-dom@6.3.0:
|
||||
version "6.3.0"
|
||||
@@ -5143,12 +5149,13 @@ react-router@6.3.0:
|
||||
dependencies:
|
||||
history "^5.2.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==
|
||||
react@^17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
readable-stream@1.1.x:
|
||||
version "1.1.14"
|
||||
@@ -5447,12 +5454,13 @@ safe-regex@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
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==
|
||||
scheduler@^0.20.2:
|
||||
version "0.20.2"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
|
||||
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
semver@^5.6.0:
|
||||
version "5.7.1"
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
export const config = {
|
||||
runtime: 'edge'
|
||||
};
|
||||
|
||||
export default function Edge() {
|
||||
return (
|
||||
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
|
||||
<h1>Welcome to Remix@Edge</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
"@remix-run/node": "^1.7.4",
|
||||
"@remix-run/react": "^1.7.4",
|
||||
"@remix-run/serve": "^1.7.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@remix-run/dev": "^1.7.4",
|
||||
|
||||
@@ -9,8 +9,5 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "Welcome to Remix" },
|
||||
{ "path": "/edge", "mustContain": "Welcome to Remix@Edge" }
|
||||
]
|
||||
"probes": [{ "path": "/", "mustContain": "Welcome to Remix" }]
|
||||
}
|
||||
|
||||
@@ -4733,6 +4733,11 @@ normalize-url@^6.0.1:
|
||||
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
|
||||
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
|
||||
|
||||
object-assign@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||
|
||||
object-copy@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
|
||||
@@ -5120,13 +5125,14 @@ raw-body@2.5.1, raw-body@^2.2.0:
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
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==
|
||||
react-dom@^17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
|
||||
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
scheduler "^0.23.0"
|
||||
object-assign "^4.1.1"
|
||||
scheduler "^0.20.2"
|
||||
|
||||
react-router-dom@6.3.0:
|
||||
version "6.3.0"
|
||||
@@ -5143,12 +5149,13 @@ react-router@6.3.0:
|
||||
dependencies:
|
||||
history "^5.2.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==
|
||||
react@^17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
readable-stream@1.1.x:
|
||||
version "1.1.14"
|
||||
@@ -5447,12 +5454,13 @@ safe-regex@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
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==
|
||||
scheduler@^0.20.2:
|
||||
version "0.20.2"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
|
||||
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
semver@^5.6.0:
|
||||
version "5.7.1"
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
node_modules
|
||||
|
||||
/.cache
|
||||
/build
|
||||
/public/build
|
||||
.env
|
||||
@@ -1,53 +0,0 @@
|
||||
# Welcome to Remix!
|
||||
|
||||
- [Remix Docs](https://remix.run/docs)
|
||||
|
||||
## Development
|
||||
|
||||
From your terminal:
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
This starts your app in development mode, rebuilding assets on file changes.
|
||||
|
||||
## Deployment
|
||||
|
||||
First, build your app for production:
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
Then run the app in production mode:
|
||||
|
||||
```sh
|
||||
npm start
|
||||
```
|
||||
|
||||
Now you'll need to pick a host to deploy it to.
|
||||
|
||||
### DIY
|
||||
|
||||
If you're familiar with deploying node applications, the built-in Remix app server is production-ready.
|
||||
|
||||
Make sure to deploy the output of `remix build`
|
||||
|
||||
- `build/`
|
||||
- `public/build/`
|
||||
|
||||
### Using a Template
|
||||
|
||||
When you ran `npx create-remix@latest` there were a few choices for hosting. You can run that again to create a new project, then copy over your `app/` folder to the new project that's pre-configured for your target server.
|
||||
|
||||
```sh
|
||||
cd ..
|
||||
# create a new project, and pick a pre-configured host
|
||||
npx create-remix@latest
|
||||
cd my-new-remix-app
|
||||
# remove the new project's app (not the old one!)
|
||||
rm -rf app
|
||||
# copy your app over
|
||||
cp -R ../my-old-remix-app/app app
|
||||
```
|
||||
@@ -1,4 +0,0 @@
|
||||
import { RemixBrowser } from "@remix-run/react";
|
||||
import { hydrate } from "react-dom";
|
||||
|
||||
hydrate(<RemixBrowser />, document);
|
||||
@@ -1,21 +0,0 @@
|
||||
import type { EntryContext } from "@remix-run/node";
|
||||
import { RemixServer } from "@remix-run/react";
|
||||
import { renderToString } from "react-dom/server";
|
||||
|
||||
export default function handleRequest(
|
||||
request: Request,
|
||||
responseStatusCode: number,
|
||||
responseHeaders: Headers,
|
||||
remixContext: EntryContext
|
||||
) {
|
||||
let markup = renderToString(
|
||||
<RemixServer context={remixContext} url={request.url} />
|
||||
);
|
||||
|
||||
responseHeaders.set("Content-Type", "text/html");
|
||||
|
||||
return new Response("<!DOCTYPE html>" + markup, {
|
||||
status: responseStatusCode,
|
||||
headers: responseHeaders,
|
||||
});
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import type { MetaFunction } from "@remix-run/node";
|
||||
import {
|
||||
Links,
|
||||
LiveReload,
|
||||
Meta,
|
||||
Outlet,
|
||||
Scripts,
|
||||
ScrollRestoration,
|
||||
} from "@remix-run/react";
|
||||
|
||||
export const meta: MetaFunction = () => ({
|
||||
charset: "utf-8",
|
||||
title: "New Remix App",
|
||||
viewport: "width=device-width,initial-scale=1",
|
||||
});
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<Meta />
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Outlet />
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
<LiveReload />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export default function B() {
|
||||
return (
|
||||
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
|
||||
<h1>B page</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
export const config = {
|
||||
runtime: 'edge'
|
||||
};
|
||||
|
||||
export default function Edge() {
|
||||
return (
|
||||
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
|
||||
<h1>Welcome to Remix@Edge</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
export default function Index() {
|
||||
return (
|
||||
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
|
||||
<h1>Welcome to Remix</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://remix.run/tutorials/blog"
|
||||
rel="noreferrer"
|
||||
>
|
||||
15m Quickstart Blog Tutorial
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://remix.run/tutorials/jokes"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Deep Dive Jokes App Tutorial
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a target="_blank" href="https://remix.run/docs" rel="noreferrer">
|
||||
Remix Docs
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export default function Another() {
|
||||
return (
|
||||
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
|
||||
<h1>Nested another page</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export default function Nested() {
|
||||
return (
|
||||
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
|
||||
<h1>Nested index page</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"name": "remix-template-remix",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"license": "",
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"build": "remix build",
|
||||
"dev": "remix dev",
|
||||
"start": "remix-serve build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@remix-run/react": "^1.7.4",
|
||||
"@remix-run/serve": "^1.7.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@remix-run/dev": "^1.7.4",
|
||||
"@types/react": "^17.0.45",
|
||||
"@types/react-dom": "^17.0.17",
|
||||
"typescript": "^4.6.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
}
|
||||
5547
packages/remix/test/fixtures/03-with-pnpm/pnpm-lock.yaml
generated
vendored
5547
packages/remix/test/fixtures/03-with-pnpm/pnpm-lock.yaml
generated
vendored
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* @type {import('@remix-run/dev').AppConfig}
|
||||
*/
|
||||
module.exports = {
|
||||
ignoredRouteFiles: ['**/.*'],
|
||||
// appDirectory: "app",
|
||||
// assetsBuildDirectory: "public/build",
|
||||
// serverBuildPath: "build/index.js",
|
||||
// publicPath: "/build/",
|
||||
};
|
||||
@@ -1,2 +0,0 @@
|
||||
/// <reference types="@remix-run/dev" />
|
||||
/// <reference types="@remix-run/node/globals" />
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2019"],
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react-jsx",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"target": "ES2019",
|
||||
"strict": true,
|
||||
"allowJs": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./app/*"]
|
||||
},
|
||||
|
||||
// Remix takes care of building everything in `remix build`.
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/remix",
|
||||
"config": {
|
||||
"zeroConfig": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "Welcome to Remix" },
|
||||
{ "path": "/edge", "mustContain": "Welcome to Remix@Edge" },
|
||||
{ "path": "/b", "mustContain": "B page" },
|
||||
{ "path": "/nested", "mustContain": "Nested index page" },
|
||||
{ "path": "/nested/another", "mustContain": "Nested another page" },
|
||||
{ "path": "/nested/index", "mustContain": "Not Found" },
|
||||
{ "path": "/asdf", "mustContain": "Not Found" }
|
||||
]
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
node_modules
|
||||
|
||||
/.cache
|
||||
/build
|
||||
/public/build
|
||||
.env
|
||||
@@ -1,53 +0,0 @@
|
||||
# Welcome to Remix!
|
||||
|
||||
- [Remix Docs](https://remix.run/docs)
|
||||
|
||||
## Development
|
||||
|
||||
From your terminal:
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
This starts your app in development mode, rebuilding assets on file changes.
|
||||
|
||||
## Deployment
|
||||
|
||||
First, build your app for production:
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
Then run the app in production mode:
|
||||
|
||||
```sh
|
||||
npm start
|
||||
```
|
||||
|
||||
Now you'll need to pick a host to deploy it to.
|
||||
|
||||
### DIY
|
||||
|
||||
If you're familiar with deploying node applications, the built-in Remix app server is production-ready.
|
||||
|
||||
Make sure to deploy the output of `remix build`
|
||||
|
||||
- `build/`
|
||||
- `public/build/`
|
||||
|
||||
### Using a Template
|
||||
|
||||
When you ran `npx create-remix@latest` there were a few choices for hosting. You can run that again to create a new project, then copy over your `app/` folder to the new project that's pre-configured for your target server.
|
||||
|
||||
```sh
|
||||
cd ..
|
||||
# create a new project, and pick a pre-configured host
|
||||
npx create-remix@latest
|
||||
cd my-new-remix-app
|
||||
# remove the new project's app (not the old one!)
|
||||
rm -rf app
|
||||
# copy your app over
|
||||
cp -R ../my-old-remix-app/app app
|
||||
```
|
||||
@@ -1,4 +0,0 @@
|
||||
import { RemixBrowser } from "@remix-run/react";
|
||||
import { hydrate } from "react-dom";
|
||||
|
||||
hydrate(<RemixBrowser />, document);
|
||||
@@ -1,21 +0,0 @@
|
||||
import type { EntryContext } from "@remix-run/node";
|
||||
import { RemixServer } from "@remix-run/react";
|
||||
import { renderToString } from "react-dom/server";
|
||||
|
||||
export default function handleRequest(
|
||||
request: Request,
|
||||
responseStatusCode: number,
|
||||
responseHeaders: Headers,
|
||||
remixContext: EntryContext
|
||||
) {
|
||||
let markup = renderToString(
|
||||
<RemixServer context={remixContext} url={request.url} />
|
||||
);
|
||||
|
||||
responseHeaders.set("Content-Type", "text/html");
|
||||
|
||||
return new Response("<!DOCTYPE html>" + markup, {
|
||||
status: responseStatusCode,
|
||||
headers: responseHeaders,
|
||||
});
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import type { MetaFunction } from "@remix-run/node";
|
||||
import {
|
||||
Links,
|
||||
LiveReload,
|
||||
Meta,
|
||||
Outlet,
|
||||
Scripts,
|
||||
ScrollRestoration,
|
||||
} from "@remix-run/react";
|
||||
|
||||
export const meta: MetaFunction = () => ({
|
||||
charset: "utf-8",
|
||||
title: "New Remix App",
|
||||
viewport: "width=device-width,initial-scale=1",
|
||||
});
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<Meta />
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Outlet />
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
<LiveReload />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export default function B() {
|
||||
return (
|
||||
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
|
||||
<h1>B page</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
export const config = {
|
||||
runtime: 'edge'
|
||||
};
|
||||
|
||||
export default function Edge() {
|
||||
return (
|
||||
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
|
||||
<h1>Welcome to Remix@Edge</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
export default function Index() {
|
||||
return (
|
||||
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
|
||||
<h1>Welcome to Remix</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://remix.run/tutorials/blog"
|
||||
rel="noreferrer"
|
||||
>
|
||||
15m Quickstart Blog Tutorial
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://remix.run/tutorials/jokes"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Deep Dive Jokes App Tutorial
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a target="_blank" href="https://remix.run/docs" rel="noreferrer">
|
||||
Remix Docs
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export default function Another() {
|
||||
return (
|
||||
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
|
||||
<h1>Nested another page</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export default function Nested() {
|
||||
return (
|
||||
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
|
||||
<h1>Nested index page</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
9390
packages/remix/test/fixtures/04-with-npm9-linked/package-lock.json
generated
vendored
9390
packages/remix/test/fixtures/04-with-npm9-linked/package-lock.json
generated
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"name": "remix-with-npm9",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"license": "",
|
||||
"sideEffects": false,
|
||||
"packageManager": "npm@9.4.1",
|
||||
"scripts": {
|
||||
"build": "remix build",
|
||||
"dev": "remix dev",
|
||||
"start": "remix-serve build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@remix-run/react": "^1.7.4",
|
||||
"@remix-run/serve": "^1.7.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@remix-run/dev": "^1.7.4",
|
||||
"@types/react": "^17.0.45",
|
||||
"@types/react-dom": "^17.0.17",
|
||||
"typescript": "^4.6.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* @type {import('@remix-run/dev').AppConfig}
|
||||
*/
|
||||
module.exports = {
|
||||
ignoredRouteFiles: ['**/.*'],
|
||||
// appDirectory: "app",
|
||||
// assetsBuildDirectory: "public/build",
|
||||
// serverBuildPath: "build/index.js",
|
||||
// publicPath: "/build/",
|
||||
};
|
||||
@@ -1,2 +0,0 @@
|
||||
/// <reference types="@remix-run/dev" />
|
||||
/// <reference types="@remix-run/node/globals" />
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2019"],
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react-jsx",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"target": "ES2019",
|
||||
"strict": true,
|
||||
"allowJs": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./app/*"]
|
||||
},
|
||||
|
||||
// Remix takes care of building everything in `remix build`.
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/remix",
|
||||
"config": {
|
||||
"installCommand": "env | sort && npm install --install-strategy=linked",
|
||||
"zeroConfig": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"build": {
|
||||
"env": {
|
||||
"ENABLE_EXPERIMENTAL_COREPACK": "1"
|
||||
}
|
||||
},
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "Welcome to Remix" },
|
||||
{ "path": "/edge", "mustContain": "Welcome to Remix@Edge" },
|
||||
{ "path": "/b", "mustContain": "B page" },
|
||||
{ "path": "/nested", "mustContain": "Nested index page" },
|
||||
{ "path": "/nested/another", "mustContain": "Nested another page" },
|
||||
{ "path": "/nested/index", "mustContain": "Not Found" },
|
||||
{ "path": "/asdf", "mustContain": "Not Found" }
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@vercel/ruby",
|
||||
"author": "Nathan Cahill <nathan@nathancahill.com>",
|
||||
"version": "1.3.63",
|
||||
"version": "1.3.62",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
|
||||
@@ -17,12 +17,12 @@
|
||||
"scripts": {
|
||||
"build": "node build",
|
||||
"test": "jest --env node --verbose --runInBand --bail",
|
||||
"test-e2e": "pnpm test"
|
||||
"test-integration-once": "pnpm test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "8.0.0",
|
||||
"@types/semver": "6.0.0",
|
||||
"@vercel/build-utils": "6.2.3",
|
||||
"@vercel/build-utils": "6.2.2",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"execa": "2.0.4",
|
||||
"fs-extra": "^7.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/static-build",
|
||||
"version": "1.3.7",
|
||||
"version": "1.3.6",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/build-step",
|
||||
@@ -16,7 +16,7 @@
|
||||
"build": "node build",
|
||||
"test": "jest --env node --verbose --bail --runInBand",
|
||||
"test-unit": "pnpm test test/build.test.ts test/gatsby.test.ts test/prepare-cache.test.ts",
|
||||
"test-e2e": "pnpm test test/integration-*.test.js"
|
||||
"test-integration-once": "pnpm test test/integration-*.test.js"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "ts-jest/presets/default",
|
||||
@@ -30,7 +30,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/gatsby-plugin-vercel-analytics": "1.0.7",
|
||||
"@vercel/gatsby-plugin-vercel-builder": "1.1.5"
|
||||
"@vercel/gatsby-plugin-vercel-builder": "1.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/aws-lambda": "8.10.64",
|
||||
@@ -42,9 +42,9 @@
|
||||
"@types/node-fetch": "2.5.4",
|
||||
"@types/promise-timeout": "1.3.0",
|
||||
"@types/semver": "7.3.13",
|
||||
"@vercel/build-utils": "6.2.3",
|
||||
"@vercel/build-utils": "6.2.2",
|
||||
"@vercel/frameworks": "1.3.0",
|
||||
"@vercel/fs-detectors": "3.7.12",
|
||||
"@vercel/fs-detectors": "3.7.11",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/routing-utils": "2.1.8",
|
||||
"@vercel/static-config": "2.0.12",
|
||||
|
||||
2301
pnpm-lock.yaml
generated
2301
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -238,7 +238,7 @@ async function runProbe(probe, deploymentId, deploymentUrl, ctx) {
|
||||
.join('\n');
|
||||
|
||||
throw new Error(
|
||||
`Page ${probeUrl} does not have expected response header ${header}.\n\nExpected: ${expected}.\n\nActual: ${headers}`
|
||||
`Page ${probeUrl} does not have header ${header}.\n\nExpected: ${expected}.\nActual: ${headers}`
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -254,7 +254,7 @@ async function runProbe(probe, deploymentId, deploymentUrl, ctx) {
|
||||
.join('\n');
|
||||
|
||||
throw new Error(
|
||||
`Page ${probeUrl} has unexpected response header ${header}.\n\nDid not expect: ${header}=${expected}.\n\nAll: ${headers}`
|
||||
`Page ${probeUrl} invalid page header ${header}.\n\n Did not expect: ${header}=${expected}.\nBut got ${headers}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -35,15 +35,15 @@
|
||||
"dependsOn": ["build"],
|
||||
"outputMode": "new-only"
|
||||
},
|
||||
"test-dev": {
|
||||
"test-integration-dev": {
|
||||
"dependsOn": ["build"],
|
||||
"outputMode": "new-only"
|
||||
},
|
||||
"test-cli": {
|
||||
"test-integration-cli": {
|
||||
"dependsOn": ["build"],
|
||||
"outputMode": "new-only"
|
||||
},
|
||||
"test-e2e": {
|
||||
"test-integration-once": {
|
||||
"dependsOn": ["build"],
|
||||
"outputMode": "new-only"
|
||||
},
|
||||
|
||||
4
utils/chunk-tests.js
vendored
4
utils/chunk-tests.js
vendored
@@ -4,9 +4,9 @@ const path = require('path');
|
||||
const NUMBER_OF_CHUNKS = 5;
|
||||
const MINIMUM_PER_CHUNK = 1;
|
||||
const runnersMap = new Map([
|
||||
['test-e2e', ['ubuntu-latest']],
|
||||
['test-integration-once', ['ubuntu-latest']],
|
||||
['test-next-local', ['ubuntu-latest']],
|
||||
['test-dev', ['ubuntu-latest', 'macos-latest']],
|
||||
['test-integration-dev', ['ubuntu-latest', 'macos-latest']],
|
||||
]);
|
||||
|
||||
async function getChunkedTests() {
|
||||
|
||||
Reference in New Issue
Block a user