mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-12 04:22:14 +00:00
Compare commits
7 Commits
@vercel/py
...
@vercel/py
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71b83d5587 | ||
|
|
d9e5fdc5e4 | ||
|
|
58f479c603 | ||
|
|
d62461d952 | ||
|
|
e7f524defb | ||
|
|
bdefd0d05d | ||
|
|
ca522fc9f1 |
@@ -16,4 +16,4 @@ If you would not like to verify your domain, you can remove it from your account
|
||||
|
||||
#### Resources
|
||||
|
||||
- [Vercel Custom Domains Documentation](https://vercel.com/docs/v2/custom-domains)
|
||||
- [Vercel Custom Domains Documentation](https://vercel.com/docs/concepts/projects/custom-domains)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#### Why This Error Occurred
|
||||
|
||||
You ran `vercel dev` inside a project that contains a `vercel.json` file with `env` or `build.env` properties that use [Vercel Secrets](https://vercel.com/docs/v2/build-step#environment-variables).
|
||||
You ran `vercel dev` inside a project that contains a `vercel.json` file with `env` or `build.env` properties that use [Vercel Secrets](https://vercel.com/docs/concepts/projects/environment-variables).
|
||||
|
||||
In order to use environment variables in your project locally that have values defined using the Vercel Secrets format (e.g. `@my-secret-value`), you will need to provide the value as an environment variable using a `.env`.
|
||||
|
||||
@@ -24,4 +24,4 @@ TEST=value
|
||||
|
||||
In the above example, `TEST` represents the name of the environment variable and `value` its value.
|
||||
|
||||
For more information on Environment Variables in development, [see the documentation](https://vercel.com/docs/v2/build-step#environment-variables).
|
||||
For more information on Environment Variables in development, [see the documentation](https://vercel.com/docs/concepts/projects/environment-variables).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "2.15.1",
|
||||
"version": "2.15.2-canary.1",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -538,7 +538,7 @@ function getMissingBuildScriptError() {
|
||||
code: 'missing_build_script',
|
||||
message:
|
||||
'Your `package.json` file is missing a `build` property inside the `scripts` property.' +
|
||||
'\nLearn More: https://vercel.com/docs/v2/platform/frequently-asked-questions#missing-build-script',
|
||||
'\nLearn More: https://vercel.link/missing-build-script',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import assert from 'assert';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import debug from '../debug';
|
||||
import Sema from 'async-sema';
|
||||
import spawn from 'cross-spawn';
|
||||
import { SpawnOptions } from 'child_process';
|
||||
import { deprecate } from 'util';
|
||||
import debug from '../debug';
|
||||
import { NowBuildError } from '../errors';
|
||||
import { Meta, PackageJson, NodeVersion, Config } from '../types';
|
||||
import { getSupportedNodeVersion, getLatestNodeVersion } from './node-version';
|
||||
import { readConfigFile } from './read-config-file';
|
||||
|
||||
// Only allow one `runNpmInstall()` invocation to run concurrently
|
||||
const runNpmInstallSema = new Sema(1);
|
||||
|
||||
export type CliType = 'yarn' | 'npm' | 'pnpm';
|
||||
|
||||
export interface ScanParentDirsResult {
|
||||
@@ -17,6 +21,11 @@ export interface ScanParentDirsResult {
|
||||
* "yarn", "npm", or "pnpm" depending on the presence of lockfiles.
|
||||
*/
|
||||
cliType: CliType;
|
||||
/**
|
||||
* The file path of found `package.json` file, or `undefined` if none was
|
||||
* found.
|
||||
*/
|
||||
packageJsonPath?: string;
|
||||
/**
|
||||
* The contents of found `package.json` file, when the `readPackageJson`
|
||||
* option is enabled.
|
||||
@@ -237,12 +246,13 @@ export async function scanParentDirs(
|
||||
|
||||
let cliType: CliType = 'yarn';
|
||||
let packageJson: PackageJson | undefined;
|
||||
let packageJsonPath: string | undefined;
|
||||
let currentDestPath = destPath;
|
||||
let lockfileVersion: number | undefined;
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
const packageJsonPath = path.join(currentDestPath, 'package.json');
|
||||
packageJsonPath = path.join(currentDestPath, 'package.json');
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
if (await fs.pathExists(packageJsonPath)) {
|
||||
// Only read the contents of the *first* `package.json` file found,
|
||||
@@ -293,7 +303,7 @@ export async function scanParentDirs(
|
||||
currentDestPath = newDestPath;
|
||||
}
|
||||
|
||||
return { cliType, packageJson, lockfileVersion };
|
||||
return { cliType, packageJson, lockfileVersion, packageJsonPath };
|
||||
}
|
||||
|
||||
export async function walkParentDirs({
|
||||
@@ -319,55 +329,87 @@ export async function walkParentDirs({
|
||||
return null;
|
||||
}
|
||||
|
||||
function isSet<T>(v: any): v is Set<T> {
|
||||
return v?.constructor?.name === 'Set';
|
||||
}
|
||||
|
||||
export async function runNpmInstall(
|
||||
destPath: string,
|
||||
args: string[] = [],
|
||||
spawnOpts?: SpawnOptions,
|
||||
meta?: Meta,
|
||||
nodeVersion?: NodeVersion
|
||||
) {
|
||||
): Promise<boolean> {
|
||||
if (meta?.isDev) {
|
||||
debug('Skipping dependency installation because dev mode is enabled');
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(path.isAbsolute(destPath));
|
||||
debug(`Installing to ${destPath}`);
|
||||
|
||||
const { cliType, lockfileVersion } = await scanParentDirs(destPath);
|
||||
const opts: SpawnOptionsExtended = { cwd: destPath, ...spawnOpts };
|
||||
const env = opts.env ? { ...opts.env } : { ...process.env };
|
||||
delete env.NODE_ENV;
|
||||
opts.env = getEnvForPackageManager({
|
||||
cliType,
|
||||
lockfileVersion,
|
||||
nodeVersion,
|
||||
env,
|
||||
});
|
||||
let commandArgs: string[];
|
||||
try {
|
||||
await runNpmInstallSema.acquire();
|
||||
const { cliType, packageJsonPath, lockfileVersion } = await scanParentDirs(
|
||||
destPath
|
||||
);
|
||||
|
||||
if (cliType === 'npm') {
|
||||
opts.prettyCommand = 'npm install';
|
||||
commandArgs = args
|
||||
.filter(a => a !== '--prefer-offline')
|
||||
.concat(['install', '--no-audit', '--unsafe-perm']);
|
||||
} else if (cliType === 'pnpm') {
|
||||
// PNPM's install command is similar to NPM's but without the audit nonsense
|
||||
// @see options https://pnpm.io/cli/install
|
||||
opts.prettyCommand = 'pnpm install';
|
||||
commandArgs = args
|
||||
.filter(a => a !== '--prefer-offline')
|
||||
.concat(['install', '--unsafe-perm']);
|
||||
} else {
|
||||
opts.prettyCommand = 'yarn install';
|
||||
commandArgs = ['install', ...args];
|
||||
// Only allow `runNpmInstall()` to run once per `package.json`
|
||||
// when doing a default install (no additional args)
|
||||
if (meta && packageJsonPath && args.length === 0) {
|
||||
if (!isSet<string>(meta.runNpmInstallSet)) {
|
||||
meta.runNpmInstallSet = new Set<string>();
|
||||
}
|
||||
if (isSet<string>(meta.runNpmInstallSet)) {
|
||||
if (meta.runNpmInstallSet.has(packageJsonPath)) {
|
||||
return false;
|
||||
} else {
|
||||
meta.runNpmInstallSet.add(packageJsonPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const installTime = Date.now();
|
||||
console.log('Installing dependencies...');
|
||||
debug(`Installing to ${destPath}`);
|
||||
|
||||
const opts: SpawnOptionsExtended = { cwd: destPath, ...spawnOpts };
|
||||
const env = opts.env ? { ...opts.env } : { ...process.env };
|
||||
delete env.NODE_ENV;
|
||||
opts.env = getEnvForPackageManager({
|
||||
cliType,
|
||||
lockfileVersion,
|
||||
nodeVersion,
|
||||
env,
|
||||
});
|
||||
let commandArgs: string[];
|
||||
|
||||
if (cliType === 'npm') {
|
||||
opts.prettyCommand = 'npm install';
|
||||
commandArgs = args
|
||||
.filter(a => a !== '--prefer-offline')
|
||||
.concat(['install', '--no-audit', '--unsafe-perm']);
|
||||
} else if (cliType === 'pnpm') {
|
||||
// PNPM's install command is similar to NPM's but without the audit nonsense
|
||||
// @see options https://pnpm.io/cli/install
|
||||
opts.prettyCommand = 'pnpm install';
|
||||
commandArgs = args
|
||||
.filter(a => a !== '--prefer-offline')
|
||||
.concat(['install', '--unsafe-perm']);
|
||||
} else {
|
||||
opts.prettyCommand = 'yarn install';
|
||||
commandArgs = ['install', ...args];
|
||||
}
|
||||
|
||||
if (process.env.NPM_ONLY_PRODUCTION) {
|
||||
commandArgs.push('--production');
|
||||
}
|
||||
|
||||
await spawnAsync(cliType, commandArgs, opts);
|
||||
debug(`Install complete [${Date.now() - installTime}ms]`);
|
||||
return true;
|
||||
} finally {
|
||||
runNpmInstallSema.release();
|
||||
}
|
||||
|
||||
if (process.env.NPM_ONLY_PRODUCTION) {
|
||||
commandArgs.push('--production');
|
||||
}
|
||||
|
||||
return spawnAsync(cliType, commandArgs, opts);
|
||||
}
|
||||
|
||||
export function getEnvForPackageManager({
|
||||
|
||||
@@ -356,7 +356,29 @@ export interface Images {
|
||||
formats?: ImageFormat[];
|
||||
}
|
||||
|
||||
export interface BuildResultV2 {
|
||||
/**
|
||||
* If a Builder ends up creating filesystem outputs conforming to
|
||||
* the Build Output API, then the Builder should return this type.
|
||||
*/
|
||||
export interface BuildResultBuildOutput {
|
||||
/**
|
||||
* Version number of the Build Output API that was created.
|
||||
* Currently only `3` is a valid value.
|
||||
* @example 3
|
||||
*/
|
||||
buildOutputVersion: 3;
|
||||
/**
|
||||
* Filesystem path to the Build Output directory.
|
||||
* @example "/path/to/.vercel/output"
|
||||
*/
|
||||
buildOutputPath: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* When a Builder implements `version: 2`, the `build()` function is expected
|
||||
* to return this type.
|
||||
*/
|
||||
export interface BuildResultV2Typical {
|
||||
// TODO: use proper `Route` type from `routing-utils` (perhaps move types to a common package)
|
||||
routes?: any[];
|
||||
images?: Images;
|
||||
@@ -369,6 +391,8 @@ export interface BuildResultV2 {
|
||||
}>;
|
||||
}
|
||||
|
||||
export type BuildResultV2 = BuildResultV2Typical | BuildResultBuildOutput;
|
||||
|
||||
export interface BuildResultV3 {
|
||||
output: Lambda;
|
||||
}
|
||||
|
||||
4
packages/build-utils/test/fixtures/02-zero-config-api/yarn.lock
vendored
Normal file
4
packages/build-utils/test/fixtures/02-zero-config-api/yarn.lock
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
37
packages/build-utils/test/unit.test.ts
vendored
37
packages/build-utils/test/unit.test.ts
vendored
@@ -15,6 +15,7 @@ import {
|
||||
runPackageJsonScript,
|
||||
scanParentDirs,
|
||||
FileBlob,
|
||||
Meta,
|
||||
} from '../src';
|
||||
|
||||
async function expectBuilderError(promise: Promise<any>, pattern: string) {
|
||||
@@ -413,3 +414,39 @@ it('should detect pnpm Workspaces', async () => {
|
||||
expect(result.cliType).toEqual('pnpm');
|
||||
expect(result.lockfileVersion).toEqual(5.3);
|
||||
});
|
||||
|
||||
it('should only invoke `runNpmInstall()` once per `package.json` file (serial)', async () => {
|
||||
const meta: Meta = {};
|
||||
const fixture = path.join(__dirname, 'fixtures', '02-zero-config-api');
|
||||
const apiDir = path.join(fixture, 'api');
|
||||
const run1 = await runNpmInstall(apiDir, [], undefined, meta);
|
||||
expect(run1).toEqual(true);
|
||||
expect(
|
||||
(meta.runNpmInstallSet as Set<string>).has(
|
||||
path.join(fixture, 'package.json')
|
||||
)
|
||||
).toEqual(true);
|
||||
const run2 = await runNpmInstall(apiDir, [], undefined, meta);
|
||||
expect(run2).toEqual(false);
|
||||
const run3 = await runNpmInstall(fixture, [], undefined, meta);
|
||||
expect(run3).toEqual(false);
|
||||
});
|
||||
|
||||
it('should only invoke `runNpmInstall()` once per `package.json` file (parallel)', async () => {
|
||||
const meta: Meta = {};
|
||||
const fixture = path.join(__dirname, 'fixtures', '02-zero-config-api');
|
||||
const apiDir = path.join(fixture, 'api');
|
||||
const [run1, run2, run3] = await Promise.all([
|
||||
runNpmInstall(apiDir, [], undefined, meta),
|
||||
runNpmInstall(apiDir, [], undefined, meta),
|
||||
runNpmInstall(fixture, [], undefined, meta),
|
||||
]);
|
||||
expect(run1).toEqual(true);
|
||||
expect(run2).toEqual(false);
|
||||
expect(run3).toEqual(false);
|
||||
expect(
|
||||
(meta.runNpmInstallSet as Set<string>).has(
|
||||
path.join(fixture, 'package.json')
|
||||
)
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "24.1.0",
|
||||
"version": "24.1.1-canary.1",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -43,11 +43,11 @@
|
||||
"node": ">= 12"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.15.1",
|
||||
"@vercel/go": "1.3.2",
|
||||
"@vercel/node": "1.14.1",
|
||||
"@vercel/python": "2.2.2",
|
||||
"@vercel/ruby": "1.3.2",
|
||||
"@vercel/build-utils": "2.15.2-canary.1",
|
||||
"@vercel/go": "1.3.3-canary.1",
|
||||
"@vercel/node": "1.14.2-canary.1",
|
||||
"@vercel/python": "2.2.3-canary.1",
|
||||
"@vercel/ruby": "1.3.3-canary.1",
|
||||
"update-notifier": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -88,11 +88,10 @@
|
||||
"@types/update-notifier": "5.1.0",
|
||||
"@types/which": "1.3.2",
|
||||
"@types/write-json-file": "2.2.1",
|
||||
"@vercel/client": "10.4.1",
|
||||
"@vercel/client": "10.4.2-canary.1",
|
||||
"@vercel/fetch-retry": "5.0.3",
|
||||
"@vercel/frameworks": "0.7.1",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/nft": "0.17.5",
|
||||
"@zeit/fun": "0.11.2",
|
||||
"@zeit/source-map-support": "0.6.2",
|
||||
"ajv": "6.12.2",
|
||||
|
||||
@@ -73,7 +73,7 @@ export default async function set(
|
||||
if (args.length === 0) {
|
||||
output.error(
|
||||
`To ship to production, optionally configure your domains (${link(
|
||||
'https://vercel.com/docs/v2/custom-domains'
|
||||
'https://vercel.link/domain-configuration'
|
||||
)}) and run ${getCommandName(`--prod`)}.`
|
||||
);
|
||||
return 1;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "10.4.1",
|
||||
"version": "10.4.2-canary.1",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"homepage": "https://vercel.com",
|
||||
@@ -41,7 +41,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.15.1",
|
||||
"@vercel/build-utils": "2.15.2-canary.1",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "3.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/go",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.3-canary.1",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
|
||||
@@ -24,7 +24,7 @@
|
||||
"@types/fs-extra": "^5.0.5",
|
||||
"@types/node-fetch": "^2.3.0",
|
||||
"@types/tar": "^4.0.0",
|
||||
"@vercel/build-utils": "2.15.1",
|
||||
"@vercel/build-utils": "2.15.2-canary.1",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"async-retry": "1.3.1",
|
||||
"execa": "^1.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/node",
|
||||
"version": "1.14.1",
|
||||
"version": "1.14.2-canary.1",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
||||
@@ -32,9 +32,9 @@
|
||||
"@types/cookie": "0.3.3",
|
||||
"@types/etag": "1.8.0",
|
||||
"@types/test-listen": "1.1.0",
|
||||
"@vercel/build-utils": "2.15.1",
|
||||
"@vercel/build-utils": "2.15.2-canary.1",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/nft": "0.17.5",
|
||||
"@vercel/nft": "0.18.1",
|
||||
"content-type": "1.0.4",
|
||||
"cookie": "0.4.0",
|
||||
"etag": "1.8.1",
|
||||
|
||||
@@ -86,7 +86,6 @@ async function downloadInstallAndBundle({
|
||||
meta,
|
||||
}: DownloadOptions) {
|
||||
const downloadedFiles = await download(files, workPath, meta);
|
||||
|
||||
const entrypointFsDirname = join(workPath, dirname(entrypoint));
|
||||
const nodeVersion = await getNodeVersion(
|
||||
entrypointFsDirname,
|
||||
@@ -95,16 +94,7 @@ async function downloadInstallAndBundle({
|
||||
meta
|
||||
);
|
||||
const spawnOpts = getSpawnOptions(meta, nodeVersion);
|
||||
|
||||
if (meta.isDev) {
|
||||
debug('Skipping dependency installation because dev mode is enabled');
|
||||
} else {
|
||||
const installTime = Date.now();
|
||||
console.log('Installing dependencies...');
|
||||
await runNpmInstall(entrypointFsDirname, [], spawnOpts, meta, nodeVersion);
|
||||
debug(`Install complete [${Date.now() - installTime}ms]`);
|
||||
}
|
||||
|
||||
await runNpmInstall(entrypointFsDirname, [], spawnOpts, meta, nodeVersion);
|
||||
const entrypointPath = downloadedFiles[entrypoint].fsPath;
|
||||
return { entrypointPath, entrypointFsDirname, nodeVersion, spawnOpts };
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/python",
|
||||
"version": "2.2.2",
|
||||
"version": "2.2.3-canary.1",
|
||||
"main": "./dist/index.js",
|
||||
"license": "MIT",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
|
||||
@@ -20,7 +20,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/execa": "^0.9.0",
|
||||
"@vercel/build-utils": "2.15.1",
|
||||
"@vercel/build-utils": "2.15.2-canary.1",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"execa": "^1.0.0",
|
||||
"typescript": "4.3.4"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/redwood",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1-canary.1",
|
||||
"main": "./dist/index.js",
|
||||
"license": "MIT",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
@@ -18,7 +18,7 @@
|
||||
"prepublishOnly": "node build.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/nft": "0.17.5",
|
||||
"@vercel/nft": "0.18.1",
|
||||
"@vercel/routing-utils": "1.13.1",
|
||||
"semver": "6.1.1"
|
||||
},
|
||||
@@ -26,6 +26,6 @@
|
||||
"@types/aws-lambda": "8.10.19",
|
||||
"@types/node": "*",
|
||||
"@types/semver": "6.0.0",
|
||||
"@vercel/build-utils": "2.15.1"
|
||||
"@vercel/build-utils": "2.15.2-canary.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,10 +105,7 @@ export const build: BuildV2 = async ({
|
||||
console.log(`Skipping "install" command...`);
|
||||
}
|
||||
} else {
|
||||
console.log('Installing dependencies...');
|
||||
const installTime = Date.now();
|
||||
await runNpmInstall(entrypointFsDirname, [], spawnOpts, meta, nodeVersion);
|
||||
debug(`Install complete [${Date.now() - installTime}ms]`);
|
||||
}
|
||||
|
||||
if (meta.isDev) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@vercel/ruby",
|
||||
"author": "Nathan Cahill <nathan@nathancahill.com>",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.3-canary.1",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
|
||||
@@ -22,7 +22,7 @@
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "8.0.0",
|
||||
"@types/semver": "6.0.0",
|
||||
"@vercel/build-utils": "2.15.1",
|
||||
"@vercel/build-utils": "2.15.2-canary.1",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"execa": "2.0.4",
|
||||
"fs-extra": "^7.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/static-build",
|
||||
"version": "0.23.1",
|
||||
"version": "0.23.2-canary.1",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/build-step",
|
||||
@@ -14,17 +14,28 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node build",
|
||||
"test-unit": "jest --env node --verbose --runInBand --bail test/unit.test.js",
|
||||
"test-unit": "jest --env node --verbose --bail test/build.test.ts test/prepare-cache.test.ts",
|
||||
"test-integration-once": "jest --env node --verbose --runInBand --bail test/integration.test.js",
|
||||
"prepublishOnly": "node build"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "ts-jest/presets/default",
|
||||
"testEnvironment": "node",
|
||||
"globals": {
|
||||
"ts-jest": {
|
||||
"diagnostics": true,
|
||||
"isolatedModules": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/aws-lambda": "8.10.64",
|
||||
"@types/cross-spawn": "6.0.0",
|
||||
"@types/jest": "27.4.1",
|
||||
"@types/ms": "0.7.31",
|
||||
"@types/node-fetch": "2.5.4",
|
||||
"@types/promise-timeout": "1.3.0",
|
||||
"@vercel/build-utils": "2.15.1",
|
||||
"@vercel/build-utils": "2.15.2-canary.1",
|
||||
"@vercel/frameworks": "0.7.1",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/routing-utils": "1.13.1",
|
||||
|
||||
@@ -4,7 +4,7 @@ import fetch from 'node-fetch';
|
||||
import getPort from 'get-port';
|
||||
import isPortReachable from 'is-port-reachable';
|
||||
import frameworks, { Framework } from '@vercel/frameworks';
|
||||
import { ChildProcess, SpawnOptions } from 'child_process';
|
||||
import type { ChildProcess, SpawnOptions } from 'child_process';
|
||||
import { existsSync, readFileSync, statSync, readdirSync } from 'fs';
|
||||
import { cpus } from 'os';
|
||||
import {
|
||||
@@ -30,14 +30,12 @@ import {
|
||||
NowBuildError,
|
||||
scanParentDirs,
|
||||
} from '@vercel/build-utils';
|
||||
import { Route, Source } from '@vercel/routing-utils';
|
||||
import {
|
||||
readBuildOutputDirectory,
|
||||
readBuildOutputConfig,
|
||||
} from './utils/read-build-output';
|
||||
import type { Route, Source } from '@vercel/routing-utils';
|
||||
import * as BuildOutputV1 from './utils/build-output-v1';
|
||||
import * as BuildOutputV3 from './utils/build-output-v3';
|
||||
import * as GatsbyUtils from './utils/gatsby';
|
||||
import * as NuxtUtils from './utils/nuxt';
|
||||
import { ImagesConfig, BuildConfig } from './utils/_shared';
|
||||
import type { ImagesConfig, BuildConfig } from './utils/_shared';
|
||||
|
||||
const sleep = (n: number) => new Promise(resolve => setTimeout(resolve, n));
|
||||
|
||||
@@ -255,7 +253,7 @@ async function fetchBinary(url: string, framework: string, version: string) {
|
||||
throw new NowBuildError({
|
||||
code: 'STATIC_BUILD_BINARY_NOT_FOUND',
|
||||
message: `Version ${version} of ${framework} does not exist. Please specify a different one.`,
|
||||
link: 'https://vercel.com/docs/v2/build-step#framework-versioning',
|
||||
link: 'https://vercel.link/framework-versioning',
|
||||
});
|
||||
}
|
||||
await spawnAsync(`curl -sSL ${url} | tar -zx -C /usr/local/bin`, [], {
|
||||
@@ -284,11 +282,8 @@ export const build: BuildV2 = async ({
|
||||
);
|
||||
|
||||
const pkg = getPkg(entrypoint, workPath);
|
||||
|
||||
const devScript = pkg ? getScriptName(pkg, 'dev', config) : null;
|
||||
|
||||
const framework = getFramework(config, pkg);
|
||||
|
||||
const devCommand = getCommand('dev', pkg, config, framework);
|
||||
const buildCommand = getCommand('build', pkg, config, framework);
|
||||
const installCommand = getCommand('install', pkg, config, framework);
|
||||
@@ -387,7 +382,7 @@ export const build: BuildV2 = async ({
|
||||
Node.js will load 'false' as a string, not a boolean, so it's truthy still.
|
||||
This is to ensure we don't accidentally break other packages that check
|
||||
if process.env.CI is true somewhere.
|
||||
|
||||
|
||||
https://github.com/facebook/create-react-app/issues/2453
|
||||
https://github.com/facebook/create-react-app/pull/2501
|
||||
https://github.com/vercel/community/discussions/30
|
||||
@@ -412,10 +407,7 @@ export const build: BuildV2 = async ({
|
||||
|
||||
if (!config.zeroConfig) {
|
||||
debug('Detected "builds" - not zero config');
|
||||
printInstall();
|
||||
const installTime = Date.now();
|
||||
await runNpmInstall(entrypointDir, [], spawnOpts, meta, nodeVersion);
|
||||
debug(`Install complete [${Date.now() - installTime}ms]`);
|
||||
isNpmInstall = true;
|
||||
} else if (typeof installCommand === 'string') {
|
||||
if (installCommand.trim()) {
|
||||
@@ -487,11 +479,7 @@ export const build: BuildV2 = async ({
|
||||
isPipInstall = true;
|
||||
}
|
||||
if (pkg) {
|
||||
console.log('Detected package.json');
|
||||
printInstall();
|
||||
const installTime = Date.now();
|
||||
await runNpmInstall(entrypointDir, [], spawnOpts, meta, nodeVersion);
|
||||
debug(`Install complete [${Date.now() - installTime}ms]`);
|
||||
isNpmInstall = true;
|
||||
}
|
||||
}
|
||||
@@ -637,6 +625,30 @@ export const build: BuildV2 = async ({
|
||||
|
||||
const outputDirPrefix = path.join(workPath, path.dirname(entrypoint));
|
||||
|
||||
// If the Build Command or Framework output files according to the
|
||||
// Build Output v3 API, then stop processing here in `static-build`
|
||||
// since the output is already in its final form.
|
||||
const buildOutputPath = await BuildOutputV3.getBuildOutputDirectory(
|
||||
outputDirPrefix
|
||||
);
|
||||
|
||||
if (buildOutputPath) {
|
||||
// Ensure that `vercel build` is being used for this Deployment
|
||||
if (!meta.cliVersion) {
|
||||
let buildCommandName: string;
|
||||
if (buildCommand) buildCommandName = `"${buildCommand}"`;
|
||||
else if (framework) buildCommandName = framework.name;
|
||||
else buildCommandName = 'the "build" script';
|
||||
throw new Error(
|
||||
`Detected Build Output v3 from ${buildCommandName}, but this Deployment is not using \`vercel build\`.\nPlease set the \`ENABLE_VC_BUILD=1\` environment variable.`
|
||||
);
|
||||
}
|
||||
return {
|
||||
buildOutputVersion: 3,
|
||||
buildOutputPath,
|
||||
};
|
||||
}
|
||||
|
||||
if (framework) {
|
||||
const outputDirName = config.outputDirectory
|
||||
? config.outputDirectory
|
||||
@@ -656,7 +668,7 @@ export const build: BuildV2 = async ({
|
||||
}
|
||||
}
|
||||
|
||||
const extraOutputs = await readBuildOutputDirectory({
|
||||
const extraOutputs = await BuildOutputV1.readBuildOutputDirectory({
|
||||
workPath,
|
||||
nodeVersion,
|
||||
});
|
||||
@@ -750,7 +762,7 @@ export const prepareCache: PrepareCache = async ({
|
||||
const cacheFiles: Files = {};
|
||||
|
||||
// File System API v1 cache files
|
||||
const buildConfig = await readBuildOutputConfig<BuildConfig>({
|
||||
const buildConfig = await BuildOutputV1.readBuildOutputConfig<BuildConfig>({
|
||||
workPath,
|
||||
configFileName: 'build.json',
|
||||
});
|
||||
|
||||
23
packages/static-build/src/utils/build-output-v3.ts
Normal file
23
packages/static-build/src/utils/build-output-v3.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { join } from 'path';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const BUILD_OUTPUT_DIR = '.vercel/output';
|
||||
|
||||
/**
|
||||
* Returns the path to the Build Output v3 directory when the
|
||||
* `config.json` file was created by the framework / build script,
|
||||
* or `undefined` if the framework did not create the v3 output.
|
||||
*/
|
||||
export async function getBuildOutputDirectory(
|
||||
path: string
|
||||
): Promise<string | undefined> {
|
||||
try {
|
||||
const outputDir = join(path, BUILD_OUTPUT_DIR);
|
||||
const configPath = join(outputDir, 'config.json');
|
||||
await fs.stat(configPath);
|
||||
return outputDir;
|
||||
} catch (err: any) {
|
||||
if (err.code !== 'ENOENT') throw err;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -30,9 +30,7 @@ export async function injectVercelAnalyticsPlugin(dir: string) {
|
||||
`Injecting Gatsby.js analytics plugin "${gatsbyPluginPackageName}" to \`${gatsbyConfigPath}\``
|
||||
);
|
||||
|
||||
const pkgJson: DeepWriteable<PackageJson> = (await readPackageJson(
|
||||
dir
|
||||
)) as DeepWriteable<PackageJson>;
|
||||
const pkgJson = (await readPackageJson(dir)) as DeepWriteable<PackageJson>;
|
||||
if (!pkgJson.dependencies) {
|
||||
pkgJson.dependencies = {};
|
||||
}
|
||||
|
||||
7
packages/static-build/test/build-fixtures/09-build-output-v3/build.js
Executable file
7
packages/static-build/test/build-fixtures/09-build-output-v3/build.js
Executable file
@@ -0,0 +1,7 @@
|
||||
const fs = require('fs');
|
||||
fs.mkdirSync('.vercel/output/static', { recursive: true });
|
||||
fs.writeFileSync('.vercel/output/config.json', '{}');
|
||||
fs.writeFileSync(
|
||||
'.vercel/output/static/index.html',
|
||||
'<h1>Build Output API</h1>'
|
||||
);
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "09-build-output-v3",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "node build.js"
|
||||
}
|
||||
}
|
||||
54
packages/static-build/test/build.test.ts
vendored
Normal file
54
packages/static-build/test/build.test.ts
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
import path from 'path';
|
||||
import { build } from '../src';
|
||||
|
||||
describe('build()', () => {
|
||||
it('should detect Builder Output v3', async () => {
|
||||
const workPath = path.join(
|
||||
__dirname,
|
||||
'build-fixtures',
|
||||
'09-build-output-v3'
|
||||
);
|
||||
const buildResult = await build({
|
||||
files: {},
|
||||
entrypoint: 'package.json',
|
||||
workPath,
|
||||
config: {},
|
||||
meta: {
|
||||
skipDownload: true,
|
||||
cliVersion: '0.0.0',
|
||||
},
|
||||
});
|
||||
if ('output' in buildResult) {
|
||||
throw new Error('Unexpected `output` in build result');
|
||||
}
|
||||
expect(buildResult.buildOutputVersion).toEqual(3);
|
||||
expect(buildResult.buildOutputPath).toEqual(
|
||||
path.join(workPath, '.vercel/output')
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an Error with Builder Output v3 without `vercel build`', async () => {
|
||||
let err;
|
||||
const workPath = path.join(
|
||||
__dirname,
|
||||
'build-fixtures',
|
||||
'09-build-output-v3'
|
||||
);
|
||||
try {
|
||||
await build({
|
||||
files: {},
|
||||
entrypoint: 'package.json',
|
||||
workPath,
|
||||
config: {},
|
||||
meta: {
|
||||
skipDownload: true,
|
||||
},
|
||||
});
|
||||
} catch (_err: any) {
|
||||
err = _err;
|
||||
}
|
||||
expect(err.message).toEqual(
|
||||
`Detected Build Output v3 from the "build" script, but this Deployment is not using \`vercel build\`.\nPlease set the \`ENABLE_VC_BUILD=1\` environment variable.`
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,12 +1,13 @@
|
||||
const { prepareCache } = require('../dist');
|
||||
const path = require('path');
|
||||
import path from 'path';
|
||||
import { prepareCache } from '../src';
|
||||
|
||||
describe('prepareCache', () => {
|
||||
describe('prepareCache()', () => {
|
||||
test('should cache node_modules and .shadow-cljs', async () => {
|
||||
const files = await prepareCache({
|
||||
config: { zeroConfig: true },
|
||||
workPath: path.resolve(__dirname, './cache-fixtures/default'),
|
||||
entrypoint: 'index.js',
|
||||
files: {},
|
||||
});
|
||||
|
||||
expect(files['node_modules/file']).toBeDefined();
|
||||
@@ -19,6 +20,7 @@ describe('prepareCache', () => {
|
||||
config: { zeroConfig: true },
|
||||
workPath: path.resolve(__dirname, './cache-fixtures/withCacheConfig'),
|
||||
entrypoint: 'index.js',
|
||||
files: {},
|
||||
});
|
||||
|
||||
expect(files['node_modules/file']).toBeUndefined();
|
||||
@@ -32,6 +34,7 @@ describe('prepareCache', () => {
|
||||
config: { zeroConfig: true },
|
||||
workPath: path.resolve(__dirname, './cache-fixtures/gatsby'),
|
||||
entrypoint: 'package.json',
|
||||
files: {},
|
||||
});
|
||||
|
||||
expect(files['node_modules/file2']).toBeDefined();
|
||||
@@ -45,6 +48,7 @@ describe('prepareCache', () => {
|
||||
config: { zeroConfig: true, framework: 'jekyll' },
|
||||
workPath: path.resolve(__dirname, './cache-fixtures/jekyll'),
|
||||
entrypoint: 'Gemfile',
|
||||
files: {},
|
||||
});
|
||||
|
||||
expect(files['vendor/bundle/b1']).toBeDefined();
|
||||
4
packages/static-build/test/tsconfig.json
vendored
Normal file
4
packages/static-build/test/tsconfig.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"include": ["*.test.ts"]
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"outDir": "dist",
|
||||
"types": ["node"],
|
||||
"types": ["node", "jest"],
|
||||
"strict": true,
|
||||
"target": "es2018"
|
||||
},
|
||||
|
||||
55
yarn.lock
55
yarn.lock
@@ -2180,6 +2180,14 @@
|
||||
jest-diff "^27.0.0"
|
||||
pretty-format "^27.0.0"
|
||||
|
||||
"@types/jest@27.4.1":
|
||||
version "27.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.1.tgz#185cbe2926eaaf9662d340cc02e548ce9e11ab6d"
|
||||
integrity sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==
|
||||
dependencies:
|
||||
jest-matcher-utils "^27.0.0"
|
||||
pretty-format "^27.0.0"
|
||||
|
||||
"@types/js-yaml@3.12.1":
|
||||
version "3.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.1.tgz#5c6f4a1eabca84792fbd916f0cb40847f123c656"
|
||||
@@ -2560,10 +2568,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@vercel/ncc/-/ncc-0.24.0.tgz#a2e8783a185caa99b5d8961a57dfc9665de16296"
|
||||
integrity sha512-crqItMcIwCkvdXY/V3/TzrHJQx6nbIaRqE1cOopJhgGX6izvNov40SmD//nS5flfEvdK54YGjwVVq+zG6crjOg==
|
||||
|
||||
"@vercel/nft@0.17.5":
|
||||
version "0.17.5"
|
||||
resolved "https://registry.yarnpkg.com/@vercel/nft/-/nft-0.17.5.tgz#eab288a3786b8bd6fc08c0ef0b70d162984d1643"
|
||||
integrity sha512-6n4uXmfkcHAmkI4rJlwFJb8yvWuH6uDOi5qme0yGC1B/KmWJ66dERupdAj9uj7eEmgM7N3bKNY5zOYE7cKZE1g==
|
||||
"@vercel/nft@0.18.1":
|
||||
version "0.18.1"
|
||||
resolved "https://registry.yarnpkg.com/@vercel/nft/-/nft-0.18.1.tgz#d1681f7fd538168553d8df19fc3761ca643ebb73"
|
||||
integrity sha512-i2zmXs8ueqCe/dmQ0fzQk9MmXoqdqxR1ZsxPN8poHZfc3NQES1WTEsXkEoaL+RV5BKZi04lNpkoGwtqcxERAOQ==
|
||||
dependencies:
|
||||
"@mapbox/node-pre-gyp" "^1.0.5"
|
||||
acorn "^8.6.0"
|
||||
@@ -4677,6 +4685,11 @@ diff-sequences@^27.0.6:
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723"
|
||||
integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==
|
||||
|
||||
diff-sequences@^27.5.1:
|
||||
version "27.5.1"
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327"
|
||||
integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==
|
||||
|
||||
diff@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
||||
@@ -7160,6 +7173,16 @@ jest-diff@^27.3.1:
|
||||
jest-get-type "^27.3.1"
|
||||
pretty-format "^27.3.1"
|
||||
|
||||
jest-diff@^27.5.1:
|
||||
version "27.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def"
|
||||
integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==
|
||||
dependencies:
|
||||
chalk "^4.0.0"
|
||||
diff-sequences "^27.5.1"
|
||||
jest-get-type "^27.5.1"
|
||||
pretty-format "^27.5.1"
|
||||
|
||||
jest-docblock@^27.0.6:
|
||||
version "27.0.6"
|
||||
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.0.6.tgz#cc78266acf7fe693ca462cbbda0ea4e639e4e5f3"
|
||||
@@ -7213,6 +7236,11 @@ jest-get-type@^27.3.1:
|
||||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.3.1.tgz#a8a2b0a12b50169773099eee60a0e6dd11423eff"
|
||||
integrity sha512-+Ilqi8hgHSAdhlQ3s12CAVNd8H96ZkQBfYoXmArzZnOfAtVAJEiPDBirjByEblvG/4LPJmkL+nBqPO3A1YJAEg==
|
||||
|
||||
jest-get-type@^27.5.1:
|
||||
version "27.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1"
|
||||
integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==
|
||||
|
||||
jest-haste-map@^27.3.1:
|
||||
version "27.3.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.3.1.tgz#7656fbd64bf48bda904e759fc9d93e2c807353ee"
|
||||
@@ -7265,6 +7293,16 @@ jest-leak-detector@^27.3.1:
|
||||
jest-get-type "^27.3.1"
|
||||
pretty-format "^27.3.1"
|
||||
|
||||
jest-matcher-utils@^27.0.0:
|
||||
version "27.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab"
|
||||
integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==
|
||||
dependencies:
|
||||
chalk "^4.0.0"
|
||||
jest-diff "^27.5.1"
|
||||
jest-get-type "^27.5.1"
|
||||
pretty-format "^27.5.1"
|
||||
|
||||
jest-matcher-utils@^27.3.1:
|
||||
version "27.3.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.3.1.tgz#257ad61e54a6d4044e080d85dbdc4a08811e9c1c"
|
||||
@@ -9540,6 +9578,15 @@ pretty-format@^27.3.1:
|
||||
ansi-styles "^5.0.0"
|
||||
react-is "^17.0.1"
|
||||
|
||||
pretty-format@^27.5.1:
|
||||
version "27.5.1"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
|
||||
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
ansi-styles "^5.0.0"
|
||||
react-is "^17.0.1"
|
||||
|
||||
pretty-ms@^5.0.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-5.1.0.tgz#b906bdd1ec9e9799995c372e2b1c34f073f95384"
|
||||
|
||||
Reference in New Issue
Block a user