Compare commits

..

5 Commits

Author SHA1 Message Date
JJ Kasper
4bf6295d7a Publish Canary
- @vercel/build-utils@4.1.1-canary.0
 - vercel@25.1.1-canary.0
 - @vercel/client@12.0.2-canary.0
 - @vercel/go@2.0.2-canary.0
 - @vercel/next@3.0.2-canary.0
 - @vercel/node@2.1.1-canary.0
 - @vercel/python@3.0.2-canary.0
 - @vercel/redwood@1.0.2-canary.0
 - @vercel/remix@1.0.2-canary.0
 - @vercel/ruby@1.3.10-canary.0
 - @vercel/static-build@1.0.2-canary.0
2022-06-10 09:37:52 -05:00
JJ Kasper
a4001ce10b [next] Add handling for resolving _next/data URLs (#7935)
* Add handling for resolving _next/data URLs

* update check

* update routes

* remove extra routes

* Update resolving
2022-06-10 08:50:59 -05:00
Nathan Rajlich
2df3432d88 [node] Add zero-config support for root-level middleware.js/middleware.ts file (#7928)
Adds support for `config.middleware` property in `@vercel/node` to output an Edge Middleware (by including a catch-all route in the output).

Also updates the zero-config detection logic to schedule a build of root-level `middleware.js`/`middleware.ts` files with `@vercel/node` with this middleware option enabled.
2022-06-09 23:04:39 +00:00
chloetedder
bcfc19de12 [build-utils] Add getProjectPaths that will evaluate if there is a monorepo (#7826)
<img width="1174" alt="Screen Shot 2022-05-19 at 3 53 55 PM" src="https://user-images.githubusercontent.com/35947020/169402845-4eb70a24-9954-44ac-8726-a6b56161d58c.png">

If there is no workspace detected, we still want to check if there are potential projects in the monorepo. An example of this case would be:

<img width="202" alt="Screen Shot 2022-05-19 at 10 29 12 AM" src="https://user-images.githubusercontent.com/35947020/169336400-e69845e3-616e-4857-80fe-c2614a65352a.png">

The directories `backend/app-three`, `frontend/app-one`, `frontend/app-two` all have definitions within their `package.json` files that define a framework assosiated with the project. This is what distinguishes them from the `package.json` files within `backend` and `frontend`

`getProjectPaths` loops through a depth of 3 (this can be changed but in most cases of this it won't be more than 3). For each directory it recursively calls itself until it finds a `package.json` file. It then checks if the directory has a framework defined and if so it adds the path to the final return value of all projects.

If any `skipPaths` are sent into the function, no projects will be looked for within those path(s). This would include the case for a hybrid monorepo where a workspace has already been detected

### Related Issues

> Fixes #1
> Related to #2

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-06-09 15:15:25 +00:00
Matthew Stanciu
04381c669b [cli] Pull correct env variables in vc build command (#7933)
`vc build` runs `vc pull` if no project settings are found locally. By default, `vc pull` pulls development environment variables, but we want either preview or production environment variables in the context of `vc build`. This PR adds a flag to the internal `vc pull` call to pull the correct environment variables.

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [x] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-06-09 06:15:30 +00:00
50 changed files with 861 additions and 102 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/build-utils", "name": "@vercel/build-utils",
"version": "4.1.0", "version": "4.1.1-canary.0",
"license": "MIT", "license": "MIT",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.js", "types": "./dist/index.d.js",

View File

@@ -23,7 +23,7 @@ interface ErrorResponse {
} }
interface Options { interface Options {
tag?: 'canary' | 'latest' | string; tag?: string;
functions?: BuilderFunctions; functions?: BuilderFunctions;
ignoreBuildScript?: boolean; ignoreBuildScript?: boolean;
projectSettings?: ProjectSettings; projectSettings?: ProjectSettings;
@@ -278,7 +278,7 @@ export async function detectBuilders(
// and package.json can be served as static files // and package.json can be served as static files
frontendBuilder = { frontendBuilder = {
use: '@vercel/static', use: '@vercel/static',
src: '!{api/**,package.json}', src: '!{api/**,package.json,middleware.[jt]s}',
config: { config: {
zeroConfig: true, zeroConfig: true,
}, },
@@ -355,7 +355,16 @@ function maybeGetApiBuilder(
apiMatches: Builder[], apiMatches: Builder[],
options: Options options: Options
) { ) {
if (!fileName.startsWith('api/')) { const middleware =
fileName === 'middleware.js' || fileName === 'middleware.ts';
// Root-level Middleware file is handled by `@vercel/next`, so don't
// schedule a separate Builder when "nextjs" framework is selected
if (middleware && options.projectSettings?.framework === 'nextjs') {
return null;
}
if (!(fileName.startsWith('api/') || middleware)) {
return null; return null;
} }
@@ -381,7 +390,7 @@ function maybeGetApiBuilder(
const { fnPattern, func } = getFunction(fileName, options); const { fnPattern, func } = getFunction(fileName, options);
const use = (func && func.runtime) || (match && match.use); const use = func?.runtime || match?.use;
if (!use) { if (!use) {
return null; return null;
@@ -389,6 +398,10 @@ function maybeGetApiBuilder(
const config: Config = { zeroConfig: true }; const config: Config = { zeroConfig: true };
if (middleware) {
config.middleware = true;
}
if (fnPattern && func) { if (fnPattern && func) {
config.functions = { [fnPattern]: func }; config.functions = { [fnPattern]: func };
@@ -428,6 +441,7 @@ function getApiMatches() {
const config = { zeroConfig: true }; const config = { zeroConfig: true };
return [ return [
{ src: 'middleware.[jt]s', use: `@vercel/node`, config },
{ src: 'api/**/*.js', use: `@vercel/node`, config }, { src: 'api/**/*.js', use: `@vercel/node`, config },
{ src: 'api/**/*.mjs', use: `@vercel/node`, config }, { src: 'api/**/*.mjs', use: `@vercel/node`, config },
{ src: 'api/**/*.ts', use: `@vercel/node`, config }, { src: 'api/**/*.ts', use: `@vercel/node`, config },

View File

@@ -0,0 +1,60 @@
import { detectFramework } from './detect-framework';
import { DetectorFilesystem } from './detectors/filesystem';
import frameworks from '@vercel/frameworks';
const MAX_DEPTH_TRAVERSE = 3;
export interface GetProjectPathsOptions {
fs: DetectorFilesystem;
path?: string;
skipPaths?: string[];
depth?: number;
}
export type ProjectPath = string;
export const getProjectPaths = async ({
fs,
path,
skipPaths,
depth = MAX_DEPTH_TRAVERSE,
}: GetProjectPathsOptions): Promise<ProjectPath[]> => {
if (depth === 0) return [];
const allPaths: Array<ProjectPath> = [];
const topPath: string = path ?? './';
if (path && skipPaths?.includes(path)) {
return allPaths;
}
const framework = await detectFramework({
fs: fs.chdir(topPath),
frameworkList: frameworks,
});
if (framework !== null) allPaths.push(topPath);
if (depth > 1) {
const directoryContents = await fs.readdir(topPath);
const childDirectories = directoryContents.filter(
stat => stat.type === 'dir' && !skipPaths?.includes(stat.path)
);
const paths = (
await Promise.all(
childDirectories.map(({ path }) => {
return getProjectPaths({
fs,
path,
depth: depth - 1,
skipPaths,
});
})
)
).flat();
return [...paths, ...allPaths];
}
return allPaths;
};

View File

@@ -88,6 +88,7 @@ export {
} from './detect-builders'; } from './detect-builders';
export { detectFileSystemAPI } from './detect-file-system-api'; export { detectFileSystemAPI } from './detect-file-system-api';
export { detectFramework } from './detect-framework'; export { detectFramework } from './detect-framework';
export { getProjectPaths } from './get-project-paths';
export { DetectorFilesystem } from './detectors/filesystem'; export { DetectorFilesystem } from './detectors/filesystem';
export { readConfigFile } from './fs/read-config-file'; export { readConfigFile } from './fs/read-config-file';
export { normalizePath } from './fs/normalize-path'; export { normalizePath } from './fs/normalize-path';

View File

@@ -428,6 +428,8 @@ export interface BuildResultV2Typical {
export type BuildResultV2 = BuildResultV2Typical | BuildResultBuildOutput; export type BuildResultV2 = BuildResultV2Typical | BuildResultBuildOutput;
export interface BuildResultV3 { export interface BuildResultV3 {
// TODO: use proper `Route` type from `routing-utils` (perhaps move types to a common package)
routes?: any[];
output: Lambda | EdgeFunction; output: Lambda | EdgeFunction;
} }

View File

@@ -0,0 +1,15 @@
{
"name": "app-three",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
}

View File

@@ -0,0 +1,5 @@
{
"name": "backend",
"license": "MIT",
"version": "0.1.0"
}

View File

@@ -0,0 +1,15 @@
{
"name": "app-one",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
}

View File

@@ -0,0 +1,15 @@
{
"name": "app-one",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
}

View File

@@ -0,0 +1,5 @@
{
"name": "frontend",
"license": "MIT",
"version": "0.1.0"
}

View File

@@ -0,0 +1,15 @@
{
"name": "app-three",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
}

View File

@@ -0,0 +1,5 @@
{
"name": "backend",
"license": "MIT",
"version": "0.1.0"
}

View File

@@ -0,0 +1,15 @@
{
"name": "a",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"debug": "^4.3.2"
}
}

View File

@@ -0,0 +1,15 @@
{
"name": "b",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"cowsay": "^1.5.0"
}
}

View File

@@ -0,0 +1,9 @@
{
"name": "21-npm-workspaces",
"version": "1.0.0",
"private": true,
"workspaces": [
"a",
"b"
]
}

View File

@@ -0,0 +1,15 @@
{
"name": "app-three",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
}

View File

@@ -0,0 +1,5 @@
{
"name": "backend",
"license": "MIT",
"version": "0.1.0"
}

View File

@@ -0,0 +1,15 @@
{
"name": "app-one",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
}

View File

@@ -0,0 +1,15 @@
{
"name": "app-two",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
}

View File

@@ -0,0 +1,5 @@
{
"name": "frontend",
"license": "MIT",
"version": "0.1.0"
}

View File

@@ -0,0 +1,12 @@
{
"name": "app-one",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

View File

@@ -0,0 +1,15 @@
{
"name": "app-one",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
}

View File

@@ -0,0 +1,15 @@
{
"name": "app-two",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
}

View File

@@ -0,0 +1,5 @@
{
"name": "frontend",
"license": "MIT",
"version": "0.1.0"
}

View File

@@ -0,0 +1,15 @@
{
"name": "app-one",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
}

View File

@@ -78,7 +78,7 @@ describe('Test `detectBuilders`', () => {
expect(builders![0].use).toBe('@vercel/node'); expect(builders![0].use).toBe('@vercel/node');
expect(builders![0].src).toBe('api/users.js'); expect(builders![0].src).toBe('api/users.js');
expect(builders![1].use).toBe('@vercel/static'); expect(builders![1].use).toBe('@vercel/static');
expect(builders![1].src).toBe('!{api/**,package.json}'); expect(builders![1].src).toBe('!{api/**,package.json,middleware.[jt]s}');
expect(builders!.length).toBe(2); expect(builders!.length).toBe(2);
expect(errors).toBe(null); expect(errors).toBe(null);
}); });
@@ -89,7 +89,7 @@ describe('Test `detectBuilders`', () => {
expect(builders![0].use).toBe('@vercel/node'); expect(builders![0].use).toBe('@vercel/node');
expect(builders![0].src).toBe('api/[endpoint].js'); expect(builders![0].src).toBe('api/[endpoint].js');
expect(builders![1].use).toBe('@vercel/static'); expect(builders![1].use).toBe('@vercel/static');
expect(builders![1].src).toBe('!{api/**,package.json}'); expect(builders![1].src).toBe('!{api/**,package.json,middleware.[jt]s}');
expect(builders!.length).toBe(2); expect(builders!.length).toBe(2);
expect(errors).toBe(null); expect(errors).toBe(null);
}); });
@@ -144,7 +144,7 @@ describe('Test `detectBuilders`', () => {
expect(builders![0].use).toBe('@vercel/node'); expect(builders![0].use).toBe('@vercel/node');
expect(builders![0].src).toBe('api/endpoint.js'); expect(builders![0].src).toBe('api/endpoint.js');
expect(builders![1].use).toBe('@vercel/static'); expect(builders![1].use).toBe('@vercel/static');
expect(builders![1].src).toBe('!{api/**,package.json}'); expect(builders![1].src).toBe('!{api/**,package.json,middleware.[jt]s}');
expect(builders!.length).toBe(2); expect(builders!.length).toBe(2);
}); });
@@ -347,7 +347,7 @@ describe('Test `detectBuilders`', () => {
expect(builders![0].use).toBe('@vercel/node'); expect(builders![0].use).toBe('@vercel/node');
expect(builders![0].src).toBe('api/index.ts'); expect(builders![0].src).toBe('api/index.ts');
expect(builders![1].use).toBe('@vercel/static'); expect(builders![1].use).toBe('@vercel/static');
expect(builders![1].src).toBe('!{api/**,package.json}'); expect(builders![1].src).toBe('!{api/**,package.json,middleware.[jt]s}');
}); });
it('functions with nextjs', async () => { it('functions with nextjs', async () => {
@@ -1010,7 +1010,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
expect(builders![0].use).toBe('@vercel/node'); expect(builders![0].use).toBe('@vercel/node');
expect(builders![0].src).toBe('api/users.js'); expect(builders![0].src).toBe('api/users.js');
expect(builders![1].use).toBe('@vercel/static'); expect(builders![1].use).toBe('@vercel/static');
expect(builders![1].src).toBe('!{api/**,package.json}'); expect(builders![1].src).toBe('!{api/**,package.json,middleware.[jt]s}');
expect(builders!.length).toBe(2); expect(builders!.length).toBe(2);
expect(errors).toBe(null); expect(errors).toBe(null);
@@ -1032,7 +1032,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
expect(builders![0].use).toBe('@vercel/node'); expect(builders![0].use).toBe('@vercel/node');
expect(builders![0].src).toBe('api/[endpoint].js'); expect(builders![0].src).toBe('api/[endpoint].js');
expect(builders![1].use).toBe('@vercel/static'); expect(builders![1].use).toBe('@vercel/static');
expect(builders![1].src).toBe('!{api/**,package.json}'); expect(builders![1].src).toBe('!{api/**,package.json,middleware.[jt]s}');
expect(builders!.length).toBe(2); expect(builders!.length).toBe(2);
expect(errors).toBe(null); expect(errors).toBe(null);
}); });
@@ -1258,7 +1258,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
expect(builders![0].use).toBe('@vercel/node'); expect(builders![0].use).toBe('@vercel/node');
expect(builders![0].src).toBe('api/endpoint.js'); expect(builders![0].src).toBe('api/endpoint.js');
expect(builders![1].use).toBe('@vercel/static'); expect(builders![1].use).toBe('@vercel/static');
expect(builders![1].src).toBe('!{api/**,package.json}'); expect(builders![1].src).toBe('!{api/**,package.json,middleware.[jt]s}');
expect(builders!.length).toBe(2); expect(builders!.length).toBe(2);
expect(defaultRoutes!.length).toBe(2); expect(defaultRoutes!.length).toBe(2);
@@ -1288,7 +1288,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
expect(builders![0].use).toBe('@vercel/node'); expect(builders![0].use).toBe('@vercel/node');
expect(builders![0].src).toBe('api/version.js'); expect(builders![0].src).toBe('api/version.js');
expect(builders![1].use).toBe('@vercel/static'); expect(builders![1].use).toBe('@vercel/static');
expect(builders![1].src).toBe('!{api/**,package.json}'); expect(builders![1].src).toBe('!{api/**,package.json,middleware.[jt]s}');
expect(builders!.length).toBe(2); expect(builders!.length).toBe(2);
expect(defaultRoutes!.length).toBe(2); expect(defaultRoutes!.length).toBe(2);
@@ -1567,7 +1567,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
expect(builders![0].use).toBe('@vercel/node'); expect(builders![0].use).toBe('@vercel/node');
expect(builders![0].src).toBe('api/index.ts'); expect(builders![0].src).toBe('api/index.ts');
expect(builders![1].use).toBe('@vercel/static'); expect(builders![1].use).toBe('@vercel/static');
expect(builders![1].src).toBe('!{api/**,package.json}'); expect(builders![1].src).toBe('!{api/**,package.json,middleware.[jt]s}');
expect(errorRoutes!.length).toBe(1); expect(errorRoutes!.length).toBe(1);
expect((errorRoutes![0] as Source).status).toBe(404); expect((errorRoutes![0] as Source).status).toBe(404);
}); });
@@ -2228,6 +2228,55 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
expect(builders).toBe(null); expect(builders).toBe(null);
expect(errors).toBe(null); expect(errors).toBe(null);
}); });
it('no package.json + no build + root-level "middleware.js"', async () => {
const files = ['middleware.js', 'index.html', 'web/middleware.js'];
const { builders, errors } = await detectBuilders(files, null, {
featHandleMiss,
});
expect(builders![0].use).toBe('@vercel/node');
expect(builders![0].src).toBe('middleware.js');
expect(builders![0].config?.middleware).toEqual(true);
expect(builders![1].use).toBe('@vercel/static');
expect(builders![1].src).toBe('!{api/**,package.json,middleware.[jt]s}');
expect(builders!.length).toBe(2);
expect(errors).toBe(null);
});
it('no package.json + no build + root-level "middleware.ts"', async () => {
const files = ['middleware.ts', 'index.html', 'web/middleware.js'];
const { builders, errors } = await detectBuilders(files, null, {
featHandleMiss,
});
expect(builders![0].use).toBe('@vercel/node');
expect(builders![0].src).toBe('middleware.ts');
expect(builders![0].config?.middleware).toEqual(true);
expect(builders![1].use).toBe('@vercel/static');
expect(builders![1].src).toBe('!{api/**,package.json,middleware.[jt]s}');
expect(builders!.length).toBe(2);
expect(errors).toBe(null);
});
it('should not add middleware builder when "nextjs" framework is selected', async () => {
const files = ['package.json', 'pages/index.ts', 'middleware.ts'];
const projectSettings = {
framework: 'nextjs',
};
const { builders } = await detectBuilders(files, null, {
projectSettings,
featHandleMiss,
});
expect(builders).toEqual([
{
use: '@vercel/next',
src: 'package.json',
config: {
zeroConfig: true,
framework: projectSettings.framework,
},
},
]);
});
}); });
it('Test `detectRoutes`', async () => { it('Test `detectRoutes`', async () => {

View File

@@ -0,0 +1,70 @@
import path from 'path';
import { normalizePath } from '../src';
import { getProjectPaths, ProjectPath } from '../src/get-project-paths';
import { FixtureFilesystem } from './utils/fixture-filesystem';
describe.each<{
fixturePath: string;
resultPaths: ProjectPath[];
skipPaths?: ProjectPath[];
readdirCalls: number;
}>([
{
fixturePath: '32-monorepo-highly-nested',
resultPaths: [],
readdirCalls: 2,
},
{
fixturePath: '33-hybrid-monorepo',
resultPaths: ['backend/app-three'],
readdirCalls: 2,
skipPaths: ['frontend'],
},
{
fixturePath: '34-monorepo-no-workspaces',
resultPaths: ['backend/app-three', 'frontend/app-one', 'frontend/app-two'],
readdirCalls: 3,
},
{
fixturePath: '35-no-monorepo',
resultPaths: [],
readdirCalls: 1,
},
{
fixturePath: '36-monorepo-some-nested',
resultPaths: ['frontend/app-two'],
readdirCalls: 2,
},
{
fixturePath: '37-project-depth-one-level',
resultPaths: ['./'],
readdirCalls: 1,
},
])(
'`getProjectPaths()`',
({ resultPaths, readdirCalls, fixturePath, skipPaths }) => {
const testName =
resultPaths.length > 0
? `should detect ${resultPaths.join()} project${
resultPaths.length > 1 ? 's' : ''
} for ${fixturePath}`
: `should not detect any path for ${fixturePath}`;
it(testName, async () => {
const fixture = path.join(
__dirname,
'non-deployed-fixtures',
fixturePath
);
const fs = new FixtureFilesystem(fixture);
const mockReaddir = jest.fn().mockImplementation(fs.readdir);
const mockHasPath = jest.fn().mockImplementation(fs.hasPath);
fs.readdir = mockReaddir;
fs.hasPath = mockHasPath;
const actualPaths = await getProjectPaths({ fs, skipPaths });
const normalizedPaths = actualPaths.map(path => normalizePath(path));
expect(normalizedPaths).toEqual(resultPaths);
expect(fs.readdir).toHaveBeenCalledTimes(readdirCalls);
});
}
);

View File

@@ -1,6 +1,6 @@
{ {
"name": "vercel", "name": "vercel",
"version": "25.1.0", "version": "25.1.1-canary.0",
"preferGlobal": true, "preferGlobal": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "The command-line interface for Vercel", "description": "The command-line interface for Vercel",
@@ -42,15 +42,15 @@
"node": ">= 14" "node": ">= 14"
}, },
"dependencies": { "dependencies": {
"@vercel/build-utils": "4.1.0", "@vercel/build-utils": "4.1.1-canary.0",
"@vercel/go": "2.0.1", "@vercel/go": "2.0.2-canary.0",
"@vercel/next": "3.0.1", "@vercel/next": "3.0.2-canary.0",
"@vercel/node": "2.1.0", "@vercel/node": "2.1.1-canary.0",
"@vercel/python": "3.0.1", "@vercel/python": "3.0.2-canary.0",
"@vercel/redwood": "1.0.1", "@vercel/redwood": "1.0.2-canary.0",
"@vercel/remix": "1.0.1", "@vercel/remix": "1.0.2-canary.0",
"@vercel/ruby": "1.3.9", "@vercel/ruby": "1.3.10-canary.0",
"@vercel/static-build": "1.0.1", "@vercel/static-build": "1.0.2-canary.0",
"update-notifier": "5.1.0" "update-notifier": "5.1.0"
}, },
"devDependencies": { "devDependencies": {
@@ -94,7 +94,7 @@
"@types/which": "1.3.2", "@types/which": "1.3.2",
"@types/write-json-file": "2.2.1", "@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0", "@types/yauzl-promise": "2.1.0",
"@vercel/client": "12.0.1", "@vercel/client": "12.0.2-canary.0",
"@vercel/frameworks": "1.0.1", "@vercel/frameworks": "1.0.1",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"@zeit/fun": "0.11.2", "@zeit/fun": "0.11.2",

View File

@@ -101,6 +101,7 @@ export default async function main(client: Client): Promise<number> {
const argv = getArgs(client.argv.slice(2), { const argv = getArgs(client.argv.slice(2), {
'--cwd': String, '--cwd': String,
'--prod': Boolean, '--prod': Boolean,
'--yes': Boolean,
}); });
if (argv['--help']) { if (argv['--help']) {
@@ -114,12 +115,18 @@ export default async function main(client: Client): Promise<number> {
} }
const cwd = process.cwd(); const cwd = process.cwd();
// Build `target` influences which environment variables will be used
const target = argv['--prod'] ? 'production' : 'preview';
const yes = Boolean(argv['--yes']);
// TODO: read project settings from the API, fall back to local `project.json` if that fails // TODO: read project settings from the API, fall back to local `project.json` if that fails
// Read project settings, and pull them from Vercel if necessary // Read project settings, and pull them from Vercel if necessary
let project = await readProjectSettings(join(cwd, VERCEL_DIR)); let project = await readProjectSettings(join(cwd, VERCEL_DIR));
const isTTY = process.stdin.isTTY; const isTTY = process.stdin.isTTY;
while (!project?.settings) { while (!project?.settings) {
let confirmed = yes;
if (!confirmed) {
if (!isTTY) { if (!isTTY) {
client.output.print( client.output.print(
`No Project Settings found locally. Run ${cli.getCommandName( `No Project Settings found locally. Run ${cli.getCommandName(
@@ -129,27 +136,32 @@ export default async function main(client: Client): Promise<number> {
return 1; return 1;
} }
const confirmed = await confirm( confirmed = await confirm(
`No Project Settings found locally. Run ${cli.getCommandName( `No Project Settings found locally. Run ${cli.getCommandName(
'pull' 'pull'
)} for retrieving them?`, )} for retrieving them?`,
true true
); );
}
if (!confirmed) { if (!confirmed) {
client.output.print(`Aborted. No Project Settings retrieved.\n`); client.output.print(`Aborted. No Project Settings retrieved.\n`);
return 0; return 0;
} }
client.argv = []; const { argv: originalArgv } = client;
client.argv = [
...originalArgv.slice(0, 2),
'pull',
`--environment`,
target,
];
const result = await pull(client); const result = await pull(client);
if (result !== 0) { if (result !== 0) {
return result; return result;
} }
client.argv = originalArgv;
project = await readProjectSettings(join(cwd, VERCEL_DIR)); project = await readProjectSettings(join(cwd, VERCEL_DIR));
} }
// Build `target` influences which environment variables will be used
const target = argv['--prod'] ? 'production' : 'preview';
// TODO: load env vars from the API, fall back to local files if that fails // TODO: load env vars from the API, fall back to local files if that fails
const envPath = await checkExists([ const envPath = await checkExists([

View File

@@ -0,0 +1,7 @@
{
"orgId": ".",
"projectId": ".",
"settings": {
"framework": null
}
}

View File

@@ -0,0 +1 @@
<h1>Vercel</h1>

View File

@@ -0,0 +1 @@
export default req => new Response('middleware');

View File

@@ -0,0 +1,4 @@
{
"projectId": "vercel-pull-next",
"orgId": "team_dummy"
}

View File

@@ -0,0 +1 @@
<h1>Vercel</h1>

View File

@@ -1,8 +1,11 @@
import ms from 'ms'; import ms from 'ms';
import fs from 'fs-extra'; import fs from 'fs-extra';
import { join } from 'path'; import { join } from 'path';
import { client } from '../../mocks/client';
import build from '../../../src/commands/build'; import build from '../../../src/commands/build';
import { client } from '../../mocks/client';
import { defaultProject, useProject } from '../../mocks/project';
import { useTeams } from '../../mocks/team';
import { useUser } from '../../mocks/user';
jest.setTimeout(ms('1 minute')); jest.setTimeout(ms('1 minute'));
@@ -139,7 +142,7 @@ describe('build', () => {
require: '@vercel/static', require: '@vercel/static',
apiVersion: 2, apiVersion: 2,
use: '@vercel/static', use: '@vercel/static',
src: '!{api/**,package.json}', src: '!{api/**,package.json,middleware.[jt]s}',
config: { config: {
zeroConfig: true, zeroConfig: true,
}, },
@@ -204,7 +207,7 @@ describe('build', () => {
require: '@vercel/static', require: '@vercel/static',
apiVersion: 2, apiVersion: 2,
use: '@vercel/static', use: '@vercel/static',
src: '!{api/**,package.json}', src: '!{api/**,package.json,middleware.[jt]s}',
config: { config: {
zeroConfig: true, zeroConfig: true,
}, },
@@ -237,4 +240,135 @@ describe('build', () => {
delete process.env.__VERCEL_BUILD_RUNNING; delete process.env.__VERCEL_BUILD_RUNNING;
} }
}); });
it('should pull "preview" env vars by default', async () => {
const cwd = fixture('static-pull');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-pull-next',
name: 'vercel-pull-next',
});
const envFilePath = join(cwd, '.vercel', '.env.preview.local');
const projectJsonPath = join(cwd, '.vercel', 'project.json');
const originalProjectJson = await fs.readJSON(
join(cwd, '.vercel/project.json')
);
try {
process.chdir(cwd);
client.setArgv('build', '--yes');
const exitCode = await build(client);
expect(exitCode).toEqual(0);
const previewEnv = await fs.readFile(envFilePath, 'utf8');
const envFileHasPreviewEnv = previewEnv.includes(
'REDIS_CONNECTION_STRING'
);
expect(envFileHasPreviewEnv).toBeTruthy();
} finally {
await fs.remove(envFilePath);
await fs.writeJSON(projectJsonPath, originalProjectJson, { spaces: 2 });
process.chdir(originalCwd);
delete process.env.__VERCEL_BUILD_RUNNING;
}
});
it('should pull "production" env vars with `--prod`', async () => {
const cwd = fixture('static-pull');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-pull-next',
name: 'vercel-pull-next',
});
const envFilePath = join(cwd, '.vercel', '.env.production.local');
const projectJsonPath = join(cwd, '.vercel', 'project.json');
const originalProjectJson = await fs.readJSON(
join(cwd, '.vercel/project.json')
);
try {
process.chdir(cwd);
client.setArgv('build', '--yes', '--prod');
const exitCode = await build(client);
expect(exitCode).toEqual(0);
const prodEnv = await fs.readFile(envFilePath, 'utf8');
const envFileHasProductionEnv1 = prodEnv.includes(
'REDIS_CONNECTION_STRING'
);
expect(envFileHasProductionEnv1).toBeTruthy();
const envFileHasProductionEnv2 = prodEnv.includes(
'SQL_CONNECTION_STRING'
);
expect(envFileHasProductionEnv2).toBeTruthy();
} finally {
await fs.remove(envFilePath);
await fs.writeJSON(projectJsonPath, originalProjectJson, { spaces: 2 });
process.chdir(originalCwd);
delete process.env.__VERCEL_BUILD_RUNNING;
}
});
it('should build root-level `middleware.js` and exclude from static files', async () => {
const cwd = fixture('middleware');
const output = join(cwd, '.vercel/output');
try {
process.chdir(cwd);
const exitCode = await build(client);
expect(exitCode).toEqual(0);
// `builds.json` says that "@vercel/static" was run
const builds = await fs.readJSON(join(output, 'builds.json'));
expect(builds).toMatchObject({
target: 'preview',
builds: [
{
require: '@vercel/node',
apiVersion: 3,
use: '@vercel/node',
src: 'middleware.js',
config: {
zeroConfig: true,
middleware: true,
},
},
{
require: '@vercel/static',
apiVersion: 2,
use: '@vercel/static',
src: '!{api/**,package.json,middleware.[jt]s}',
config: {
zeroConfig: true,
},
},
],
});
// `config.json` includes the "middlewarePath" route
const config = await fs.readJSON(join(output, 'config.json'));
expect(config).toMatchObject({
version: 3,
routes: [
{ src: '/.*', middlewarePath: 'middleware', continue: true },
{ handle: 'filesystem' },
{ src: '^/api(/.*)?$', status: 404 },
{ handle: 'error' },
{ status: 404, src: '^(?!/api).*$', dest: '/404.html' },
],
});
// "static" directory contains `index.html`, but *not* `middleware.js`
const staticFiles = await fs.readdir(join(output, 'static'));
expect(staticFiles.sort()).toEqual(['index.html']);
// "functions" directory contains `middleware.func`
const functions = await fs.readdir(join(output, 'functions'));
expect(functions.sort()).toEqual(['middleware.func']);
} finally {
process.chdir(originalCwd);
delete process.env.__VERCEL_BUILD_RUNNING;
}
});
}); });

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/client", "name": "@vercel/client",
"version": "12.0.1", "version": "12.0.2-canary.0",
"main": "dist/index.js", "main": "dist/index.js",
"typings": "dist/index.d.ts", "typings": "dist/index.d.ts",
"homepage": "https://vercel.com", "homepage": "https://vercel.com",
@@ -42,7 +42,7 @@
] ]
}, },
"dependencies": { "dependencies": {
"@vercel/build-utils": "4.1.0", "@vercel/build-utils": "4.1.1-canary.0",
"@zeit/fetch": "5.2.0", "@zeit/fetch": "5.2.0",
"async-retry": "1.2.3", "async-retry": "1.2.3",
"async-sema": "3.0.0", "async-sema": "3.0.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/go", "name": "@vercel/go",
"version": "2.0.1", "version": "2.0.2-canary.0",
"license": "MIT", "license": "MIT",
"main": "./dist/index", "main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go", "homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
@@ -25,7 +25,7 @@
"@types/fs-extra": "^5.0.5", "@types/fs-extra": "^5.0.5",
"@types/node-fetch": "^2.3.0", "@types/node-fetch": "^2.3.0",
"@types/tar": "^4.0.0", "@types/tar": "^4.0.0",
"@vercel/build-utils": "4.1.0", "@vercel/build-utils": "4.1.1-canary.0",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"async-retry": "1.3.1", "async-retry": "1.3.1",
"execa": "^1.0.0", "execa": "^1.0.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/next", "name": "@vercel/next",
"version": "3.0.1", "version": "3.0.2-canary.0",
"license": "MIT", "license": "MIT",
"main": "./dist/index", "main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js", "homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
@@ -45,7 +45,7 @@
"@types/semver": "6.0.0", "@types/semver": "6.0.0",
"@types/text-table": "0.2.1", "@types/text-table": "0.2.1",
"@types/webpack-sources": "3.2.0", "@types/webpack-sources": "3.2.0",
"@vercel/build-utils": "4.1.0", "@vercel/build-utils": "4.1.1-canary.0",
"@vercel/nft": "0.19.1", "@vercel/nft": "0.19.1",
"@vercel/routing-utils": "1.13.4", "@vercel/routing-utils": "1.13.4",
"async-sema": "3.0.1", "async-sema": "3.0.1",

View File

@@ -1294,6 +1294,7 @@ export const build: BuildV2 = async ({
entryPath, entryPath,
baseDir, baseDir,
dataRoutes, dataRoutes,
buildId,
escapedBuildId, escapedBuildId,
outputDirectory, outputDirectory,
trailingSlashRedirects, trailingSlashRedirects,

View File

@@ -55,6 +55,7 @@ import prettyBytes from 'pretty-bytes';
// related PR: https://github.com/vercel/next.js/pull/30046 // related PR: https://github.com/vercel/next.js/pull/30046
const CORRECT_NOT_FOUND_ROUTES_VERSION = 'v12.0.1'; const CORRECT_NOT_FOUND_ROUTES_VERSION = 'v12.0.1';
const CORRECT_MIDDLEWARE_ORDER_VERSION = 'v12.1.7-canary.29'; const CORRECT_MIDDLEWARE_ORDER_VERSION = 'v12.1.7-canary.29';
const NEXT_DATA_MIDDLEWARE_RESOLVING_VERSION = 'v12.1.7-canary.33';
export async function serverBuild({ export async function serverBuild({
dynamicPages, dynamicPages,
@@ -65,6 +66,7 @@ export async function serverBuild({
workPath, workPath,
entryPath, entryPath,
nodeVersion, nodeVersion,
buildId,
escapedBuildId, escapedBuildId,
dynamicPrefix, dynamicPrefix,
entryDirectory, entryDirectory,
@@ -101,6 +103,7 @@ export async function serverBuild({
privateOutputs: { files: Files; routes: Route[] }; privateOutputs: { files: Files; routes: Route[] };
entryPath: string; entryPath: string;
dynamicPrefix: string; dynamicPrefix: string;
buildId: string;
escapedBuildId: string; escapedBuildId: string;
wildcardConfig: BuildResult['wildcard']; wildcardConfig: BuildResult['wildcard'];
nodeVersion: NodeVersion; nodeVersion: NodeVersion;
@@ -796,6 +799,10 @@ export async function serverBuild({
isCorrectMiddlewareOrder, isCorrectMiddlewareOrder,
}); });
const isNextDataServerResolving =
middleware.staticRoutes.length > 0 &&
semver.gte(nextVersion, NEXT_DATA_MIDDLEWARE_RESOLVING_VERSION);
const dynamicRoutes = await getDynamicRoutes( const dynamicRoutes = await getDynamicRoutes(
entryPath, entryPath,
entryDirectory, entryDirectory,
@@ -876,6 +883,53 @@ export async function serverBuild({
} }
} }
const normalizeNextDataRoute = isNextDataServerResolving
? [
// strip _next/data prefix for resolving
{
src: `^${path.join(
'/',
entryDirectory,
'/_next/data/',
escapedBuildId,
'/(.*).json'
)}`,
dest: `${path.join('/', entryDirectory, '/$1')}`,
continue: true,
override: true,
has: [
{
type: 'header',
key: 'x-nextjs-data',
},
],
},
]
: [];
const denormalizeNextDataRoute = isNextDataServerResolving
? [
{
src: '/(.*)',
has: [
{
type: 'header',
key: 'x-nextjs-data',
},
],
dest: `${path.join(
'/',
entryDirectory,
'/_next/data/',
buildId,
'/$1.json'
)}`,
continue: true,
override: true,
},
]
: [];
return { return {
wildcard: wildcardConfig, wildcard: wildcardConfig,
images: images:
@@ -900,6 +954,15 @@ export async function serverBuild({
...staticDirectoryFiles, ...staticDirectoryFiles,
...privateOutputs.files, ...privateOutputs.files,
...middleware.edgeFunctions, ...middleware.edgeFunctions,
...(isNextDataServerResolving
? {
__next_data_catchall: new FileBlob({
contentType: 'application/json',
mode: 0o644,
data: '{}',
}),
}
: {}),
}, },
routes: [ routes: [
/* /*
@@ -919,6 +982,9 @@ export async function serverBuild({
...privateOutputs.routes, ...privateOutputs.routes,
// normalize _next/data URL before processing redirects
...normalizeNextDataRoute,
...(i18n ...(i18n
? [ ? [
// Handle auto-adding current default locale to path based on // Handle auto-adding current default locale to path based on
@@ -1077,6 +1143,9 @@ export async function serverBuild({
}, },
]), ]),
// we need to undo _next/data normalize before checking filesystem
...denormalizeNextDataRoute,
// while middleware was in beta the order came right before // while middleware was in beta the order came right before
// handle: 'filesystem' we maintain this for older versions // handle: 'filesystem' we maintain this for older versions
// to prevent a local/deploy mismatch // to prevent a local/deploy mismatch
@@ -1098,6 +1167,11 @@ export async function serverBuild({
] ]
: []), : []),
// normalize _next/data URL before processing rewrites
...normalizeNextDataRoute,
...(!isNextDataServerResolving
? [
// No-op _next/data rewrite to trigger handle: 'rewrites' and then 404 // No-op _next/data rewrite to trigger handle: 'rewrites' and then 404
// if no match to prevent rewriting _next/data unexpectedly // if no match to prevent rewriting _next/data unexpectedly
{ {
@@ -1105,6 +1179,8 @@ export async function serverBuild({
dest: path.join('/', entryDirectory, '_next/data/$1'), dest: path.join('/', entryDirectory, '_next/data/$1'),
check: true, check: true,
}, },
]
: []),
// These need to come before handle: miss or else they are grouped // These need to come before handle: miss or else they are grouped
// with that routing section // with that routing section
@@ -1163,9 +1239,14 @@ export async function serverBuild({
// if there no rewrites // if there no rewrites
{ handle: 'rewrite' }, { handle: 'rewrite' },
// re-build /_next/data URL after resolving
...denormalizeNextDataRoute,
// /_next/data routes for getServerProps/getStaticProps pages // /_next/data routes for getServerProps/getStaticProps pages
...dataRoutes, ...dataRoutes,
...(!isNextDataServerResolving
? [
// ensure we 404 for non-existent _next/data routes before // ensure we 404 for non-existent _next/data routes before
// trying page dynamic routes // trying page dynamic routes
{ {
@@ -1173,11 +1254,30 @@ export async function serverBuild({
dest: path.join('/', entryDirectory, '404'), dest: path.join('/', entryDirectory, '404'),
status: 404, status: 404,
}, },
]
: []),
// Dynamic routes (must come after dataRoutes as dataRoutes are more // Dynamic routes (must come after dataRoutes as dataRoutes are more
// specific) // specific)
...dynamicRoutes, ...dynamicRoutes,
...(isNextDataServerResolving
? [
// add a catch-all data route so we don't 404 when getting
// middleware effects
{
src: `^${path.join(
'/',
entryDirectory,
'/_next/data/',
escapedBuildId,
'/(.*).json'
)}`,
dest: '__next_data_catchall',
},
]
: []),
// routes to call after a file has been matched // routes to call after a file has been matched
{ handle: 'hit' }, { handle: 'hit' },
// Before we handle static files we need to set proper caching headers // Before we handle static files we need to set proper caching headers

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/node", "name": "@vercel/node",
"version": "2.1.0", "version": "2.1.1-canary.0",
"license": "MIT", "license": "MIT",
"main": "./dist/index", "main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js", "homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
@@ -45,7 +45,7 @@
"@types/etag": "1.8.0", "@types/etag": "1.8.0",
"@types/jest": "27.4.1", "@types/jest": "27.4.1",
"@types/test-listen": "1.1.0", "@types/test-listen": "1.1.0",
"@vercel/build-utils": "4.1.0", "@vercel/build-utils": "4.1.1-canary.0",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"@vercel/nft": "0.19.1", "@vercel/nft": "0.19.1",
"@vercel/static-config": "2.0.1", "@vercel/static-config": "2.0.1",

View File

@@ -373,12 +373,38 @@ export const build: BuildV3 = async ({
); );
debug(`Trace complete [${Date.now() - traceTime}ms]`); debug(`Trace complete [${Date.now() - traceTime}ms]`);
let routes: BuildResultV3['routes'];
let output: BuildResultV3['output'] | undefined;
const handler = renameTStoJS(relative(baseDir, entrypointPath));
const outputName = config.zeroConfig
? handler.substring(0, handler.length - 3)
: handler;
// Will output an `EdgeFunction` for when `config.middleware = true`
// (i.e. for root-level "middleware" file) or if source code contains:
// `export const config = { runtime: 'experimental-edge' }`
let isEdgeFunction = false;
// Add a catch-all `route` for Middleware
if (config.middleware === true) {
routes = [
{
src: '/.*',
middlewarePath: config.zeroConfig
? outputName
: relative(baseDir, entrypointPath),
continue: true,
},
];
// Middleware is implicitly an Edge Function
isEdgeFunction = true;
}
if (!isEdgeFunction) {
const project = new Project(); const project = new Project();
const staticConfig = getConfig(project, entrypointPath); const staticConfig = getConfig(project, entrypointPath);
let output: BuildResultV3['output'] | undefined;
const handler = renameTStoJS(relative(baseDir, entrypointPath));
if (staticConfig?.runtime) { if (staticConfig?.runtime) {
if (!ALLOWED_RUNTIMES.includes(staticConfig.runtime)) { if (!ALLOWED_RUNTIMES.includes(staticConfig.runtime)) {
throw new Error( throw new Error(
@@ -387,22 +413,20 @@ export const build: BuildV3 = async ({
)} (must be one of: ${JSON.stringify(ALLOWED_RUNTIMES)})` )} (must be one of: ${JSON.stringify(ALLOWED_RUNTIMES)})`
); );
} }
if (staticConfig.runtime === 'experimental-edge') { isEdgeFunction = staticConfig.runtime === 'experimental-edge';
const name = config.zeroConfig }
? handler.substring(0, handler.length - 3) }
: handler;
if (isEdgeFunction) {
output = new EdgeFunction({ output = new EdgeFunction({
entrypoint: handler, entrypoint: handler,
files: preparedFiles, files: preparedFiles,
// TODO: remove - these two properties should not be required // TODO: remove - these two properties should not be required
name, name: outputName,
deploymentTarget: 'v8-worker', deploymentTarget: 'v8-worker',
}); });
} } else {
}
if (!output) {
// "nodejs" runtime is the default // "nodejs" runtime is the default
const shouldAddHelpers = !( const shouldAddHelpers = !(
config.helpers === false || process.env.NODEJS_HELPERS === '0' config.helpers === false || process.env.NODEJS_HELPERS === '0'
@@ -418,7 +442,7 @@ export const build: BuildV3 = async ({
}); });
} }
return { output }; return { routes, output };
}; };
export const prepareCache: PrepareCache = ({ repoRootPath, workPath }) => { export const prepareCache: PrepareCache = ({ repoRootPath, workPath }) => {

View File

@@ -0,0 +1,7 @@
export const config = {
runtime: 'experimental-edge',
};
export default req => {
return new Response(`RANDOMNESS_PLACEHOLDER:edge`);
};

View File

@@ -0,0 +1,14 @@
export default (req: Request) => {
const url = new URL(req.url);
const headers = new Headers({
'x-got-middleware': 'true',
});
if (url.pathname === '/' || url.pathname.startsWith('/api/')) {
// For `index.html` and `/api/edge.js`, pass through
headers.set('x-middleware-next', '1');
return new Response(null, { headers });
} else {
// For everything else, serve a custom response
return new Response(`RANDOMNESS_PLACEHOLDER:middleware`, { headers });
}
};

View File

@@ -0,0 +1,26 @@
{
"builds": [
{ "src": "api/**/*.js", "use": "@vercel/node" },
{
"src": "middleware.ts",
"use": "@vercel/node",
"config": { "middleware": true }
}
],
"probes": [
{
"path": "/api/edge.js",
"mustContain": "RANDOMNESS_PLACEHOLDER:edge",
"responseHeaders": {
"x-got-middleware": "true"
}
},
{
"path": "/non-exist",
"mustContain": "RANDOMNESS_PLACEHOLDER:middleware",
"responseHeaders": {
"x-got-middleware": "true"
}
}
]
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/python", "name": "@vercel/python",
"version": "3.0.1", "version": "3.0.2-canary.0",
"main": "./dist/index.js", "main": "./dist/index.js",
"license": "MIT", "license": "MIT",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python", "homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -23,7 +23,7 @@
"devDependencies": { "devDependencies": {
"@types/execa": "^0.9.0", "@types/execa": "^0.9.0",
"@types/jest": "27.4.1", "@types/jest": "27.4.1",
"@vercel/build-utils": "4.1.0", "@vercel/build-utils": "4.1.1-canary.0",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"execa": "^1.0.0", "execa": "^1.0.0",
"typescript": "4.3.4" "typescript": "4.3.4"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/redwood", "name": "@vercel/redwood",
"version": "1.0.1", "version": "1.0.2-canary.0",
"main": "./dist/index.js", "main": "./dist/index.js",
"license": "MIT", "license": "MIT",
"homepage": "https://vercel.com/docs", "homepage": "https://vercel.com/docs",
@@ -28,6 +28,6 @@
"@types/aws-lambda": "8.10.19", "@types/aws-lambda": "8.10.19",
"@types/node": "*", "@types/node": "*",
"@types/semver": "6.0.0", "@types/semver": "6.0.0",
"@vercel/build-utils": "4.1.0" "@vercel/build-utils": "4.1.1-canary.0"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/remix", "name": "@vercel/remix",
"version": "1.0.1", "version": "1.0.2-canary.0",
"license": "MIT", "license": "MIT",
"main": "./dist/index.js", "main": "./dist/index.js",
"homepage": "https://vercel.com/docs", "homepage": "https://vercel.com/docs",
@@ -27,7 +27,7 @@
"devDependencies": { "devDependencies": {
"@types/jest": "27.5.1", "@types/jest": "27.5.1",
"@types/node": "*", "@types/node": "*",
"@vercel/build-utils": "4.1.0", "@vercel/build-utils": "4.1.1-canary.0",
"typescript": "4.6.4" "typescript": "4.6.4"
} }
} }

View File

@@ -1,7 +1,7 @@
{ {
"name": "@vercel/ruby", "name": "@vercel/ruby",
"author": "Nathan Cahill <nathan@nathancahill.com>", "author": "Nathan Cahill <nathan@nathancahill.com>",
"version": "1.3.9", "version": "1.3.10-canary.0",
"license": "MIT", "license": "MIT",
"main": "./dist/index", "main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby", "homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
@@ -23,7 +23,7 @@
"devDependencies": { "devDependencies": {
"@types/fs-extra": "8.0.0", "@types/fs-extra": "8.0.0",
"@types/semver": "6.0.0", "@types/semver": "6.0.0",
"@vercel/build-utils": "4.1.0", "@vercel/build-utils": "4.1.1-canary.0",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"execa": "2.0.4", "execa": "2.0.4",
"fs-extra": "^7.0.1", "fs-extra": "^7.0.1",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/static-build", "name": "@vercel/static-build",
"version": "1.0.1", "version": "1.0.2-canary.0",
"license": "MIT", "license": "MIT",
"main": "./dist/index", "main": "./dist/index",
"homepage": "https://vercel.com/docs/build-step", "homepage": "https://vercel.com/docs/build-step",
@@ -37,7 +37,7 @@
"@types/ms": "0.7.31", "@types/ms": "0.7.31",
"@types/node-fetch": "2.5.4", "@types/node-fetch": "2.5.4",
"@types/promise-timeout": "1.3.0", "@types/promise-timeout": "1.3.0",
"@vercel/build-utils": "4.1.0", "@vercel/build-utils": "4.1.1-canary.0",
"@vercel/frameworks": "1.0.1", "@vercel/frameworks": "1.0.1",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "1.13.4", "@vercel/routing-utils": "1.13.4",