Compare commits

..

15 Commits

Author SHA1 Message Date
Ethan Arrowood
92f5b6e0c9 Publish Stable
- vercel@28.13.0
 - @vercel/gatsby-plugin-vercel-builder@0.1.2
 - @vercel/next@3.3.18
 - @vercel/node@2.8.15
 - @vercel/static-build@1.2.0
2023-01-20 10:27:31 -07:00
JJ Kasper
ed6ce1149a [next] Ensure outputDirectory is normalized (#9269)
This ensures we normalize an absolute `outputDirectory` passed into the Next.js builder as this can cause incorrect resolving. We should probably normalize this higher up as well but this is a starting point. 

Fixes: [slack thread](https://vercel.slack.com/archives/C0289CGVAR2/p1674176040821579?thread_ts=1674168748.257019&cid=C0289CGVAR2)
2023-01-20 17:08:32 +00:00
Ethan Arrowood
fc3611fb80 [static-build] add gatsby v5 fixture (#9267)
Adds a gatsby v5 fixture using ssr and dsg to demonstrate plugin works

Co-authored-by: Nathan Rajlich <n@n8.io>
2023-01-20 03:02:26 -08:00
Nathan Rajlich
ed33c2b27c [gatsby-plugin-vercel-builder] Remove some leftover console.log calls (#9265) 2023-01-19 16:05:48 -08:00
Nathan Rajlich
a7a5bf1a12 [tests] Pass in builder to runBuildLambda() (#9262)
Removes the need for `next`/`static-build` to be present in the root `package.json` file.
2023-01-19 15:10:06 -08:00
JJ Kasper
cc687b3880 [next] Correct RSC route for app dir with trailingSlash (#9263) 2023-01-19 15:03:58 -08:00
Nathan Rajlich
053ec92d5f [node] Add build logs probe for TypeScript usage messages (#9261)
Follow-up to https://github.com/vercel/vercel/pull/9258, even though that issue seemed to only happen when linked to the monorepo locally. In any case, this test will ensure those log messages are intact for any other change around that part of the codebase in the future.
2023-01-19 21:39:27 +00:00
Ethan Arrowood
4838dc336a [static-build] run pnpm install --lockfile-only after injecting gatsby plugins (#9259)
Fixes a bug where after injecting plugins and modifying the users package.json, pnpm would fail normal install
2023-01-19 20:07:53 +00:00
Nathan Rajlich
eae45f4019 [node] Fix TypeScript built-in compiler log message check (#9258)
This check started breaking probably after the pnpm switch, so switch to a simpler solution that doesn't require comparing the paths.
2023-01-19 13:12:31 +00:00
Ethan Arrowood
02feb564a7 [static-build][fs-detectors] add gatsby builder plugin injection (#9252)
Modifies the `@vercel/static-build` injection logic in order to now inject both the analytics and builder gatsby plugins.
2023-01-19 03:34:47 +00:00
Nathan Rajlich
e174a06673 Publish Stable
- vercel@28.12.8
 - @vercel/gatsby-plugin-vercel-builder@0.1.1
 - @vercel/next@3.3.17
 - @vercel/remix@1.2.7
2023-01-18 15:19:12 -08:00
Nathan Rajlich
de034943af [gatsby-plugin-vercel-builder] Fix turbo cache outputs configuration (#9257)
The published version on npm was missing the compiled code because Turbo
was not configured to cache them properly.
2023-01-18 15:10:28 -08:00
JJ Kasper
b3862271a5 [next] Fix normalize route with middleware and basePath (#9255)
This ensures we properly match the index route when normalizing routes with `x-nextjs-data` for middleware. 

x-ref: [slack thread](https://vercel.slack.com/archives/C03S8ED1DKM/p1674034533265989)
2023-01-18 21:30:30 +00:00
Steven
aaceeef604 [tests] Add test for ./examples/remix (#9251)
- Related to #9249
2023-01-18 19:13:55 +00:00
Steven
ad107ecf79 [tests] Split more dev tests (#9230)
This PR attempts to balance the tests so they run concurrently and therefore faster.

I also sorted the tests so they are deterministic when splitting/chunking.
2023-01-18 17:35:45 +00:00
178 changed files with 41367 additions and 568 deletions

View File

@@ -43,5 +43,8 @@ packages/static-build/test/cache-fixtures
# redwood
packages/redwood/test/fixtures
# remix
packages/remix/test/fixtures
# gatsby-plugin-vercel-analytics
packages/gatsby-plugin-vercel-analytics

View File

@@ -13,7 +13,6 @@
"@typescript-eslint/parser": "5.21.0",
"@vercel/build-utils": "*",
"@vercel/ncc": "0.24.0",
"@vercel/next": "*",
"async-retry": "1.2.3",
"buffer-replace": "1.0.0",
"create-svelte": "2.0.1",

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "28.12.7",
"version": "28.13.0",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -44,13 +44,13 @@
"@vercel/build-utils": "5.9.0",
"@vercel/go": "2.2.30",
"@vercel/hydrogen": "0.0.44",
"@vercel/next": "3.3.16",
"@vercel/node": "2.8.14",
"@vercel/next": "3.3.18",
"@vercel/node": "2.8.15",
"@vercel/python": "3.1.40",
"@vercel/redwood": "1.0.51",
"@vercel/remix": "1.2.6",
"@vercel/remix": "1.2.7",
"@vercel/ruby": "1.3.56",
"@vercel/static-build": "1.1.7"
"@vercel/static-build": "1.2.0"
},
"devDependencies": {
"@alex_neo/jest-expect-message": "1.0.5",

View File

@@ -1,3 +1,4 @@
import { isIP } from 'net';
const { exec, fixture, testFixture, testFixtureStdio } = require('./utils.js');
test('[vercel dev] validate redirects', async () => {
@@ -110,3 +111,114 @@ test(
});
})
);
test(
'[vercel dev] Use `@vercel/python` with Flask requirements.txt',
testFixtureStdio('python-flask', async (testPath: any) => {
const name = 'Alice';
const year = new Date().getFullYear();
await testPath(200, `/api/user?name=${name}`, new RegExp(`Hello ${name}`));
await testPath(200, `/api/date`, new RegExp(`Current date is ${year}`));
await testPath(200, `/api/date.py`, new RegExp(`Current date is ${year}`));
await testPath(200, `/api/headers`, (body: any, res: any) => {
// @ts-ignore
const { host } = new URL(res.url);
expect(body).toBe(host);
});
})
);
test(
'[vercel dev] Use custom runtime from the "functions" property',
testFixtureStdio('custom-runtime', async (testPath: any) => {
await testPath(200, `/api/user`, /Hello, from Bash!/m);
await testPath(200, `/api/user.sh`, /Hello, from Bash!/m);
})
);
test(
'[vercel dev] Should work with nested `tsconfig.json` files',
testFixtureStdio('nested-tsconfig', async (testPath: any) => {
await testPath(200, `/`, /Nested tsconfig.json test page/);
await testPath(200, `/api`, 'Nested `tsconfig.json` API endpoint');
})
);
test(
'[vercel dev] Should force `tsc` option "module: commonjs" for `startDevServer()`',
testFixtureStdio('force-module-commonjs', async (testPath: any) => {
await testPath(200, `/`, /Force &quot;module: commonjs&quot; test page/);
await testPath(
200,
`/api`,
'Force "module: commonjs" JavaScript with ES Modules API endpoint'
);
await testPath(
200,
`/api/ts`,
'Force "module: commonjs" TypeScript API endpoint'
);
})
);
test(
'[vercel dev] should prioritize index.html over other file named index.*',
testFixtureStdio('index-html-priority', async (testPath: any) => {
await testPath(200, '/', 'This is index.html');
await testPath(200, '/index.css', 'This is index.css');
})
);
test(
'[vercel dev] Should support `*.go` API serverless functions',
testFixtureStdio('go', async (testPath: any) => {
await testPath(200, `/api`, 'This is the index page');
await testPath(200, `/api/index`, 'This is the index page');
await testPath(200, `/api/index.go`, 'This is the index page');
await testPath(200, `/api/another`, 'This is another page');
await testPath(200, '/api/another.go', 'This is another page');
await testPath(200, `/api/foo`, 'Req Path: /api/foo');
await testPath(200, `/api/bar`, 'Req Path: /api/bar');
})
);
test(
'[vercel dev] Should set the `ts-node` "target" to match Node.js version',
testFixtureStdio('node-ts-node-target', async (testPath: any) => {
await testPath(200, `/api/subclass`, '{"ok":true}');
await testPath(
200,
`/api/array`,
'{"months":[1,2,3,4,5,6,7,8,9,10,11,12]}'
);
await testPath(200, `/api/dump`, (body: any, res: any, isDev: any) => {
// @ts-ignore
const { host } = new URL(res.url);
const { env, headers } = JSON.parse(body);
// Test that the API endpoint receives the Vercel proxy request headers
expect(headers['x-forwarded-host']).toBe(host);
expect(headers['x-vercel-deployment-url']).toBe(host);
expect(isIP(headers['x-real-ip'])).toBeTruthy();
expect(isIP(headers['x-forwarded-for'])).toBeTruthy();
expect(isIP(headers['x-vercel-forwarded-for'])).toBeTruthy();
// Test that the API endpoint has the Vercel platform env vars defined.
expect(env.NOW_REGION).toMatch(/^[a-z]{3}\d$/);
if (isDev) {
// Only dev is tested because in production these are opt-in.
expect(env.VERCEL_URL).toBe(host);
expect(env.VERCEL_REGION).toBe('dev1');
}
});
})
);
test(
'[vercel dev] Do not fail if `src` is missing',
testFixtureStdio('missing-src-property', async (testPath: any) => {
await testPath(200, '/', /hello:index.txt/m);
await testPath(404, '/i-do-not-exist');
})
);

View File

@@ -1,196 +1,15 @@
import ms from 'ms';
import fs from 'fs-extra';
import { isIP } from 'net';
import { join } from 'path';
import { Response } from 'node-fetch';
const {
fetch,
sleep,
fixture,
testFixture,
testFixtureStdio,
validateResponseHeaders,
} = require('./utils.js');
test(
'[vercel dev] temporary directory listing',
testFixtureStdio(
'temporary-directory-listing',
async (_testPath: any, port: any) => {
const directory = fixture('temporary-directory-listing');
await fs.unlink(join(directory, 'index.txt')).catch(() => null);
await sleep(ms('20s'));
const firstResponse = await fetch(`http://localhost:${port}`);
validateResponseHeaders(firstResponse);
const body = await firstResponse.text();
console.log(body);
expect(firstResponse.status).toBe(404);
await fs.writeFile(join(directory, 'index.txt'), 'hello');
for (let i = 0; i < 20; i++) {
const response = await fetch(`http://localhost:${port}`);
validateResponseHeaders(response);
if (response.status === 200) {
const body = await response.text();
expect(body).toBe('hello');
}
await sleep(ms('1s'));
}
},
{ skipDeploy: true }
)
);
test('[vercel dev] add a `package.json` to trigger `@vercel/static-build`', async () => {
const directory = fixture('trigger-static-build');
await fs.unlink(join(directory, 'package.json')).catch(() => null);
await fs.unlink(join(directory, 'public', 'index.txt')).catch(() => null);
await fs.rmdir(join(directory, 'public')).catch(() => null);
const tester = testFixtureStdio(
'trigger-static-build',
async (_testPath: any, port: any) => {
{
const response = await fetch(`http://localhost:${port}`);
validateResponseHeaders(response);
const body = await response.text();
expect(body.trim()).toBe('hello:index.txt');
}
const rnd = Math.random().toString();
const pkg = {
private: true,
scripts: { build: `mkdir -p public && echo ${rnd} > public/index.txt` },
};
await fs.writeFile(join(directory, 'package.json'), JSON.stringify(pkg));
// Wait until file events have been processed
await sleep(ms('2s'));
{
const response = await fetch(`http://localhost:${port}`);
validateResponseHeaders(response);
const body = await response.text();
expect(body.trim()).toBe(rnd);
}
},
{ skipDeploy: true }
);
await tester();
});
test('[vercel dev] no build matches warning', async () => {
const directory = fixture('no-build-matches');
const { dev } = await testFixture(directory, {
stdio: ['ignore', 'pipe', 'pipe'],
});
try {
// start `vercel dev` detached in child_process
dev.unref();
dev.stderr.setEncoding('utf8');
await new Promise<void>(resolve => {
dev.stderr.on('data', (str: string) => {
if (str.includes('did not match any source files')) {
resolve();
}
});
});
} finally {
await dev.kill();
}
});
test(
'[vercel dev] do not recursivly check the path',
testFixtureStdio('handle-filesystem-missing', async (testPath: any) => {
await testPath(200, '/', /hello/m);
await testPath(404, '/favicon.txt');
})
);
test('[vercel dev] render warning for empty cwd dir', async () => {
const directory = fixture('empty');
const { dev, port } = await testFixture(directory, {
stdio: ['ignore', 'pipe', 'pipe'],
});
try {
dev.unref();
// Monitor `stderr` for the warning
dev.stderr.setEncoding('utf8');
const msg = 'There are no files inside your deployment.';
await new Promise<void>(resolve => {
dev.stderr.on('data', (str: string) => {
if (str.includes(msg)) {
resolve();
}
});
});
// Issue a request to ensure a 404 response
await sleep(ms('3s'));
const response = await fetch(`http://localhost:${port}`);
validateResponseHeaders(response);
expect(response.status).toBe(404);
} finally {
await dev.kill();
}
});
test('[vercel dev] do not rebuild for changes in the output directory', async () => {
const directory = fixture('output-is-source');
const { dev, port } = await testFixture(directory, {
stdio: ['ignore', 'pipe', 'pipe'],
});
try {
dev.unref();
let stderr: any = [];
const start = Date.now();
dev.stderr.on('data', (str: any) => stderr.push(str));
while (stderr.join('').includes('Ready') === false) {
await sleep(ms('3s'));
if (Date.now() - start > ms('30s')) {
console.log('stderr:', stderr.join(''));
break;
}
}
const resp1 = await fetch(`http://localhost:${port}`);
const text1 = await resp1.text();
expect(text1.trim()).toBe('hello first');
await fs.writeFile(join(directory, 'public', 'index.html'), 'hello second');
await sleep(ms('3s'));
const resp2 = await fetch(`http://localhost:${port}`);
const text2 = await resp2.text();
expect(text2.trim()).toBe('hello second');
} finally {
await dev.kill();
}
});
test(
'[vercel dev] 25-nextjs-src-dir',
testFixtureStdio('25-nextjs-src-dir', async (testPath: any) => {
@@ -324,117 +143,6 @@ test(
})
);
test(
'[vercel dev] Use `@vercel/python` with Flask requirements.txt',
testFixtureStdio('python-flask', async (testPath: any) => {
const name = 'Alice';
const year = new Date().getFullYear();
await testPath(200, `/api/user?name=${name}`, new RegExp(`Hello ${name}`));
await testPath(200, `/api/date`, new RegExp(`Current date is ${year}`));
await testPath(200, `/api/date.py`, new RegExp(`Current date is ${year}`));
await testPath(200, `/api/headers`, (body: any, res: any) => {
// @ts-ignore
const { host } = new URL(res.url);
expect(body).toBe(host);
});
})
);
test(
'[vercel dev] Use custom runtime from the "functions" property',
testFixtureStdio('custom-runtime', async (testPath: any) => {
await testPath(200, `/api/user`, /Hello, from Bash!/m);
await testPath(200, `/api/user.sh`, /Hello, from Bash!/m);
})
);
test(
'[vercel dev] Should work with nested `tsconfig.json` files',
testFixtureStdio('nested-tsconfig', async (testPath: any) => {
await testPath(200, `/`, /Nested tsconfig.json test page/);
await testPath(200, `/api`, 'Nested `tsconfig.json` API endpoint');
})
);
test(
'[vercel dev] Should force `tsc` option "module: commonjs" for `startDevServer()`',
testFixtureStdio('force-module-commonjs', async (testPath: any) => {
await testPath(200, `/`, /Force &quot;module: commonjs&quot; test page/);
await testPath(
200,
`/api`,
'Force "module: commonjs" JavaScript with ES Modules API endpoint'
);
await testPath(
200,
`/api/ts`,
'Force "module: commonjs" TypeScript API endpoint'
);
})
);
test(
'[vercel dev] should prioritize index.html over other file named index.*',
testFixtureStdio('index-html-priority', async (testPath: any) => {
await testPath(200, '/', 'This is index.html');
await testPath(200, '/index.css', 'This is index.css');
})
);
test(
'[vercel dev] Should support `*.go` API serverless functions',
testFixtureStdio('go', async (testPath: any) => {
await testPath(200, `/api`, 'This is the index page');
await testPath(200, `/api/index`, 'This is the index page');
await testPath(200, `/api/index.go`, 'This is the index page');
await testPath(200, `/api/another`, 'This is another page');
await testPath(200, '/api/another.go', 'This is another page');
await testPath(200, `/api/foo`, 'Req Path: /api/foo');
await testPath(200, `/api/bar`, 'Req Path: /api/bar');
})
);
test(
'[vercel dev] Should set the `ts-node` "target" to match Node.js version',
testFixtureStdio('node-ts-node-target', async (testPath: any) => {
await testPath(200, `/api/subclass`, '{"ok":true}');
await testPath(
200,
`/api/array`,
'{"months":[1,2,3,4,5,6,7,8,9,10,11,12]}'
);
await testPath(200, `/api/dump`, (body: any, res: any, isDev: any) => {
// @ts-ignore
const { host } = new URL(res.url);
const { env, headers } = JSON.parse(body);
// Test that the API endpoint receives the Vercel proxy request headers
expect(headers['x-forwarded-host']).toBe(host);
expect(headers['x-vercel-deployment-url']).toBe(host);
expect(isIP(headers['x-real-ip'])).toBeTruthy();
expect(isIP(headers['x-forwarded-for'])).toBeTruthy();
expect(isIP(headers['x-vercel-forwarded-for'])).toBeTruthy();
// Test that the API endpoint has the Vercel platform env vars defined.
expect(env.NOW_REGION).toMatch(/^[a-z]{3}\d$/);
if (isDev) {
// Only dev is tested because in production these are opt-in.
expect(env.VERCEL_URL).toBe(host);
expect(env.VERCEL_REGION).toBe('dev1');
}
});
})
);
test(
'[vercel dev] Do not fail if `src` is missing',
testFixtureStdio('missing-src-property', async (testPath: any) => {
await testPath(200, '/', /hello:index.txt/m);
await testPath(404, '/i-do-not-exist');
})
);
test(
'[vercel dev] Middleware that returns a 200 response',
testFixtureStdio('middleware-response', async (testPath: any) => {

View File

@@ -6,10 +6,189 @@ const {
fetch,
fixture,
sleep,
testFixture,
testFixtureStdio,
validateResponseHeaders,
} = require('./utils.js');
test(
'[vercel dev] temporary directory listing',
testFixtureStdio(
'temporary-directory-listing',
async (_testPath: any, port: any) => {
const directory = fixture('temporary-directory-listing');
await fs.unlink(join(directory, 'index.txt')).catch(() => null);
await sleep(ms('20s'));
const firstResponse = await fetch(`http://localhost:${port}`);
validateResponseHeaders(firstResponse);
const body = await firstResponse.text();
console.log(body);
expect(firstResponse.status).toBe(404);
await fs.writeFile(join(directory, 'index.txt'), 'hello');
for (let i = 0; i < 20; i++) {
const response = await fetch(`http://localhost:${port}`);
validateResponseHeaders(response);
if (response.status === 200) {
const body = await response.text();
expect(body).toBe('hello');
}
await sleep(ms('1s'));
}
},
{ skipDeploy: true }
)
);
test('[vercel dev] add a `package.json` to trigger `@vercel/static-build`', async () => {
const directory = fixture('trigger-static-build');
await fs.unlink(join(directory, 'package.json')).catch(() => null);
await fs.unlink(join(directory, 'public', 'index.txt')).catch(() => null);
await fs.rmdir(join(directory, 'public')).catch(() => null);
const tester = testFixtureStdio(
'trigger-static-build',
async (_testPath: any, port: any) => {
{
const response = await fetch(`http://localhost:${port}`);
validateResponseHeaders(response);
const body = await response.text();
expect(body.trim()).toBe('hello:index.txt');
}
const rnd = Math.random().toString();
const pkg = {
private: true,
scripts: { build: `mkdir -p public && echo ${rnd} > public/index.txt` },
};
await fs.writeFile(join(directory, 'package.json'), JSON.stringify(pkg));
// Wait until file events have been processed
await sleep(ms('2s'));
{
const response = await fetch(`http://localhost:${port}`);
validateResponseHeaders(response);
const body = await response.text();
expect(body.trim()).toBe(rnd);
}
},
{ skipDeploy: true }
);
await tester();
});
test('[vercel dev] no build matches warning', async () => {
const directory = fixture('no-build-matches');
const { dev } = await testFixture(directory, {
stdio: ['ignore', 'pipe', 'pipe'],
});
try {
// start `vercel dev` detached in child_process
dev.unref();
dev.stderr.setEncoding('utf8');
await new Promise<void>(resolve => {
dev.stderr.on('data', (str: string) => {
if (str.includes('did not match any source files')) {
resolve();
}
});
});
} finally {
await dev.kill();
}
});
test(
'[vercel dev] do not recursivly check the path',
testFixtureStdio('handle-filesystem-missing', async (testPath: any) => {
await testPath(200, '/', /hello/m);
await testPath(404, '/favicon.txt');
})
);
test('[vercel dev] render warning for empty cwd dir', async () => {
const directory = fixture('empty');
const { dev, port } = await testFixture(directory, {
stdio: ['ignore', 'pipe', 'pipe'],
});
try {
dev.unref();
// Monitor `stderr` for the warning
dev.stderr.setEncoding('utf8');
const msg = 'There are no files inside your deployment.';
await new Promise<void>(resolve => {
dev.stderr.on('data', (str: string) => {
if (str.includes(msg)) {
resolve();
}
});
});
// Issue a request to ensure a 404 response
await sleep(ms('3s'));
const response = await fetch(`http://localhost:${port}`);
validateResponseHeaders(response);
expect(response.status).toBe(404);
} finally {
await dev.kill();
}
});
test('[vercel dev] do not rebuild for changes in the output directory', async () => {
const directory = fixture('output-is-source');
const { dev, port } = await testFixture(directory, {
stdio: ['ignore', 'pipe', 'pipe'],
});
try {
dev.unref();
let stderr: any = [];
const start = Date.now();
dev.stderr.on('data', (str: any) => stderr.push(str));
while (stderr.join('').includes('Ready') === false) {
await sleep(ms('3s'));
if (Date.now() - start > ms('30s')) {
console.log('stderr:', stderr.join(''));
break;
}
}
const resp1 = await fetch(`http://localhost:${port}`);
const text1 = await resp1.text();
expect(text1.trim()).toBe('hello first');
await fs.writeFile(join(directory, 'public', 'index.html'), 'hello second');
await sleep(ms('3s'));
const resp2 = await fetch(`http://localhost:${port}`);
const text2 = await resp2.text();
expect(text2.trim()).toBe('hello second');
} finally {
await dev.kill();
}
});
test(
'[vercel dev] test cleanUrls serve correct content',
testFixtureStdio('test-clean-urls', async (testPath: any) => {

View File

@@ -1,4 +1,4 @@
gatsby-node.*
!gatsby-node.ts
build
dist

View File

@@ -2,8 +2,8 @@ import path from 'path';
import type { GatsbyNode } from 'gatsby';
// this gets built separately, so import from "build" instead of "src"
import { generateVercelBuildOutputAPI3Output } from './build';
// this gets built separately, so import from "dist" instead of "src"
import { generateVercelBuildOutputAPI3Output } from './dist';
export const pluginOptionsSchema: GatsbyNode['pluginOptionsSchema'] = ({
Joi,

View File

@@ -1,9 +1,9 @@
{
"name": "@vercel/gatsby-plugin-vercel-builder",
"version": "0.1.0",
"main": "build/index.js",
"version": "0.1.2",
"main": "dist/index.js",
"files": [
"build",
"dist",
"gatsby-node.ts",
"gatsby-node.js",
"gatsby-node.js.map"
@@ -15,7 +15,7 @@
},
"dependencies": {
"@vercel/build-utils": "5.9.0",
"@vercel/node": "2.8.14",
"@vercel/node": "2.8.15",
"@vercel/routing-utils": "2.1.8",
"ajv": "8.12.0",
"esbuild": "0.16.17",

View File

@@ -31,8 +31,6 @@ export async function generateVercelBuildOutputAPI3Output({
functions: gatsbyStoreState.functions,
};
console.log(state);
if (validateGatsbyState(state)) {
console.log('▲ Creating Vercel build output');
await remove(join('.vercel', 'output'));
@@ -96,8 +94,6 @@ export async function generateVercelBuildOutputAPI3Output({
routes: routes || undefined,
};
console.log(config);
await writeJson(exportPath, config);
console.log('Vercel output has been generated');
} else {

View File

@@ -1,7 +1,7 @@
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"outDir": "./build"
"outDir": "./dist"
},
"include": ["src/**/*.ts"]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/next",
"version": "3.3.16",
"version": "3.3.18",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",

View File

@@ -214,7 +214,7 @@ export const build: BuildV2 = async ({
repoRootPath = entryPath;
entryPath = path.join(entryPath, config.rootDirectory as string);
}
const outputDirectory = config.outputDirectory || '.next';
const outputDirectory = path.join('./', config.outputDirectory || '.next');
const dotNextStatic = path.join(entryPath, outputDirectory, 'static');
// TODO: remove after testing used for simulating root directory monorepo
// setting that can't be triggered with vercel.json
@@ -2595,7 +2595,7 @@ export const prepareCache: PrepareCache = async ({
debug('Preparing cache...');
const entryDirectory = path.dirname(entrypoint);
const entryPath = path.join(workPath, entryDirectory);
const outputDirectory = config.outputDirectory || '.next';
const outputDirectory = path.join('./', config.outputDirectory || '.next');
const nextVersionRange = await getNextVersionRange(entryPath);
const isLegacy = nextVersionRange && isLegacyNext(nextVersionRange);

View File

@@ -1060,9 +1060,9 @@ export async function serverBuild({
{
src: path.posix.join(
'^/',
entryDirectory,
trailingSlash ? '/' : '',
'$'
entryDirectory !== '.'
? `${entryDirectory}${trailingSlash ? '/$' : '$'}`
: '$'
),
has: [
{
@@ -1417,7 +1417,7 @@ export async function serverBuild({
src: `^${path.posix.join(
'/',
entryDirectory,
'/((?!.+\\.rsc).+)$'
'/((?!.+\\.rsc).+?)(?:/)?$'
)}`,
has: [
{

View File

@@ -0,0 +1,12 @@
/* eslint-env jest */
const path = require('path');
const { deployAndTest } = require('../../utils');
const ctx = {};
describe(`${__dirname.split(path.sep).pop()}`, () => {
it('should deploy and pass probe checks', async () => {
const info = await deployAndTest(__dirname);
Object.assign(ctx, info);
});
});

View File

@@ -0,0 +1,12 @@
{
"name": "test-root-dir",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"@next/font": "13.1.2",
"next": "13.1.2",
"react": "18.2.0",
"react-dom": "18.2.0"
}
}

View File

@@ -0,0 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
};
module.exports = nextConfig;

View File

@@ -0,0 +1,12 @@
{
"name": "client",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {}
}

View File

@@ -0,0 +1,9 @@
import { NextResponse } from 'next/server';
export default function handler(req) {
return NextResponse.json({ hello: 'world', now: Date.now(), url: req.url });
}
export const config = {
runtime: 'edge',
};

View File

@@ -0,0 +1,5 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
export default function handler(req, res) {
res.status(200).json({ name: 'John Doe' });
}

View File

@@ -0,0 +1,3 @@
export default function Index() {
return <p>Index page</p>;
}

View File

@@ -0,0 +1,20 @@
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/next",
"config": {
"buildCommand": "cd packages/client && yarn build",
"outputDirectory": "/packages/client/.next"
}
}
],
"probes": [
{
"path": "/api/data/first",
"status": 200,
"mustContain": "\"hello\":\"world\""
}
]
}

View File

@@ -0,0 +1,7 @@
export default function AnotherPage(props) {
return (
<>
<p>hello from newroot/dashboard/another</p>
</>
);
}

View File

@@ -0,0 +1,10 @@
export default function Root({ children }) {
return (
<html className="this-is-another-document-html">
<head>
<title>{`hello world`}</title>
</head>
<body className="this-is-another-document-body">{children}</body>
</html>
);
}

View File

@@ -0,0 +1,7 @@
export default function ChangelogPage(props) {
return (
<>
<p>hello from app/dashboard/changelog</p>
</>
);
}

View File

@@ -0,0 +1,7 @@
export default function HelloPage(props) {
return (
<>
<p>hello from app/dashboard/rootonly/hello</p>
</>
);
}

View File

@@ -0,0 +1,20 @@
'use client';
import { useState, useEffect } from 'react';
import style from './style.module.css';
import './style.css';
export default function ClientComponentRoute() {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(1);
}, [count]);
return (
<>
<p className={style.red}>
hello from app/client-component-route. <b>count: {count}</b>
</p>
</>
);
}

View File

@@ -0,0 +1,3 @@
b {
color: blue;
}

View File

@@ -0,0 +1,3 @@
.red {
color: red;
}

View File

@@ -0,0 +1,20 @@
'use client';
import { useState, useEffect } from 'react';
import styles from './style.module.css';
import './style.css';
export default function ClientNestedLayout({ children }) {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(1);
}, []);
return (
<>
<h1 className={styles.red}>Client Nested. Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>{count}</button>
{children}
</>
);
}

View File

@@ -0,0 +1,7 @@
export default function ClientPage() {
return (
<>
<p>hello from app/client-nested</p>
</>
);
}

View File

@@ -0,0 +1,3 @@
button {
color: red;
}

View File

@@ -0,0 +1,3 @@
.red {
color: red;
}

View File

@@ -0,0 +1,7 @@
export default function DeploymentsBreakdownPage(props) {
return (
<>
<p>hello from app/dashboard/(custom)/deployments/breakdown</p>
</>
);
}

View File

@@ -0,0 +1,8 @@
export default function CustomDashboardRootLayout({ children }) {
return (
<>
<h2>Custom dashboard</h2>
{children}
</>
);
}

View File

@@ -0,0 +1,7 @@
export default function DeploymentsPage(props) {
return (
<>
<p>hello from app/dashboard/deployments/[id]. ID is: {props.params.id}</p>
</>
);
}

View File

@@ -0,0 +1,10 @@
export default function DeploymentsPage(props) {
return (
<>
<p>
hello from app/dashboard/deployments/[id]/settings. ID is:{' '}
{props.params.id}
</p>
</>
);
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <p>catchall</p>;
}

View File

@@ -0,0 +1,7 @@
export default function DeploymentsInfoPage(props) {
return (
<>
<p>hello from app/dashboard/deployments/info</p>
</>
);
}

View File

@@ -0,0 +1,8 @@
export default function DeploymentsLayout({ message, children }) {
return (
<>
<h2>Deployments hello</h2>
{children}
</>
);
}

View File

@@ -0,0 +1,7 @@
export default function IntegrationsPage(props) {
return (
<>
<p>hello from app/dashboard/integrations</p>
</>
);
}

View File

@@ -0,0 +1,8 @@
export default function DashboardLayout(props) {
return (
<>
<h1>Dashboard</h1>
{props.children}
</>
);
}

View File

@@ -0,0 +1,7 @@
export default function DashboardPage(props) {
return (
<>
<p>hello from app/dashboard</p>
</>
);
}

View File

@@ -0,0 +1,11 @@
export default function IdLayout({ children, params }) {
return (
<>
<h3>
Id Layout. Params:{' '}
<span id="id-layout-params">{JSON.stringify(params)}</span>
</h3>
{children}
</>
);
}

View File

@@ -0,0 +1,11 @@
export default function IdPage({ children, params }) {
return (
<>
<p>
Id Page. Params:{' '}
<span id="id-page-params">{JSON.stringify(params)}</span>
</p>
{children}
</>
);
}

View File

@@ -0,0 +1,11 @@
export default function CategoryLayout({ children, params }) {
return (
<>
<h2>
Category Layout. Params:{' '}
<span id="category-layout-params">{JSON.stringify(params)}</span>{' '}
</h2>
{children}
</>
);
}

View File

@@ -0,0 +1,11 @@
export default function DynamicLayout({ children, params }) {
return (
<>
<h1>
Dynamic Layout. Params:{' '}
<span id="dynamic-layout-params">{JSON.stringify(params)}</span>
</h1>
{children}
</>
);
}

View File

@@ -0,0 +1,10 @@
export default function Root({ children }) {
return (
<html className="this-is-the-document-html">
<head>
<title>{`hello world`}</title>
</head>
<body className="this-is-the-document-body">{children}</body>
</html>
);
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <p>index app page</p>;
}

View File

@@ -0,0 +1,7 @@
export default function DeploymentsPage(props) {
return (
<>
<p>hello from app/partial-match-[id]. ID is: {props.params.id}</p>
</>
);
}

View File

@@ -0,0 +1,7 @@
export default function SharedComponentRoute() {
return (
<>
<p>hello from app/shared-component-route</p>
</>
);
}

View File

@@ -0,0 +1,9 @@
'use client';
export default function ShouldNotServeClientDotJs(props) {
return (
<>
<p>hello from app/should-not-serve-client</p>
</>
);
}

View File

@@ -0,0 +1,7 @@
export default function ShouldNotServeServerDotJs(props) {
return (
<>
<p>hello from app/should-not-serve-server</p>
</>
);
}

View File

@@ -0,0 +1,3 @@
export default function Loading() {
return <p id="loading-layout">Loading layout...</p>;
}

View File

@@ -0,0 +1,18 @@
import { use } from 'react';
async function getData() {
await new Promise(resolve => setTimeout(resolve, 1000));
return {
message: 'hello from slow layout',
};
}
export default function SlowLayout(props) {
const data = use(getData());
return (
<>
<p id="slow-layout-message">{data.message}</p>
{props.children}
</>
);
}

View File

@@ -0,0 +1,3 @@
export default function Loading() {
return <p id="loading-page">Loading page...</p>;
}

View File

@@ -0,0 +1,13 @@
import { use } from 'react';
async function getData() {
await new Promise(resolve => setTimeout(resolve, 5000));
return {
message: 'hello from slow page',
};
}
export default function SlowPage(props) {
const data = use(getData());
return <h1 id="slow-page-message">{data.message}</h1>;
}

View File

@@ -0,0 +1,3 @@
export default function Loading() {
return <p id="loading">Loading...</p>;
}

View File

@@ -0,0 +1,18 @@
import { use } from 'react';
async function getData() {
await new Promise(resolve => setTimeout(resolve, 5000));
return {
message: 'hello from slow layout',
};
}
export default function SlowLayout(props) {
const data = use(getData());
return (
<>
<p id="slow-layout-message">{data.message}</p>
{props.children}
</>
);
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <h1 id="page-message">Hello World</h1>;
}

View File

@@ -0,0 +1,3 @@
export default function Loading() {
return <p id="loading">Loading...</p>;
}

View File

@@ -0,0 +1,13 @@
import { use } from 'react';
async function getData() {
await new Promise(resolve => setTimeout(resolve, 5000));
return {
message: 'hello from slow page',
};
}
export default function SlowPage(props) {
const data = use(getData());
return <h1 id="slow-page-message">{data.message}</h1>;
}

View File

@@ -0,0 +1,10 @@
export const revalidate = 3;
export default function Page() {
return (
<>
<p>hello from /ssg</p>
<p>{Date.now()}</p>
</>
);
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <p id="page">Page</p>;
}

View File

@@ -0,0 +1,12 @@
/* eslint-env jest */
const path = require('path');
const { deployAndTest } = require('../../utils');
const ctx = {};
describe(`${__dirname.split(path.sep).pop()}`, () => {
it('should deploy and pass probe checks', async () => {
const info = await deployAndTest(__dirname);
Object.assign(ctx, info);
});
});

View File

@@ -0,0 +1,16 @@
import { NextResponse } from 'next/server';
export function middleware(request) {
if (request.nextUrl.pathname === '/middleware-to-dashboard/') {
// TODO: this does not copy __flight__ and __flight_router_state_tree__
return NextResponse.rewrite(new URL('/dashboard', request.url));
}
if (
request.nextUrl.pathname === '/ssg/' &&
request.nextUrl.searchParams.get('override')
) {
request.nextUrl.pathname = '/overridden/';
return NextResponse.redirect(request.nextUrl);
}
}

View File

@@ -0,0 +1,14 @@
module.exports = {
trailingSlash: true,
experimental: {
appDir: true,
},
rewrites: async () => {
return [
{
source: '/rewritten-to-dashboard',
destination: '/dashboard',
},
];
},
};

View File

@@ -0,0 +1,7 @@
{
"dependencies": {
"next": "canary",
"react": "experimental",
"react-dom": "experimental"
}
}

View File

@@ -0,0 +1,3 @@
export default function handler(req, res) {
return res.json({ hello: 'world' });
}

View File

@@ -0,0 +1,7 @@
export default function Page(props) {
return (
<>
<p>hello from pages/blog/[slug]</p>
</>
);
}

View File

@@ -0,0 +1 @@
hello world

View File

@@ -0,0 +1,169 @@
{
"builds": [
{
"src": "package.json",
"use": "@vercel/next"
}
],
"probes": [
{
"path": "/dynamic/category-1/id-1/",
"status": 200,
"headers": {
"RSC": "1"
},
"fetchOptions": {
"redirect": "manual"
},
"mustContain": ":{",
"mustNotContain": "<html"
},
{
"path": "/ssg/",
"status": 200,
"mustContain": "hello from /ssg",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/ssg/",
"status": 200,
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
},
"headers": {
"RSC": "1"
},
"mustContain": ":{",
"mustNotContain": "<html"
},
{
"path": "/ssg/?override=1",
"status": 307,
"responseHeaders": {
"location": "/overridden/"
},
"fetchOptions": {
"redirect": "manual"
}
},
{
"path": "/ssg/?override=1",
"status": 307,
"responseHeaders": {
"location": "/overridden/"
},
"fetchOptions": {
"redirect": "manual"
}
},
{
"path": "/dashboard/deployments/123/settings/",
"status": 200,
"mustContain": "hello from app/dashboard/deployments/[id]/settings",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/dashboard/deployments/123/settings/",
"status": 200,
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
},
"headers": {
"RSC": "1"
},
"mustContain": ":{",
"mustNotContain": "<html"
},
{
"path": "/dashboard/deployments/catchall/something/",
"status": 200,
"mustContain": "catchall",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/dashboard/deployments/catchall/something/",
"status": 200,
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
},
"headers": {
"RSC": "1"
},
"mustContain": ":{",
"mustNotContain": "<html"
},
{
"path": "/dashboard/",
"status": 200,
"mustContain": "hello from app/dashboard",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/dashboard/",
"status": 200,
"headers": {
"RSC": "1"
},
"mustContain": ":{",
"mustNotContain": "<html"
},
{
"path": "/dashboard/",
"status": 200,
"headers": {
"RSC": "1"
},
"responseHeaders": {
"content-type": "application/octet-stream",
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/dashboard/another/",
"status": 200,
"mustContain": "hello from newroot/dashboard/another"
},
{
"path": "/dashboard/deployments/123/",
"status": 200,
"mustContain": "hello from app/dashboard/deployments/[id]. ID is: <!-- -->123"
},
{
"path": "/",
"status": 200,
"mustContain": "index app page"
},
{
"path": "/blog/123/",
"status": 200,
"mustContain": "hello from pages/blog/[slug]"
},
{
"path": "/dynamic/category-1/id-1/",
"status": 200,
"mustContain": "{&quot;category&quot;:&quot;category-1&quot;,&quot;id&quot;:&quot;id-1&quot;}"
},
{
"path": "/dashboard/changelog/",
"status": 200,
"mustContain": "hello from app/dashboard/changelog"
},
{
"path": "/",
"status": 200,
"headers": {
"RSC": "1"
},
"mustContain": ":{",
"mustNotContain": "<html"
}
]
}

View File

@@ -24,18 +24,19 @@ describe(`${__dirname.split(path.sep).pop()}`, () => {
expect(isNaN(props.now)).toBe(false);
const { pageProps: data } = await fetch(
`${ctx.deploymentUrl}/docs/_next/data/testing-build-id/rewrite-to-another-site.json`
`${ctx.deploymentUrl}/docs/_next/data/testing-build-id/rewrite-to-another-site.json`,
{ headers: { 'x-nextjs-data': '1' } }
).then(res => res.json());
expect(isNaN(data.now)).toBe(false);
const revalidateRes = await fetch(
`${ctx.deploymentUrl}/docs/api/revalidate?urlPath=/docs/_sites/test-revalidate`
);
expect(revalidateRes.status).toBe(200);
expect(await revalidateRes.json()).toEqual({ revalidated: true });
await check(async () => {
const revalidateRes = await fetch(
`${ctx.deploymentUrl}/docs/api/revalidate?urlPath=/docs/_sites/test-revalidate`
);
expect(revalidateRes.status).toBe(200);
expect(await revalidateRes.json()).toEqual({ revalidated: true });
const newProps = await propsFromHtml();
console.log({ props, newProps });
@@ -52,7 +53,8 @@ describe(`${__dirname.split(path.sep).pop()}`, () => {
await check(async () => {
const { pageProps: newData } = await fetch(
`${ctx.deploymentUrl}/docs/_next/data/testing-build-id/rewrite-to-another-site.json`
`${ctx.deploymentUrl}/docs/_next/data/testing-build-id/rewrite-to-another-site.json`,
{ headers: { 'x-nextjs-data': '1' } }
).then(res => res.json());
console.log({ newData, data });
@@ -80,18 +82,19 @@ describe(`${__dirname.split(path.sep).pop()}`, () => {
expect(isNaN(props.now)).toBe(false);
const { pageProps: data } = await fetch(
`${ctx.deploymentUrl}/docs/_next/data/testing-build-id/financial.json?slug=financial`
`${ctx.deploymentUrl}/docs/_next/data/testing-build-id/financial.json?slug=financial`,
{ headers: { 'x-nextjs-data': '1' } }
).then(res => res.json());
expect(isNaN(data.now)).toBe(false);
const revalidateRes = await fetch(
`${ctx.deploymentUrl}/docs/api/revalidate?urlPath=/docs/financial`
);
expect(revalidateRes.status).toBe(200);
expect(await revalidateRes.json()).toEqual({ revalidated: true });
await check(async () => {
const revalidateRes = await fetch(
`${ctx.deploymentUrl}/docs/api/revalidate?urlPath=/docs/financial`
);
expect(revalidateRes.status).toBe(200);
expect(await revalidateRes.json()).toEqual({ revalidated: true });
const newProps = await propsFromHtml();
console.log({ props, newProps });
@@ -108,7 +111,8 @@ describe(`${__dirname.split(path.sep).pop()}`, () => {
await check(async () => {
const { pageProps: newData } = await fetch(
`${ctx.deploymentUrl}/docs/_next/data/testing-build-id/financial.json?slug=financial`
`${ctx.deploymentUrl}/docs/_next/data/testing-build-id/financial.json?slug=financial`,
{ headers: { 'x-nextjs-data': '1' } }
).then(res => res.json());
console.log({ newData, data });

View File

@@ -7,6 +7,15 @@
}
],
"probes": [
{
"path": "/docs/_next/data/testing-build-id/index.json",
"status": 200,
"headers": {
"x-nextjs-data": 1
},
"mustContain": "\"pageProps\":{",
"mustNotContain": "<html"
},
{
"path": "/docs/_next/data/testing-build-id/dynamic/static.json",
"status": 200,
@@ -44,7 +53,7 @@
"x-nextjs-data": 1
},
"mustContain": "site\":\"subdomain-1\"",
"mustNotContain": "<html>"
"mustNotContain": "<html"
},
{
"path": "/docs/redirect-me",
@@ -113,7 +122,7 @@
"fetchOptions": {
"redirect": "manual"
},
"mustNotContain": "<html>",
"mustNotContain": "<html",
"mustContain": "\"site\":\"subdomain-1\""
}
]

View File

@@ -2,8 +2,12 @@ process.env.NEXT_TELEMETRY_DISABLED = '1';
const path = require('path');
const fs = require('fs-extra');
const builder = require('../../');
const {
createRunBuildLambda,
} = require('../../../../test/lib/run-build-lambda');
const runBuildLambda = require('../../../../test/lib/run-build-lambda');
const runBuildLambda = createRunBuildLambda(builder);
jest.setTimeout(360000);

View File

@@ -6,10 +6,13 @@ import type { Context } from '../types';
import { duplicateWithConfig } from '../utils';
import fs from 'fs-extra';
import path from 'path';
import runBuildLambda from '../../../../test/lib/run-build-lambda';
import * as builder from '../../';
import { createRunBuildLambda } from '../../../../test/lib/run-build-lambda';
import { EdgeFunction, Files, streamToBuffer } from '@vercel/build-utils';
import { createHash } from 'crypto';
const runBuildLambda = createRunBuildLambda(builder);
const SIMPLE_PROJECT = path.resolve(
__dirname,
'..',

View File

@@ -1,6 +0,0 @@
describe('export', () => {
it('should require by path main', async () => {
const main = require('@vercel/next');
expect(main).toBeDefined();
});
});

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/node",
"version": "2.8.14",
"version": "2.8.15",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",

View File

@@ -1,6 +1,6 @@
import _ts from 'typescript';
import { NowBuildError } from '@vercel/build-utils';
import { relative, basename, resolve, dirname } from 'path';
import { relative, basename, dirname } from 'path';
import type _ts from 'typescript';
/*
* Fork of TS-Node - https://github.com/TypeStrong/ts-node
@@ -133,21 +133,20 @@ export function register(opts: Options = {}): Register {
// Require the TypeScript compiler and configuration.
const cwd = options.basePath || process.cwd();
const nowNodeBase = resolve(__dirname, '..', '..', '..');
let compiler: string;
const require_ = eval('require');
try {
compiler = require_.resolve(options.compiler || 'typescript', {
paths: [options.project || cwd, nowNodeBase],
paths: [options.project || cwd],
});
} catch (e) {
compiler = 'typescript';
}
//eslint-disable-next-line @typescript-eslint/no-var-requires
const ts: typeof _ts = require_(compiler);
if (compiler.startsWith(nowNodeBase)) {
if (compiler === 'typescript') {
console.log(
`Using built-in TypeScript ${ts.version} since "typescript" missing from "devDependencies"`
`Using built-in TypeScript ${ts.version} since "typescript" is missing from "devDependencies"`
);
} else {
console.log(`Using TypeScript ${ts.version} (local user-provided)`);

View File

@@ -4,7 +4,8 @@
"probes": [
{
"path": "/",
"mustContain": "declare namespace Test"
"mustContain": "declare namespace Test",
"logMustContain": "Using built-in TypeScript"
}
]
}

View File

@@ -24,7 +24,8 @@
"probes": [
{
"path": "/",
"mustContain": "`"
"mustContain": "`",
"logMustContain": "Using TypeScript 3.5.3 (local user-provided)"
},
{
"path": "/functions/es5.ts",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/remix",
"version": "1.2.6",
"version": "1.2.7",
"license": "MIT",
"main": "./dist/index.js",
"homepage": "https://vercel.com/docs",
@@ -11,7 +11,7 @@
},
"scripts": {
"build": "node build.js",
"test-integration-once": "pnpm test test/test.js",
"test-integration-once": "pnpm test test/integration.test.ts",
"test": "jest --env node --verbose --bail --runInBand"
},
"files": [

View File

@@ -1,5 +1,5 @@
const fs = require('fs');
const path = require('path');
const { join } = require('path');
const {
testDeployment,
@@ -7,14 +7,21 @@ const {
jest.setTimeout(12 * 60 * 1000);
const fixturesPath = path.resolve(__dirname, 'fixtures');
const fixturesPath = join(__dirname, 'fixtures');
const exampleAbsolute = (name: string) =>
join(__dirname, '..', '..', '..', 'examples', name);
// eslint-disable-next-line no-restricted-syntax
for (const fixture of fs.readdirSync(fixturesPath)) {
// eslint-disable-next-line no-loop-func
it(`should build ${fixture}`, async () => {
await expect(
testDeployment(path.join(fixturesPath, fixture))
testDeployment(join(fixturesPath, fixture))
).resolves.toBeDefined();
});
}
it(`should build remix example`, async () => {
const example = exampleAbsolute('remix');
await expect(testDeployment(example)).resolves.toBeDefined();
});

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/static-build",
"version": "1.1.7",
"version": "1.2.0",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/build-step",
@@ -37,8 +37,10 @@
"@types/node": "14.18.33",
"@types/node-fetch": "2.5.4",
"@types/promise-timeout": "1.3.0",
"@types/semver": "7.3.13",
"@vercel/build-utils": "5.9.0",
"@vercel/frameworks": "1.2.4",
"@vercel/fs-detectors": "3.7.5",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.1.8",
"@vercel/static-config": "2.0.11",
@@ -49,6 +51,7 @@
"ms": "2.1.2",
"node-fetch": "2.6.7",
"rc9": "1.2.0",
"semver": "7.3.8",
"tree-kill": "1.2.2",
"ts-morph": "12.0.0",
"typescript": "4.3.4"

View File

@@ -41,6 +41,12 @@ import * as GatsbyUtils from './utils/gatsby';
import * as NuxtUtils from './utils/nuxt';
import type { ImagesConfig, BuildConfig } from './utils/_shared';
import treeKill from 'tree-kill';
import {
detectFrameworkRecord,
detectFramework,
LocalFileSystemDetector,
packageManagers,
} from '@vercel/fs-detectors';
const sleep = (n: number) => new Promise(resolve => setTimeout(resolve, n));
@@ -120,7 +126,7 @@ function getCommand(
name: 'install' | 'build' | 'dev',
pkg: PackageJson | null,
config: Config,
framework: Framework | undefined
framework?: Framework
): string | null {
if (!config.zeroConfig) {
return null;
@@ -318,6 +324,12 @@ export const build: BuildV2 = async ({
const pkg = getPkg(entrypoint, workPath);
const devScript = pkg ? getScriptName(pkg, 'dev', config) : null;
const framework = getFramework(config, pkg);
const localFileSystemDetector = new LocalFileSystemDetector(workPath);
const { detectedVersion = null } =
(await detectFrameworkRecord({
fs: localFileSystemDetector,
frameworkList: frameworks,
})) ?? {};
const devCommand = getCommand('dev', pkg, config, framework);
const buildCommand = getCommand('build', pkg, config, framework);
const installCommand = getCommand('install', pkg, config, framework);
@@ -378,15 +390,29 @@ export const build: BuildV2 = async ({
process.env[key] = value;
}
if (framework.slug === 'gatsby') {
const injectedPlugins = await GatsbyUtils.injectPlugins(
detectedVersion,
entrypointDir
);
if (injectedPlugins) {
const packageManager = await detectFramework({
fs: localFileSystemDetector,
frameworkList: packageManagers,
});
if (packageManager === 'pnpm') {
await execCommand('pnpm install --lockfile-only');
}
}
}
if (process.env.VERCEL_ANALYTICS_ID) {
const frameworkDirectory = path.join(
workPath,
path.dirname(entrypoint)
);
switch (framework.slug) {
case 'gatsby':
await GatsbyUtils.injectVercelAnalyticsPlugin(frameworkDirectory);
break;
case 'nuxtjs':
await NuxtUtils.injectVercelAnalyticsPlugin(frameworkDirectory);
break;

View File

@@ -44,7 +44,7 @@ export async function writePackageJson(
) {
await fs.writeFile(
path.join(workPath, 'package.json'),
JSON.stringify(packageJson, null, 2)
`${JSON.stringify(packageJson, null, 2)}\n`
);
}

View File

@@ -41,7 +41,7 @@ export function createBuildOutput(
meta: Meta,
buildCommand: string | null,
buildOutputPath: string,
framework?: Framework
framework: Framework | undefined
): BuildResultV2 {
if (meta.isDev) {
let buildCommandName: string;

View File

@@ -1,6 +1,7 @@
import { PackageJson } from '@vercel/build-utils';
import { promises as fs } from 'fs';
import * as path from 'path';
import semver from 'semver';
import {
fileExists,
readPackageJson,
@@ -8,72 +9,97 @@ import {
writePackageJson,
} from './_shared';
const GATSBY_PLUGIN_PACKAGE_NAME = '@vercel/gatsby-plugin-vercel-analytics';
const DEFAULT_CONFIG = {
plugins: [
{
resolve: GATSBY_PLUGIN_PACKAGE_NAME,
options: {},
},
],
const PLUGINS = {
GATSBY_PLUGIN_VERCEL_ANALYTICS: '@vercel/gatsby-plugin-vercel-analytics',
GATSBY_PLUGIN_VERCEL_BUILDER: '@vercel/gatsby-plugin-vercel-builder',
};
const GATSBY_CONFIG_FILE = 'gatsby-config';
export async function injectVercelAnalyticsPlugin(dir: string): Promise<void> {
// Gatsby requires a special variable name for environment variables to be
// exposed to the client-side JavaScript bundles:
process.env.GATSBY_VERCEL_ANALYTICS_ID = process.env.VERCEL_ANALYTICS_ID;
export async function injectPlugins(
detectedVersion: string | null,
dir: string
) {
const pluginsToInject = [];
if (process.env.VERCEL_GATSBY_BUILDER_PLUGIN && detectedVersion) {
const version = semver.coerce(detectedVersion);
if (version && semver.satisfies(version, '>=4.0.0')) {
pluginsToInject.push(PLUGINS.GATSBY_PLUGIN_VERCEL_BUILDER);
}
}
if (process.env.VERCEL_ANALYTICS_ID) {
process.env.GATSBY_VERCEL_ANALYTICS_ID = process.env.VERCEL_ANALYTICS_ID;
pluginsToInject.push(PLUGINS.GATSBY_PLUGIN_VERCEL_ANALYTICS);
}
if (pluginsToInject.length === 0) {
return false;
}
await addGatsbyPackage(dir, pluginsToInject);
const gatsbyConfigPathJs = path.join(dir, `${GATSBY_CONFIG_FILE}.js`);
const gatsbyConfigPathMjs = path.join(dir, `${GATSBY_CONFIG_FILE}.mjs`);
const gatsbyConfigPathTs = path.join(dir, `${GATSBY_CONFIG_FILE}.ts`);
const gatsbyConfigPathMjs = path.join(dir, `${GATSBY_CONFIG_FILE}.mjs`);
const gatsbyConfigPathJs = path.join(dir, `${GATSBY_CONFIG_FILE}.js`);
if (await fileExists(gatsbyConfigPathTs)) {
console.log(
`Injecting Gatsby.js analytics plugin "${GATSBY_PLUGIN_PACKAGE_NAME}" to \`${gatsbyConfigPathTs}\``
);
await addGatsbyPackage(dir);
return updateGatsbyTsConfig(gatsbyConfigPathTs);
}
if (await fileExists(gatsbyConfigPathMjs)) {
console.log(
`Injecting Gatsby.js analytics plugin "${GATSBY_PLUGIN_PACKAGE_NAME}" to \`${gatsbyConfigPathMjs}\``
);
await addGatsbyPackage(dir);
return updateGatsbyMjsConfig(gatsbyConfigPathMjs);
}
console.log(
`Injecting Gatsby.js analytics plugin "${GATSBY_PLUGIN_PACKAGE_NAME}" to \`${gatsbyConfigPathJs}\``
);
await addGatsbyPackage(dir);
if (await fileExists(gatsbyConfigPathJs)) {
await updateGatsbyJsConfig(gatsbyConfigPathJs);
printInjectingPlugins(pluginsToInject, gatsbyConfigPathTs);
await updateGatsbyTsConfig(gatsbyConfigPathTs, pluginsToInject);
} else if (await fileExists(gatsbyConfigPathMjs)) {
printInjectingPlugins(pluginsToInject, gatsbyConfigPathMjs);
await updateGatsbyMjsConfig(gatsbyConfigPathMjs, pluginsToInject);
} else {
await fs.writeFile(
gatsbyConfigPathJs,
`module.exports = ${JSON.stringify(DEFAULT_CONFIG)}`
);
printInjectingPlugins(pluginsToInject, gatsbyConfigPathJs);
if (await fileExists(gatsbyConfigPathJs)) {
await updateGatsbyJsConfig(gatsbyConfigPathJs, pluginsToInject);
} else {
await fs.writeFile(
gatsbyConfigPathJs,
`module.exports = ${JSON.stringify({
plugins: pluginsToInject,
})}`
);
}
}
return true;
}
async function addGatsbyPackage(dir: string): Promise<void> {
function printInjectingPlugins(plugins: string[], configPath: string) {
let pluginsStr = 'plugin';
if (plugins.length > 1) {
pluginsStr += 's';
}
console.log(
`Injecting Gatsby.js ${pluginsStr} ${plugins
.map(p => `"${p}"`)
.join(', ')} to \`${configPath}\``
);
}
async function addGatsbyPackage(
dir: string,
plugins: Array<string>
): Promise<void> {
const pkgJson = (await readPackageJson(dir)) as DeepWriteable<PackageJson>;
if (!pkgJson.dependencies) {
pkgJson.dependencies = {};
}
if (!pkgJson.dependencies[GATSBY_PLUGIN_PACKAGE_NAME]) {
console.log(
`Adding "${GATSBY_PLUGIN_PACKAGE_NAME}" to \`package.json\` "dependencies"`
);
pkgJson.dependencies[GATSBY_PLUGIN_PACKAGE_NAME] = 'latest';
await writePackageJson(dir, pkgJson);
for (const plugin of plugins) {
if (!pkgJson.dependencies[plugin]) {
console.log(`Adding "${plugin}" to \`package.json\` "dependencies"`);
pkgJson.dependencies[plugin] = 'latest';
}
}
await writePackageJson(dir, pkgJson);
}
async function updateGatsbyTsConfig(configPath: string): Promise<void> {
async function updateGatsbyTsConfig(
configPath: string,
plugins: Array<string>
): Promise<void> {
await fs.rename(configPath, configPath + '.__vercel_builder_backup__.ts');
await fs.writeFile(
@@ -86,25 +112,24 @@ const preferDefault = (m: any) => (m && m.default) || m;
const vercelConfig = Object.assign(
{},
// https://github.com/gatsbyjs/gatsby/blob/a6ecfb2b01d761e8a3612b8ea132c698659923d9/packages/gatsby/src/services/initialize.ts#L113-L117
preferDefault(userConfig)
);
if (!vercelConfig.plugins) {
vercelConfig.plugins = [];
}
const hasPlugin = vercelConfig.plugins.find(
(p: PluginRef) =>
p && (p === "${GATSBY_PLUGIN_PACKAGE_NAME}" || p.resolve === "${GATSBY_PLUGIN_PACKAGE_NAME}")
);
for (const plugin of ${JSON.stringify(plugins)}) {
const hasPlugin = vercelConfig.plugins.find(
(p: PluginRef) =>
p && (p === plugin || p.resolve === plugin)
);
if (!hasPlugin) {
vercelConfig.plugins = vercelConfig.plugins.slice();
vercelConfig.plugins.push({
resolve: "${GATSBY_PLUGIN_PACKAGE_NAME}",
options: {},
});
if (!hasPlugin) {
vercelConfig.plugins = vercelConfig.plugins.slice();
vercelConfig.plugins.push(plugin);
}
}
export default vercelConfig;
@@ -112,7 +137,10 @@ export default vercelConfig;
);
}
async function updateGatsbyMjsConfig(configPath: string): Promise<void> {
async function updateGatsbyMjsConfig(
configPath: string,
plugins: Array<string>
): Promise<void> {
await fs.rename(configPath, configPath + '.__vercel_builder_backup__.mjs');
await fs.writeFile(
@@ -124,7 +152,6 @@ const preferDefault = (m) => (m && m.default) || m;
const vercelConfig = Object.assign(
{},
// https://github.com/gatsbyjs/gatsby/blob/a6ecfb2b01d761e8a3612b8ea132c698659923d9/packages/gatsby/src/services/initialize.ts#L113-L117
preferDefault(userConfig)
);
@@ -132,17 +159,15 @@ if (!vercelConfig.plugins) {
vercelConfig.plugins = [];
}
const hasPlugin = vercelConfig.plugins.find(
(p) =>
p && (p === "${GATSBY_PLUGIN_PACKAGE_NAME}" || p.resolve === "${GATSBY_PLUGIN_PACKAGE_NAME}")
);
for (const plugin of ${JSON.stringify(plugins)}) {
const hasPlugin = vercelConfig.plugins.find(
(p) => p && (p === plugin || p.resolve === plugin)
);
if (!hasPlugin) {
vercelConfig.plugins = vercelConfig.plugins.slice();
vercelConfig.plugins.push({
resolve: "${GATSBY_PLUGIN_PACKAGE_NAME}",
options: {},
});
if (!hasPlugin) {
vercelConfig.plugins = vercelConfig.plugins.slice();
vercelConfig.plugins.push(plugin);
}
}
export default vercelConfig;
@@ -150,7 +175,10 @@ export default vercelConfig;
);
}
async function updateGatsbyJsConfig(configPath: string): Promise<void> {
async function updateGatsbyJsConfig(
configPath: string,
plugins: Array<string>
): Promise<void> {
await fs.rename(configPath, configPath + '.__vercel_builder_backup__.js');
await fs.writeFile(
@@ -162,7 +190,6 @@ const preferDefault = m => (m && m.default) || m;
const vercelConfig = Object.assign(
{},
// https://github.com/gatsbyjs/gatsby/blob/a6ecfb2b01d761e8a3612b8ea132c698659923d9/packages/gatsby/src/services/initialize.ts#L113-L117
preferDefault(userConfig)
);
@@ -170,18 +197,16 @@ if (!vercelConfig.plugins) {
vercelConfig.plugins = [];
}
const hasPlugin = vercelConfig.plugins.find(
(p) =>
p && (p === "${GATSBY_PLUGIN_PACKAGE_NAME}" || p.resolve === "${GATSBY_PLUGIN_PACKAGE_NAME}")
);
if (!hasPlugin) {
vercelConfig.plugins = vercelConfig.plugins.slice();
vercelConfig.plugins.push({
resolve: "${GATSBY_PLUGIN_PACKAGE_NAME}",
options: {},
});
}
for (const plugin of ${JSON.stringify(plugins)}) {
const hasPlugin = vercelConfig.plugins.find(
(p) => p && (p === plugin || p.resolve === plugin)
);
if (!hasPlugin) {
vercelConfig.plugins = vercelConfig.plugins.slice();
vercelConfig.plugins.push(plugin);
}
}
module.exports = vercelConfig;
`
);

View File

@@ -1,6 +1,9 @@
const path = require('path');
const fs = require('fs-extra');
const runBuildLambda = require('../../../test/lib/run-build-lambda');
const builder = require('../');
const { createRunBuildLambda } = require('../../../test/lib/run-build-lambda');
const runBuildLambda = createRunBuildLambda(builder);
const FOUR_MINUTES = 240000;
@@ -28,10 +31,7 @@ it(
.toMatchInlineSnapshot(`
Object {
"plugins": Array [
Object {
"options": Object {},
"resolve": "@vercel/gatsby-plugin-vercel-analytics",
},
"@vercel/gatsby-plugin-vercel-analytics"
],
}
`);
@@ -59,10 +59,7 @@ it(
.toMatchInlineSnapshot(`
Object {
"plugins": Array [
Object {
"options": Object {},
"resolve": "@vercel/gatsby-plugin-vercel-analytics",
},
"@vercel/gatsby-plugin-vercel-analytics"
],
"siteMetadata": Object {
"author": "@gatsbyjs",
@@ -96,10 +93,7 @@ it(
Object {
"plugins": Array [
"gatsby-plugin-react-helmet",
Object {
"options": Object {},
"resolve": "@vercel/gatsby-plugin-vercel-analytics",
},
"@vercel/gatsby-plugin-vercel-analytics"
],
"siteMetadata": Object {
"author": "@gatsbyjs",
@@ -202,10 +196,7 @@ it(
Object {
"plugins": Array [
"gatsby-plugin-react-helmet",
Object {
"options": Object {},
"resolve": "@vercel/gatsby-plugin-vercel-analytics",
},
"@vercel/gatsby-plugin-vercel-analytics"
],
"siteMetadata": Object {
"author": "@gatsbyjs",
@@ -241,10 +232,7 @@ it(
},
"resolve": "gatsby-plugin-zeit-now",
},
Object {
"options": Object {},
"resolve": "@vercel/gatsby-plugin-vercel-analytics",
},
"@vercel/gatsby-plugin-vercel-analytics"
],
"siteMetadata": Object {
"author": "@gatsbyjs",
@@ -274,10 +262,8 @@ it(
Object {
"default": Object {
"plugins": Array [
Object {
"options": Object {},
"resolve": "@vercel/gatsby-plugin-vercel-analytics",
},
"@vercel/gatsby-plugin-vercel-builder",
"@vercel/gatsby-plugin-vercel-analytics",
],
"siteMetadata": Object {
"siteUrl": "https://gatsby-typescript-config.vercel.app",
@@ -311,7 +297,6 @@ const preferDefault = (m) => (m && m.default) || m;
const vercelConfig = Object.assign(
{},
// https://github.com/gatsbyjs/gatsby/blob/a6ecfb2b01d761e8a3612b8ea132c698659923d9/packages/gatsby/src/services/initialize.ts#L113-L117
preferDefault(userConfig)
);
@@ -319,17 +304,15 @@ if (!vercelConfig.plugins) {
vercelConfig.plugins = [];
}
const hasPlugin = vercelConfig.plugins.find(
(p) =>
p && (p === "@vercel/gatsby-plugin-vercel-analytics" || p.resolve === "@vercel/gatsby-plugin-vercel-analytics")
);
for (const plugin of ["@vercel/gatsby-plugin-vercel-builder","@vercel/gatsby-plugin-vercel-analytics"]) {
const hasPlugin = vercelConfig.plugins.find(
(p) => p && (p === plugin || p.resolve === plugin)
);
if (!hasPlugin) {
vercelConfig.plugins = vercelConfig.plugins.slice();
vercelConfig.plugins.push({
resolve: "@vercel/gatsby-plugin-vercel-analytics",
options: {},
});
if (!hasPlugin) {
vercelConfig.plugins = vercelConfig.plugins.slice();
vercelConfig.plugins.push(plugin);
}
}
export default vercelConfig;

View File

@@ -0,0 +1,69 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# dotenv environment variable files
.env*
# gatsby files
.cache/
public
# Mac files
.DS_Store
# Yarn
yarn-error.log
.pnp/
.pnp.js
# Yarn Integrity file
.yarn-integrity

View File

@@ -0,0 +1,8 @@
module.exports = {
siteMetadata: {
title: `Gatsby Default Starter`,
description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
author: `@gatsbyjs`,
siteUrl: `https://gatsbystarterdefaultsource.gatsbyjs.io/`,
},
};

View File

@@ -0,0 +1,20 @@
{
"name": "gatsby-v2",
"private": true,
"dependencies": {
"gatsby": "2.32.13",
"prop-types": "15.8.1",
"react": "16.14.0",
"react-dom": "16.14.0"
},
"scripts": {
"build": "gatsby build",
"develop": "gatsby develop",
"start": "gatsby develop",
"serve": "gatsby serve",
"clean": "gatsby clean"
},
"engines": {
"node": "14.x"
}
}

View File

@@ -0,0 +1,10 @@
{
"probes": [
{ "path": "/", "mustContain": "Welcome to <b>Gatsby!</b>" },
{ "path": "/page-2", "mustContain": "Hi from the second page" },
{
"path": "/using-typescript",
"mustContain": "Gatsby supports <b>TypeScript by default</b>"
}
]
}

View File

@@ -0,0 +1,41 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'gatsby';
const Header = ({ siteTitle }) => (
<header
style={{
margin: `0 auto`,
padding: `var(--space-4) var(--size-gutter)`,
display: `flex`,
alignItems: `center`,
justifyContent: `space-between`,
}}
>
<Link
to="/"
style={{
fontSize: `var(--font-sm)`,
textDecoration: `none`,
}}
>
{siteTitle}
</Link>
<img
alt="Gatsby logo"
height={20}
style={{ margin: 0 }}
src="data:image/svg+xml,%3Csvg fill='none' viewBox='0 0 107 28' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3CclipPath id='a'%3E%3Cpath d='m0 0h106.1v28h-106.1z'/%3E%3C/clipPath%3E%3Cg clip-path='url(%23a)'%3E%3Cg fill='%23000'%3E%3Cpath clip-rule='evenodd' d='m89 11.7c-.8 0-2.2.2-3.2 1.6v-8.10005h-2.8v16.80005h2.7v-1.3c1.1 1.5 2.6 1.5999 3.2 1.5999 3 0 5-2.2999 5-5.2999s-2-5.3-4.9-5.3zm-.7 2.5c1.7 0 2.8 1.2 2.8 2.8s-1.2 2.8-2.8 2.8c-1.7 0-2.8-1.2-2.8-2.8s1.1-2.8 2.8-2.8z' fill-rule='evenodd'/%3E%3Cpath d='m71.2 21.9999v-7.6h1.9v-2.4h-1.9v-3.40005h-2.8v3.40005h-1.1v2.4h1.1v7.6z'/%3E%3Cpath clip-rule='evenodd' d='m65.6999 12h-2.9v1.3c-.8999-1.5-2.4-1.6-3.2-1.6-2.9 0-4.8999 2.4-4.8999 5.3s1.9999 5.2999 5.0999 5.2999c.8 0 2.1001-.0999 3.1001-1.5999v1.3h2.7999zm-5.1999 7.8c-1.7001 0-2.8-1.2-2.8-2.8s1.2-2.8 2.8-2.8c1.7 0 2.7999 1.2 2.7999 2.8s-1.1999 2.8-2.7999 2.8z' fill-rule='evenodd'/%3E%3Cpath d='m79.7001 14.4c-.7-.6-1.3-.7-1.6-.7-.7 0-1.1.3-1.1.8 0 .3.1.6.9.9l.7.2c.1261.0472.2621.0945.4037.1437.7571.2632 1.6751.5823 2.0963 1.2563.3.4.5 1 .5 1.7 0 .9-.3 1.8-1.1 2.5s-1.8 1.0999-3 1.0999c-2.1 0-3.2-.9999-3.9-1.6999l1.5-1.7c.6.6 1.4 1.2 2.2 1.2s1.4-.4 1.4-1.1c0-.6-.5-.9-.9-1l-.6-.2c-.0687-.0295-.1384-.0589-.2087-.0887l-.0011-.0004c-.6458-.2729-1.3496-.5704-1.8902-1.1109-.5-.5-.8-1.1-.8-1.9 0-1 .5-1.8 1-2.3.8-.6 1.8-.7 2.6-.7.7 0 1.9.1 3.2 1.1z'/%3E%3Cpath d='m98.5 20.5-4.8-8.5h3.3l3.1 5.7 2.8-5.7h3.2l-8 15.3h-3.2z'/%3E%3Cpath d='m47 13.7h7c0 .0634.01.1267.0206.1932.0227.1435.0477.3018-.0206.5068 0 4.5-3.4 8.1-8 8.1s-8-3.6-8-8.1c0-4.49995 3.6-8.09995 8-8.09995 2.6 0 5 1.2 6.5 3.3l-2.3 1.49995c-1-1.29995-2.6-2.09995-4.2-2.09995-2.9 0-4.9 2.49995-4.9 5.39995s2.1 5.3 5 5.3c2.6 0 4-1.3 4.6-3.2h-3.7z'/%3E%3C/g%3E%3Cpath d='m18 14h7c0 5.2-3.7 9.6-8.5 10.8l-13.19995-13.2c1.1-4.9 5.5-8.6 10.69995-8.6 3.7 0 6.9 1.8 8.9 4.5l-1.5 1.3c-1.7-2.3-4.4-3.8-7.4-3.8-3.9 0-7.29995 2.5-8.49995 6l11.49995 11.5c2.9-1 5.1-3.5 5.8-6.5h-4.8z' fill='%23fff'/%3E%3Cpath d='m6.2 21.7001c-2.1-2.1-3.2-4.8-3.2-7.6l10.8 10.8c-2.7 0-5.5-1.1-7.6-3.2z' fill='%23fff'/%3E%3Cpath d='m14 0c-7.7 0-14 6.3-14 14s6.3 14 14 14 14-6.3 14-14-6.3-14-14-14zm-7.8 21.8c-2.1-2.1-3.2-4.9-3.2-7.6l10.9 10.8c-2.8-.1-5.6-1.1-7.7-3.2zm10.2 2.9-13.1-13.1c1.1-4.9 5.5-8.6 10.7-8.6 3.7 0 6.9 1.8 8.9 4.5l-1.5 1.3c-1.7-2.3-4.4-3.8-7.4-3.8-3.9 0-7.2 2.5-8.5 6l11.5 11.5c2.9-1 5.1-3.5 5.8-6.5h-4.8v-2h7c0 5.2-3.7 9.6-8.6 10.7z' fill='%237026b9'/%3E%3C/g%3E%3C/svg%3E"
/>
</header>
);
Header.propTypes = {
siteTitle: PropTypes.string,
};
Header.defaultProps = {
siteTitle: ``,
};
export default Header;

View File

@@ -0,0 +1,54 @@
.list {
display: grid;
margin: 0;
/* https://css-tricks.com/responsive-layouts-fewer-media-queries/ */
--w: 280px;
--n: 2;
gap: var(--size-gap);
grid-template-columns: repeat(
auto-fit,
minmax(max(var(--w), 100%/ (var(--n) + 1) + 0.1%), 1fr)
);
margin-bottom: var(--size-gap);
margin-top: var(--size-gap);
}
.listItem {
margin: 0;
}
.listItem::marker {
color: #e95800;
}
.listItem:nth-child(2)::marker {
color: #159bf3;
}
.listItem:nth-child(3)::marker {
color: #8eb814;
}
.listItem:nth-child(4)::marker {
color: #663399;
}
.listItemLink {
color: var(--color-primary);
font-weight: bold;
}
.listItemDescription {
color: var(--color-text);
margin-bottom: 0;
margin-top: var(--space-1);
}
.textCenter {
text-align: center;
}
.intro {
max-width: none;
line-height: var(--line-height-loose);
}

View File

@@ -0,0 +1,224 @@
:root {
--border-radius: 4px;
--color-text: #333;
--color-primary: #7026b9;
--color-code-bg: #fff4db;
--color-code: #8a6534;
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
--font-mono: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace;
--font-lg: 18px;
--font-md: 16px;
--font-sm: 14px;
--font-sx: 12px;
--line-height-loose: 1.75;
--line-height-normal: 1.5;
--line-height-dense: 1.1;
--space-1: 4px;
--space-2: 8px;
--space-3: 16px;
--space-4: 24px;
--space-5: 32px;
--space-6: 64px;
--size-content: 54rem;
--size-gutter: var(--space-5);
--size-gap: var(--space-6);
}
html {
-webkit-text-size-adjust: 100%;
box-sizing: border-box;
font: sans-serif;
font-size: var(--font-md);
line-height: var(--line-height-normal);
overflow-y: scroll;
}
body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
color: var(--color-text);
font-family: sans-serif;
font-family: var(--font-sans);
font-weight: normal;
margin: 0;
word-wrap: break-word;
}
a {
background-color: transparent;
color: var(--color-primary);
text-decoration: none;
font-weight: 500;
text-decoration: underline;
text-decoration-thickness: 1.5px;
text-underline-offset: 2px;
}
a:active,
a:hover {
outline-width: 0;
text-decoration: none;
}
abbr[title] {
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
cursor: help;
text-decoration: none;
}
b,
strong {
font-weight: inherit;
font-weight: bolder;
}
dfn {
font-style: italic;
}
h1 {
margin: 0;
margin-bottom: 3rem;
padding: 0;
line-height: var(--line-height-dense);
letter-spacing: -0.01em;
}
h1 > b {
color: var(--color-primary);
}
img {
border-style: none;
max-width: 100%;
}
code,
kbd,
pre,
samp {
font-family: var(--font-mono);
font-size: 1em;
line-height: inherit;
}
hr {
box-sizing: content-box;
overflow: visible;
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding: 0;
margin-bottom: calc(var(--space-4) - 1px);
background: hsla(0, 0%, 0%, 0.2);
border: none;
height: 1px;
}
* {
box-sizing: inherit;
}
*:before {
box-sizing: inherit;
}
*:after {
box-sizing: inherit;
}
hgroup,
img,
figure,
fieldset,
ul,
ol,
dl,
dd,
p {
margin: 0;
padding: 0;
margin-bottom: var(--space-4);
}
ul,
ol {
margin-left: var(--space-4);
list-style-position: outside;
list-style-image: none;
}
pre {
margin-left: 0;
margin-right: 0;
margin-top: 0;
margin-bottom: var(--space-4);
font-size: 0.875rem;
line-height: var(--line-height-normal);
background: hsla(0, 0%, 0%, 0.04);
border-radius: var(--border-radius);
overflow: auto;
word-wrap: normal;
padding: var(--space-4);
}
b,
strong,
dt,
th {
font-weight: bold;
}
li {
margin-bottom: calc(var(--space-4) / 2);
}
ol li,
ul li {
padding-left: 0;
}
li > ol,
li > ul {
margin-bottom: calc(var(--space-4) / 2);
margin-left: var(--space-4);
margin-top: calc(var(--space-4) / 2);
}
blockquote *:last-child {
margin-bottom: 0;
}
li *:last-child {
margin-bottom: 0;
}
p *:last-child {
margin-bottom: 0;
}
li > p {
margin-bottom: calc(var(--space-4) / 2);
}
p {
max-width: 680px;
}
code {
font-size: 0.875rem;
}
kbd {
font-size: 0.875rem;
}
samp {
font-size: 0.875rem;
}
abbr {
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
cursor: help;
}
acronym {
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
cursor: help;
}
tt,
code {
background-color: var(--color-code-bg);
border-radius: var(--border-radius);
color: var(--color-code);
font-family: var(--font-mono);
padding-bottom: 0.25em;
padding-top: 0.25em;
word-break: normal;
}
pre code {
background: none;
}
code:before,
code:after,
tt:before,
tt:after {
content: "\00a0";
letter-spacing: -0.2em;
}
pre code:before,
pre code:after,
pre tt:before,
pre tt:after {
content: none;
}

View File

@@ -0,0 +1,56 @@
/**
* Layout component that queries for data
* with Gatsby's useStaticQuery component
*
* See: https://www.gatsbyjs.com/docs/use-static-query/
*/
import * as React from 'react';
import PropTypes from 'prop-types';
import { useStaticQuery, graphql } from 'gatsby';
import Header from './header';
import './layout.css';
const Layout = ({ children }) => {
const data = useStaticQuery(graphql`
query SiteTitleQuery {
site {
siteMetadata {
title
}
}
}
`);
return (
<>
<Header siteTitle={data.site.siteMetadata?.title || `Title`} />
<div
style={{
margin: `0 auto`,
maxWidth: `var(--size-content)`,
padding: `var(--size-gutter)`,
}}
>
<main>{children}</main>
<footer
style={{
marginTop: `var(--space-5)`,
fontSize: `var(--font-sm)`,
}}
>
© {new Date().getFullYear()} &middot; Built with
{` `}
<a href="https://www.gatsbyjs.com">Gatsby</a>
</footer>
</div>
</>
);
};
Layout.propTypes = {
children: PropTypes.node.isRequired,
};
export default Layout;

View File

@@ -0,0 +1,12 @@
import * as React from 'react';
import Layout from '../components/layout';
const NotFoundPage = () => (
<Layout>
<h1>404: Not Found</h1>
<p>You just hit a route that doesn&#39;t exist... the sadness.</p>
</Layout>
);
export default NotFoundPage;

View File

@@ -0,0 +1,108 @@
import * as React from 'react';
import { Link } from 'gatsby';
import Layout from '../components/layout';
import * as styles from '../components/index.module.css';
const links = [
{
text: 'Tutorial',
url: 'https://www.gatsbyjs.com/docs/tutorial',
description:
"A great place to get started if you're new to web development. Designed to guide you through setting up your first Gatsby site.",
},
{
text: 'Examples',
url: 'https://github.com/gatsbyjs/gatsby/tree/master/examples',
description:
'A collection of websites ranging from very basic to complex/complete that illustrate how to accomplish specific tasks within your Gatsby sites.',
},
{
text: 'Plugin Library',
url: 'https://www.gatsbyjs.com/plugins',
description:
'Learn how to add functionality and customize your Gatsby site or app with thousands of plugins built by our amazing developer community.',
},
{
text: 'Build and Host',
url: 'https://www.gatsbyjs.com/cloud',
description:
'Now youre ready to show the world! Give your Gatsby site superpowers: Build and host on Gatsby Cloud. Get started for free!',
},
];
const samplePageLinks = [
{
text: 'Page 2',
url: 'page-2',
badge: false,
description:
'A simple example of linking to another page within a Gatsby site',
},
{ text: 'TypeScript', url: 'using-typescript' },
];
const moreLinks = [
{ text: 'Join us on Discord', url: 'https://gatsby.dev/discord' },
{
text: 'Documentation',
url: 'https://gatsbyjs.com/docs/',
},
{
text: 'Starters',
url: 'https://gatsbyjs.com/starters/',
},
{
text: 'Showcase',
url: 'https://gatsbyjs.com/showcase/',
},
{
text: 'Contributing',
url: 'https://www.gatsbyjs.com/contributing/',
},
{ text: 'Issues', url: 'https://github.com/gatsbyjs/gatsby/issues' },
];
const utmParameters = `?utm_source=starter&utm_medium=start-page&utm_campaign=default-starter`;
const IndexPage = () => (
<Layout>
<div className={styles.textCenter}>
<h1>
Welcome to <b>Gatsby!</b>
</h1>
<p className={styles.intro}>
<b>Example pages:</b>{' '}
{samplePageLinks.map((link, i) => (
<React.Fragment key={link.url}>
<Link to={link.url}>{link.text}</Link>
{i !== samplePageLinks.length - 1 && <> · </>}
</React.Fragment>
))}
<br />
Edit <code>src/pages/index.js</code> to update this page.
</p>
</div>
<ul className={styles.list}>
{links.map(link => (
<li key={link.url} className={styles.listItem}>
<a
className={styles.listItemLink}
href={`${link.url}${utmParameters}`}
>
{link.text}
</a>
<p className={styles.listItemDescription}>{link.description}</p>
</li>
))}
</ul>
{moreLinks.map((link, i) => (
<React.Fragment key={link.url}>
<a href={`${link.url}${utmParameters}`}>{link.text}</a>
{i !== moreLinks.length - 1 && <> · </>}
</React.Fragment>
))}
</Layout>
);
export default IndexPage;

View File

@@ -0,0 +1,14 @@
import * as React from 'react';
import { Link } from 'gatsby';
import Layout from '../components/layout';
const SecondPage = () => (
<Layout>
<h1>Hi from the second page</h1>
<p>Welcome to page 2</p>
<Link to="/">Go back to the homepage</Link>
</Layout>
);
export default SecondPage;

Some files were not shown because too many files have changed in this diff Show More