mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-11 12:57:46 +00:00
Compare commits
16 Commits
@vercel/py
...
@vercel/py
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b61f049f11 | ||
|
|
16e28f326b | ||
|
|
276397c940 | ||
|
|
85d7311199 | ||
|
|
b8114b8b39 | ||
|
|
d63e8d3187 | ||
|
|
11d3dd04aa | ||
|
|
46bf95ee36 | ||
|
|
92252468c2 | ||
|
|
71b83d5587 | ||
|
|
d9e5fdc5e4 | ||
|
|
58f479c603 | ||
|
|
d62461d952 | ||
|
|
e7f524defb | ||
|
|
bdefd0d05d | ||
|
|
ca522fc9f1 |
@@ -6,6 +6,7 @@
|
||||
!.yarnrc
|
||||
!yarn.lock
|
||||
!package.json
|
||||
!turbo.json
|
||||
|
||||
# api
|
||||
!api/
|
||||
@@ -14,4 +15,4 @@
|
||||
# packages
|
||||
!packages/
|
||||
!packages/frameworks
|
||||
!packages/frameworks/**
|
||||
!packages/frameworks/**
|
||||
@@ -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).
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Dojo](https://dojo.io) site that can be
|
||||
|
||||
Deploy your own Dojo project with Vercel.
|
||||
|
||||
[](https://vercel.com/new/clone?repository-url=https://github.com/vercel/vercel/tree/main/dojo&template=dojo)
|
||||
[](https://vercel.com/new/clone?repository-url=https://github.com/vercel/vercel/tree/main/examples/dojo&template=dojo)
|
||||
|
||||
### How We Created This Example
|
||||
|
||||
|
||||
15
package.json
15
package.json
@@ -14,19 +14,6 @@
|
||||
"dependencies": {
|
||||
"lerna": "3.16.4"
|
||||
},
|
||||
"turbo": {
|
||||
"baseBranch": "origin/main",
|
||||
"pipeline": {
|
||||
"build": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"outputs": [
|
||||
"dist/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "4.28.0",
|
||||
"@typescript-eslint/parser": "4.28.0",
|
||||
@@ -44,7 +31,7 @@
|
||||
"prettier": "2.3.1",
|
||||
"ts-eager": "2.0.2",
|
||||
"ts-jest": "27.0.4",
|
||||
"turbo": "1.1.9"
|
||||
"turbo": "1.2.2"
|
||||
},
|
||||
"scripts": {
|
||||
"lerna": "lerna",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "2.15.1",
|
||||
"version": "2.15.2-canary.2",
|
||||
"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({
|
||||
|
||||
@@ -332,6 +332,7 @@ export interface ProjectSettings {
|
||||
sourceFilesOutsideRootDirectory?: boolean;
|
||||
directoryListing?: boolean;
|
||||
gitForkProtection?: boolean;
|
||||
commandForIgnoringBuildStep?: string | null;
|
||||
}
|
||||
|
||||
export interface BuilderV2 {
|
||||
@@ -356,7 +357,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 +392,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.3",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -43,14 +43,15 @@
|
||||
"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.2",
|
||||
"@vercel/go": "1.3.3-canary.2",
|
||||
"@vercel/node": "1.14.2-canary.3",
|
||||
"@vercel/python": "2.2.3-canary.2",
|
||||
"@vercel/ruby": "1.3.3-canary.2",
|
||||
"update-notifier": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alex_neo/jest-expect-message": "1.0.5",
|
||||
"@next/env": "11.1.2",
|
||||
"@sentry/node": "5.5.0",
|
||||
"@sindresorhus/slugify": "0.11.0",
|
||||
@@ -69,6 +70,7 @@
|
||||
"@types/http-proxy": "1.16.2",
|
||||
"@types/inquirer": "7.3.1",
|
||||
"@types/jest": "27.0.1",
|
||||
"@types/jest-expect-message": "1.0.3",
|
||||
"@types/load-json-file": "2.0.7",
|
||||
"@types/mime-types": "2.1.0",
|
||||
"@types/minimatch": "3.0.3",
|
||||
@@ -88,11 +90,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.2",
|
||||
"@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",
|
||||
@@ -177,6 +178,9 @@
|
||||
"isolatedModules": true
|
||||
}
|
||||
},
|
||||
"setupFilesAfterEnv": [
|
||||
"@alex_neo/jest-expect-message"
|
||||
],
|
||||
"verbose": false,
|
||||
"testEnvironment": "node",
|
||||
"testMatch": [
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -195,6 +195,14 @@ export default async (client: Client) => {
|
||||
let sourceFilesOutsideRootDirectory: boolean | undefined = true;
|
||||
|
||||
if (status === 'not_linked') {
|
||||
// In the future this will need to be implemented in both the CLI and vercel.com/new at the same time
|
||||
if (localConfig?.projectSettings) {
|
||||
output.error(
|
||||
'Unexpected property detected in vercel.json: "projectSettings"'
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const shouldStartSetup =
|
||||
autoConfirm ||
|
||||
(await confirm(
|
||||
@@ -460,7 +468,10 @@ export default async (client: Client) => {
|
||||
|
||||
if (!localConfig.builds || localConfig.builds.length === 0) {
|
||||
// Only add projectSettings for zero config deployments
|
||||
createArgs.projectSettings = { sourceFilesOutsideRootDirectory };
|
||||
createArgs.projectSettings = {
|
||||
...localConfig.projectSettings,
|
||||
sourceFilesOutsideRootDirectory,
|
||||
};
|
||||
}
|
||||
|
||||
deployment = await createDeploy(
|
||||
@@ -492,7 +503,10 @@ export default async (client: Client) => {
|
||||
);
|
||||
|
||||
// deploy again, but send projectSettings this time
|
||||
createArgs.projectSettings = settings;
|
||||
createArgs.projectSettings = {
|
||||
...settings,
|
||||
...localConfig.projectSettings,
|
||||
};
|
||||
|
||||
deployStamp = stamp();
|
||||
createArgs.deployStamp = deployStamp;
|
||||
|
||||
9
packages/cli/src/commands/env/index.ts
vendored
9
packages/cli/src/commands/env/index.ts
vendored
@@ -115,9 +115,11 @@ export default async function main(client: Client) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
|
||||
const cwd = argv['--cwd'] || process.cwd();
|
||||
const subArgs = argv._.slice(1);
|
||||
const { subcommand, args } = getSubcommand(subArgs, COMMAND_CONFIG);
|
||||
const { output, config } = client;
|
||||
const link = await getLinkedProject(client);
|
||||
const link = await getLinkedProject(client, cwd);
|
||||
if (link.status === 'error') {
|
||||
return link.exitCode;
|
||||
} else if (link.status === 'not_linked') {
|
||||
@@ -144,7 +146,8 @@ export default async function main(client: Client) {
|
||||
ProjectEnvTarget.Development,
|
||||
argv,
|
||||
args,
|
||||
output
|
||||
output,
|
||||
cwd
|
||||
);
|
||||
default:
|
||||
output.error(getInvalidSubcommand(COMMAND_CONFIG));
|
||||
|
||||
8
packages/cli/src/commands/env/pull.ts
vendored
8
packages/cli/src/commands/env/pull.ts
vendored
@@ -49,7 +49,7 @@ export default async function pull(
|
||||
opts: Partial<Options>,
|
||||
args: string[],
|
||||
output: Output,
|
||||
cwd: string = process.cwd()
|
||||
cwd: string
|
||||
) {
|
||||
if (args.length > 1) {
|
||||
output.error(
|
||||
@@ -81,7 +81,7 @@ export default async function pull(
|
||||
}
|
||||
|
||||
output.print(
|
||||
`Downloading Development Environment Variables for Project ${chalk.bold(
|
||||
`Downloading "${environment}" Environment Variables for Project ${chalk.bold(
|
||||
project.name
|
||||
)}\n`
|
||||
);
|
||||
@@ -99,7 +99,9 @@ export default async function pull(
|
||||
const records = exposeSystemEnvs(
|
||||
projectEnvs,
|
||||
systemEnvValues,
|
||||
project.autoExposeSystemEnvs
|
||||
project.autoExposeSystemEnvs,
|
||||
undefined,
|
||||
environment
|
||||
);
|
||||
|
||||
const contents =
|
||||
|
||||
@@ -4,7 +4,6 @@ import Client from '../util/client';
|
||||
import { ProjectEnvTarget } from '../types';
|
||||
import { emoji, prependEmoji } from '../util/emoji';
|
||||
import getArgs from '../util/get-args';
|
||||
import handleError from '../util/handle-error';
|
||||
import setupAndLink from '../util/link/setup-and-link';
|
||||
import logo from '../util/output/logo';
|
||||
import stamp from '../util/output/stamp';
|
||||
@@ -69,19 +68,14 @@ function processArgs(client: Client) {
|
||||
}
|
||||
|
||||
function parseArgs(client: Client) {
|
||||
try {
|
||||
const argv = processArgs(client);
|
||||
const argv = processArgs(client);
|
||||
|
||||
if (argv['--help']) {
|
||||
help();
|
||||
return 2;
|
||||
}
|
||||
|
||||
return argv;
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
return 1;
|
||||
if (argv['--help']) {
|
||||
help();
|
||||
return 2;
|
||||
}
|
||||
|
||||
return argv;
|
||||
}
|
||||
|
||||
type LinkResult = {
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
import { Output } from '../output';
|
||||
// @ts-ignore
|
||||
import Now from '../../util';
|
||||
import { VercelConfig } from '../dev/types';
|
||||
import { Org } from '../../types';
|
||||
import ua from '../ua';
|
||||
import { linkFolderToProject } from '../projects/link';
|
||||
@@ -43,7 +42,6 @@ export default async function processDeployment({
|
||||
uploadStamp: () => string;
|
||||
deployStamp: () => string;
|
||||
quiet: boolean;
|
||||
nowConfig?: VercelConfig;
|
||||
force?: boolean;
|
||||
withCache?: boolean;
|
||||
org: Org;
|
||||
@@ -62,7 +60,6 @@ export default async function processDeployment({
|
||||
deployStamp,
|
||||
force,
|
||||
withCache,
|
||||
nowConfig,
|
||||
quiet,
|
||||
prebuilt,
|
||||
rootDirectory,
|
||||
@@ -104,11 +101,7 @@ export default async function processDeployment({
|
||||
const indications = [];
|
||||
|
||||
try {
|
||||
for await (const event of createDeployment(
|
||||
clientOptions,
|
||||
requestBody,
|
||||
nowConfig
|
||||
)) {
|
||||
for await (const event of createDeployment(clientOptions, requestBody)) {
|
||||
if (['tip', 'notice', 'warning'].includes(event.type)) {
|
||||
indications.push(event);
|
||||
}
|
||||
|
||||
@@ -160,7 +160,6 @@ export default class Now extends EventEmitter {
|
||||
uploadStamp,
|
||||
deployStamp,
|
||||
quiet,
|
||||
nowConfig,
|
||||
force: forceNew,
|
||||
withCache,
|
||||
org,
|
||||
|
||||
@@ -227,6 +227,7 @@ export default async function setupAndLink(
|
||||
return { status: 'linked', org, project };
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
|
||||
return { status: 'error', exitCode: 1 };
|
||||
}
|
||||
}
|
||||
|
||||
75
packages/cli/test/commands/env.test.ts
Normal file
75
packages/cli/test/commands/env.test.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import env from '../../src/commands/env';
|
||||
import { setupFixture } from '../helpers/setup-fixture';
|
||||
import { client } from '../mocks/client';
|
||||
import { defaultProject, useProject } from '../mocks/project';
|
||||
import { useTeams } from '../mocks/team';
|
||||
import { useUser } from '../mocks/user';
|
||||
|
||||
describe('env', () => {
|
||||
describe('pull', () => {
|
||||
it('should handle pulling', async () => {
|
||||
const cwd = setupFixture('vercel-env-pull');
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'vercel-env-pull',
|
||||
name: 'vercel-env-pull',
|
||||
});
|
||||
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
|
||||
const exitCode = await env(client);
|
||||
expect(exitCode, client.outputBuffer).toEqual(0);
|
||||
|
||||
const rawDevEnv = await fs.readFile(path.join(cwd, '.env'));
|
||||
|
||||
// check for development env value
|
||||
const devFileHasDevEnv = rawDevEnv.toString().includes('SPECIAL_FLAG');
|
||||
expect(devFileHasDevEnv).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should handle alternate filename', async () => {
|
||||
const cwd = setupFixture('vercel-env-pull');
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'vercel-env-pull',
|
||||
name: 'vercel-env-pull',
|
||||
});
|
||||
client.setArgv('env', 'pull', 'other.env', '--yes', '--cwd', cwd);
|
||||
const exitCode = await env(client);
|
||||
expect(exitCode, client.outputBuffer).toEqual(0);
|
||||
|
||||
const rawDevEnv = await fs.readFile(path.join(cwd, 'other.env'));
|
||||
|
||||
// check for development env value
|
||||
const devFileHasDevEnv = rawDevEnv.toString().includes('SPECIAL_FLAG');
|
||||
expect(devFileHasDevEnv).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should expose production system env variables', async () => {
|
||||
const cwd = setupFixture('vercel-env-pull');
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'vercel-env-pull',
|
||||
name: 'vercel-env-pull',
|
||||
autoExposeSystemEnvs: true,
|
||||
});
|
||||
|
||||
client.setArgv('env', 'pull', 'other.env', '--yes', '--cwd', cwd);
|
||||
const exitCode = await env(client);
|
||||
expect(exitCode, client.outputBuffer).toEqual(0);
|
||||
|
||||
const rawDevEnv = await fs.readFile(path.join(cwd, 'other.env'));
|
||||
|
||||
const productionFileHasVercelEnv = rawDevEnv
|
||||
.toString()
|
||||
.includes('VERCEL_ENV="development"');
|
||||
expect(productionFileHasVercelEnv).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -11,7 +11,7 @@ describe('pull', () => {
|
||||
it('should handle pulling', async () => {
|
||||
const cwd = setupFixture('vercel-pull-next');
|
||||
useUser();
|
||||
useTeams();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'vercel-pull-next',
|
||||
@@ -19,7 +19,7 @@ describe('pull', () => {
|
||||
});
|
||||
client.setArgv('pull', '--yes', cwd);
|
||||
const exitCode = await pull(client);
|
||||
expect(exitCode).toEqual(0);
|
||||
expect(exitCode, client.outputBuffer).toEqual(0);
|
||||
|
||||
const rawDevEnv = await fs.readFile(
|
||||
path.join(cwd, '.vercel', '.env.development.local')
|
||||
@@ -31,7 +31,7 @@ describe('pull', () => {
|
||||
it('should handle --environment=preview flag', async () => {
|
||||
const cwd = setupFixture('vercel-pull-next');
|
||||
useUser();
|
||||
useTeams();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'vercel-pull-next',
|
||||
@@ -53,7 +53,7 @@ describe('pull', () => {
|
||||
it('should handle --environment=production flag', async () => {
|
||||
const cwd = setupFixture('vercel-pull-next');
|
||||
useUser();
|
||||
useTeams();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'vercel-pull-next',
|
||||
|
||||
2
packages/cli/test/fixtures/unit/vercel-env-pull/.gitignore
vendored
Normal file
2
packages/cli/test/fixtures/unit/vercel-env-pull/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.next
|
||||
yarn.lock
|
||||
4
packages/cli/test/fixtures/unit/vercel-env-pull/.vercel/project.json
vendored
Normal file
4
packages/cli/test/fixtures/unit/vercel-env-pull/.vercel/project.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"orgId": "team_dummy",
|
||||
"projectId": "vercel-env-pull"
|
||||
}
|
||||
12
packages/cli/test/fixtures/unit/vercel-env-pull/package.json
vendored
Normal file
12
packages/cli/test/fixtures/unit/vercel-env-pull/package.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "next build",
|
||||
"dev": "next",
|
||||
"now-build": "next build"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "^8.0.0",
|
||||
"react": "^16.7.0",
|
||||
"react-dom": "^16.7.0"
|
||||
}
|
||||
}
|
||||
11
packages/cli/test/fixtures/unit/vercel-env-pull/pages/index.js
vendored
Normal file
11
packages/cli/test/fixtures/unit/vercel-env-pull/pages/index.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { withRouter } from 'next/router';
|
||||
|
||||
function Index({ router }) {
|
||||
const data = {
|
||||
pathname: router.pathname,
|
||||
query: router.query,
|
||||
};
|
||||
return <div>{JSON.stringify(data)}</div>;
|
||||
}
|
||||
|
||||
export default withRouter(Index);
|
||||
10
packages/cli/test/fixtures/unit/vercel-env-pull/vercel.json
vendored
Normal file
10
packages/cli/test/fixtures/unit/vercel-env-pull/vercel.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "vercel-env-pull",
|
||||
"routes": [
|
||||
{
|
||||
"src": "/(.*)",
|
||||
"dest": "/index?route-param=b"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,3 +1,2 @@
|
||||
.next
|
||||
yarn.lock
|
||||
.vercel
|
||||
4
packages/cli/test/fixtures/unit/vercel-pull-next/.vercel/project.json
vendored
Normal file
4
packages/cli/test/fixtures/unit/vercel-pull-next/.vercel/project.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"orgId": "team_dummy",
|
||||
"projectId": "vercel-pull-next"
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "vercel-pull-next",
|
||||
"builds": [{ "src": "package.json", "use": "@vercel/next@canary" }],
|
||||
"routes": [
|
||||
{
|
||||
"src": "/(.*)",
|
||||
|
||||
@@ -413,6 +413,10 @@ module.exports = async function prepare(session, binaryPath) {
|
||||
projectId: 'QmRoBYhejkkmssotLZr8tWgewPdPcjYucYUNERFbhJrRNi',
|
||||
}),
|
||||
},
|
||||
'project-settings-overrides': {
|
||||
'vercel.json': '{}',
|
||||
'package.json': '{}',
|
||||
},
|
||||
};
|
||||
|
||||
for (const [typeName, needed] of Object.entries(spec)) {
|
||||
|
||||
153
packages/cli/test/integration.js
vendored
153
packages/cli/test/integration.js
vendored
@@ -18,6 +18,7 @@ import fs, {
|
||||
copy,
|
||||
ensureDir,
|
||||
exists,
|
||||
mkdir,
|
||||
} from 'fs-extra';
|
||||
import logo from '../src/util/output/logo';
|
||||
import sleep from '../src/util/sleep';
|
||||
@@ -796,13 +797,16 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
t.regex(stderr, /Created .env file/gm);
|
||||
|
||||
const contents = fs.readFileSync(path.join(target, '.env'), 'utf8');
|
||||
t.true(contents.startsWith('# Created by Vercel CLI\n'));
|
||||
t.regex(contents, /^# Created by Vercel CLI\n/);
|
||||
|
||||
const lines = new Set(contents.split('\n'));
|
||||
t.true(lines.has('MY_NEW_ENV_VAR="my plaintext value"'));
|
||||
t.true(lines.has('MY_STDIN_VAR="{"expect":"quotes"}"'));
|
||||
t.true(lines.has('MY_DECRYPTABLE_SECRET_ENV="decryptable value"'));
|
||||
t.false(lines.has('MY_PREVIEW'));
|
||||
t.true(lines.has('MY_NEW_ENV_VAR="my plaintext value"'), 'MY_NEW_ENV_VAR');
|
||||
t.true(lines.has('MY_STDIN_VAR="{"expect":"quotes"}"'), 'MY_STDIN_VAR');
|
||||
t.true(
|
||||
lines.has('MY_DECRYPTABLE_SECRET_ENV="decryptable value"'),
|
||||
'MY_DECRYPTABLE_SECRET_ENV'
|
||||
);
|
||||
t.false(lines.has('MY_PREVIEW'), 'MY_PREVIEW');
|
||||
}
|
||||
|
||||
async function vcEnvPullOverwrite() {
|
||||
@@ -974,11 +978,12 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
const contents = fs.readFileSync(path.join(target, '.env'), 'utf8');
|
||||
|
||||
const lines = new Set(contents.split('\n'));
|
||||
t.true(lines.has('VERCEL="1"'));
|
||||
t.true(lines.has('VERCEL_URL=""'));
|
||||
t.true(lines.has('VERCEL_ENV="development"'));
|
||||
t.true(lines.has('VERCEL_GIT_PROVIDER=""'));
|
||||
t.true(lines.has('VERCEL_GIT_REPO_SLUG=""'));
|
||||
|
||||
t.true(lines.has('VERCEL="1"'), 'VERCEL');
|
||||
t.true(lines.has('VERCEL_URL=""'), 'VERCEL_URL');
|
||||
t.true(lines.has('VERCEL_ENV="development"'), 'VERCEL_ENV');
|
||||
t.true(lines.has('VERCEL_GIT_PROVIDER=""'), 'VERCEL_GIT_PROVIDER');
|
||||
t.true(lines.has('VERCEL_GIT_REPO_SLUG=""'), 'VERCEL_GIT_REPO_SLUG');
|
||||
}
|
||||
|
||||
async function vcDevAndFetchSystemVars() {
|
||||
@@ -2274,6 +2279,8 @@ test('try to revert a deployment and assign the automatic aliases', async t => {
|
||||
const { name } = JSON.parse(
|
||||
fs.readFileSync(path.join(firstDeployment, 'now.json'))
|
||||
);
|
||||
t.true(!!name, 'name has a value');
|
||||
|
||||
const url = `https://${name}.user.vercel.app`;
|
||||
|
||||
{
|
||||
@@ -2862,7 +2869,7 @@ test('should show prompts to set up project during first deploy', async t => {
|
||||
);
|
||||
|
||||
// Send a test request to the deployment
|
||||
const response = await fetch(new URL(output.stdout).href);
|
||||
const response = await fetch(new URL(output.stdout));
|
||||
const text = await response.text();
|
||||
t.is(text.includes('<h1>custom hello</h1>'), true, text);
|
||||
|
||||
@@ -3675,3 +3682,127 @@ test('[vc link] should support the `--project` flag', async t => {
|
||||
formatOutput(output)
|
||||
);
|
||||
});
|
||||
|
||||
test('vercel.json projectSettings overrides', async t => {
|
||||
// create project directory and get path to vercel.json
|
||||
const projectName = 'project-settings-overrides';
|
||||
const directory = fixture(projectName);
|
||||
const vercelJsonPath = path.join(directory, 'vercel.json');
|
||||
|
||||
async function deploy(expectedStatusCode = 0) {
|
||||
// deploy and assert deployment is successful
|
||||
const deployment = await execa(
|
||||
binaryPath,
|
||||
[directory, ...defaultArgs, '--public', '--confirm'],
|
||||
{ reject: false }
|
||||
);
|
||||
t.is(
|
||||
deployment.exitCode,
|
||||
expectedStatusCode,
|
||||
formatOutput({
|
||||
stderr: deployment.stderr,
|
||||
stdout: deployment.stdout,
|
||||
})
|
||||
);
|
||||
return deployment;
|
||||
}
|
||||
|
||||
// Test that deployment fails with override settings on new deployments
|
||||
await writeFile(
|
||||
vercelJsonPath,
|
||||
JSON.stringify({
|
||||
projectSettings: {
|
||||
outputDirectory: 'output',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
let deployment = await deploy(1);
|
||||
|
||||
t.true(
|
||||
deployment.stderr.includes(
|
||||
'Error! Unexpected property detected in vercel.json: "projectSettings"'
|
||||
)
|
||||
);
|
||||
|
||||
// Make sure vercel.json is empty for first deployment
|
||||
await writeFile(vercelJsonPath, JSON.stringify({}));
|
||||
|
||||
deployment = await deploy();
|
||||
|
||||
// create and write override project settings
|
||||
const BUILD_COMMAND =
|
||||
'mkdir output && echo "Hello, World 1" >> output/index.txt';
|
||||
const OUTPUT_DIRECTORY = 'output';
|
||||
|
||||
await writeFile(
|
||||
vercelJsonPath,
|
||||
JSON.stringify({
|
||||
projectSettings: {
|
||||
buildCommand: BUILD_COMMAND,
|
||||
outputDirectory: OUTPUT_DIRECTORY,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
deployment = await deploy();
|
||||
|
||||
// assert the command were executed
|
||||
let page = await fetch(deployment.stdout);
|
||||
let text = await page.text();
|
||||
t.is(text, `Hello, World 1\n`);
|
||||
|
||||
// Failure Case
|
||||
await writeFile(
|
||||
vercelJsonPath,
|
||||
JSON.stringify({
|
||||
projectSettings: {
|
||||
invalidProjectSetting: 'invalid project setting',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
// Deployment should fail
|
||||
deployment = await deploy(1);
|
||||
t.true(
|
||||
deployment.stderr.includes(
|
||||
'Error! Invalid request: `projectSettings` should NOT have additional property `invalidProjectSetting`.'
|
||||
)
|
||||
);
|
||||
|
||||
// Test Next.js Framework deployment
|
||||
await mkdir(`${directory}/pages`);
|
||||
await writeFile(
|
||||
`${directory}/pages/index.js`,
|
||||
`export default () => 'Hello, World 2'`
|
||||
);
|
||||
await writeFile(
|
||||
vercelJsonPath,
|
||||
JSON.stringify({
|
||||
projectSettings: {
|
||||
framework: 'nextjs',
|
||||
},
|
||||
})
|
||||
);
|
||||
await writeFile(
|
||||
`${directory}/package.json`,
|
||||
JSON.stringify({
|
||||
scripts: {
|
||||
dev: 'next',
|
||||
start: 'next start',
|
||||
build: 'next build',
|
||||
},
|
||||
dependencies: {
|
||||
next: 'latest',
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
deployment = await deploy();
|
||||
|
||||
page = await fetch(deployment.stdout);
|
||||
text = await page.text();
|
||||
t.true(text.includes(`Hello, World 2`));
|
||||
});
|
||||
|
||||
@@ -87,7 +87,20 @@ export class MockClient extends Client {
|
||||
}
|
||||
|
||||
stopMockServer() {
|
||||
this.mockServer?.close();
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (!this.mockServer?.close) {
|
||||
reject(new Error(`mockServer did not exist when closing`));
|
||||
return;
|
||||
}
|
||||
|
||||
this.mockServer.close(error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setArgv(...argv: string[]) {
|
||||
@@ -109,6 +122,6 @@ beforeEach(() => {
|
||||
client.reset();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
client.stopMockServer();
|
||||
afterAll(async () => {
|
||||
await client.stopMockServer();
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { client } from './client';
|
||||
import { Project } from '../../src/types';
|
||||
|
||||
const envs = [
|
||||
{
|
||||
@@ -36,6 +37,42 @@ const envs = [
|
||||
},
|
||||
];
|
||||
|
||||
const systemEnvs = [
|
||||
{
|
||||
type: 'encrypted',
|
||||
id: 'a235l6frtu25df32',
|
||||
key: 'SYSTEM_ENV_FOR_DEV',
|
||||
value: 'development',
|
||||
target: ['development'],
|
||||
gitBranch: null,
|
||||
configurationId: null,
|
||||
updatedAt: 1557241361445,
|
||||
createdAt: 1557241361445,
|
||||
},
|
||||
{
|
||||
type: 'encrypted',
|
||||
id: 'a235l6frtu25df32',
|
||||
key: 'SYSTEM_ENV_FOR_PREV',
|
||||
value: 'preview',
|
||||
target: ['preview'],
|
||||
gitBranch: null,
|
||||
configurationId: null,
|
||||
updatedAt: 1557241361445,
|
||||
createdAt: 1557241361445,
|
||||
},
|
||||
{
|
||||
type: 'encrypted',
|
||||
id: 'a235l6frtu25df32',
|
||||
key: 'SYSTEM_ENV_FOR_PROD',
|
||||
value: 'production',
|
||||
target: ['production'],
|
||||
gitBranch: null,
|
||||
configurationId: null,
|
||||
updatedAt: 1557241361445,
|
||||
createdAt: 1557241361445,
|
||||
},
|
||||
];
|
||||
|
||||
export const defaultProject = {
|
||||
id: 'foo',
|
||||
name: 'cli',
|
||||
@@ -80,20 +117,36 @@ export const defaultProject = {
|
||||
requestedAt: 1571239348998,
|
||||
target: 'production',
|
||||
teamId: null,
|
||||
type: 'LAMBDAS',
|
||||
type: undefined,
|
||||
url: 'a-project-name-rjtr4pz3f.vercel.app',
|
||||
userId: 'K4amb7K9dAt5R2vBJWF32bmY',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export function useProject(project = defaultProject) {
|
||||
export function useProject(project: Partial<Project> = defaultProject) {
|
||||
client.scenario.get(`/v8/projects/${project.name}`, (_req, res) => {
|
||||
res.json(project);
|
||||
});
|
||||
client.scenario.get(`/v8/projects/${project.id}`, (_req, res) => {
|
||||
res.json(project);
|
||||
});
|
||||
client.scenario.get(
|
||||
`/v6/projects/${project.id}/system-env-values`,
|
||||
(_req, res) => {
|
||||
const target = _req.query.target || 'development';
|
||||
if (typeof target !== 'string') {
|
||||
throw new Error(
|
||||
`/v6/projects/${project.id}/system-env-values was given a query param of "target=${target}", which is not a valid environment.`
|
||||
);
|
||||
}
|
||||
const targetEnvs = systemEnvs.filter(env => env.target.includes(target));
|
||||
|
||||
res.json({
|
||||
systemEnvValues: targetEnvs,
|
||||
});
|
||||
}
|
||||
);
|
||||
client.scenario.get(`/v8/projects/${project.id}/env`, (_req, res) => {
|
||||
const target = _req.query.target;
|
||||
if (typeof target === 'string') {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import chance from 'chance';
|
||||
import { client } from './client';
|
||||
|
||||
export function useTeams() {
|
||||
export function useTeams(teamId?: string) {
|
||||
const id = teamId || chance().guid();
|
||||
const teams = [
|
||||
{
|
||||
id: chance().guid(),
|
||||
id,
|
||||
slug: chance().string({ length: 5, casing: 'lower' }),
|
||||
name: chance().company(),
|
||||
creatorId: chance().guid(),
|
||||
@@ -14,7 +15,7 @@ export function useTeams() {
|
||||
];
|
||||
|
||||
for (let team of teams) {
|
||||
client.scenario.get(`/v1/team/${team.id}`, (_req, res) => {
|
||||
client.scenario.get(`/teams/${team.id}`, (_req, res) => {
|
||||
res.json(team);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "10.4.1",
|
||||
"version": "10.4.2-canary.2",
|
||||
"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.2",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "3.0.0",
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { lstatSync } from 'fs-extra';
|
||||
|
||||
import { relative, isAbsolute } from 'path';
|
||||
import { isAbsolute } from 'path';
|
||||
import { hashes, mapToObject } from './utils/hashes';
|
||||
import { upload } from './upload';
|
||||
import { buildFileTree, createDebug, parseVercelConfig } from './utils';
|
||||
import { buildFileTree, createDebug } from './utils';
|
||||
import { DeploymentError } from './errors';
|
||||
import {
|
||||
VercelConfig,
|
||||
VercelClientOptions,
|
||||
DeploymentOptions,
|
||||
DeploymentEventType,
|
||||
@@ -15,13 +13,11 @@ import {
|
||||
export default function buildCreateDeployment() {
|
||||
return async function* createDeployment(
|
||||
clientOptions: VercelClientOptions,
|
||||
deploymentOptions: DeploymentOptions = {},
|
||||
nowConfig: VercelConfig = {}
|
||||
deploymentOptions: DeploymentOptions = {}
|
||||
): AsyncIterableIterator<{ type: DeploymentEventType; payload: any }> {
|
||||
const { path } = clientOptions;
|
||||
|
||||
const debug = createDebug(clientOptions.debug);
|
||||
const cwd = process.cwd();
|
||||
|
||||
debug('Creating deployment...');
|
||||
|
||||
@@ -76,29 +72,6 @@ export default function buildCreateDeployment() {
|
||||
|
||||
const { fileList } = await buildFileTree(path, clientOptions, debug);
|
||||
|
||||
let configPath: string | undefined;
|
||||
if (!nowConfig) {
|
||||
// If the user did not provide a config file, use the one in the root directory.
|
||||
const relativePaths = fileList.map(f => relative(cwd, f));
|
||||
const hasVercelConfig = relativePaths.includes('vercel.json');
|
||||
const hasNowConfig = relativePaths.includes('now.json');
|
||||
|
||||
if (hasVercelConfig) {
|
||||
if (hasNowConfig) {
|
||||
throw new DeploymentError({
|
||||
code: 'conflicting_config',
|
||||
message:
|
||||
'Cannot use both a `vercel.json` and `now.json` file. Please delete the `now.json` file.',
|
||||
});
|
||||
}
|
||||
configPath = 'vercel.json';
|
||||
} else if (hasNowConfig) {
|
||||
configPath = 'now.json';
|
||||
}
|
||||
|
||||
nowConfig = await parseVercelConfig(configPath);
|
||||
}
|
||||
|
||||
// This is a useful warning because it prevents people
|
||||
// from getting confused about a deployment that renders 404.
|
||||
if (fileList.length === 0) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { join } from 'path';
|
||||
import stringArgv from 'string-argv';
|
||||
import { debug } from '@vercel/build-utils';
|
||||
const versionMap = new Map([
|
||||
['1.18', '1.18.1'],
|
||||
['1.17', '1.17.3'],
|
||||
['1.16', '1.16.10'],
|
||||
['1.15', '1.15.8'],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/go",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.3-canary.2",
|
||||
"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.2",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"async-retry": "1.3.1",
|
||||
"execa": "^1.0.0",
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
{ "src": "subdirectory/index.go", "use": "@vercel/go" }
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "cow:go1.17.3:RANDOMNESS_PLACEHOLDER" },
|
||||
{ "path": "/", "mustContain": "cow:go1.18.1:RANDOMNESS_PLACEHOLDER" },
|
||||
{
|
||||
"path": "/subdirectory",
|
||||
"mustContain": "subcow:go1.17.3:RANDOMNESS_PLACEHOLDER"
|
||||
"mustContain": "subcow:go1.18.1:RANDOMNESS_PLACEHOLDER"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -186,9 +186,6 @@ class Bridge {
|
||||
'payloads' in normalizedEvent &&
|
||||
Array.isArray(normalizedEvent.payloads)
|
||||
) {
|
||||
// statusCode and headers are required to match when using
|
||||
// multiple payloads in a single invocation so we can use
|
||||
// the first
|
||||
let statusCode = 200;
|
||||
/**
|
||||
* @type {import('http').IncomingHttpHeaders}
|
||||
@@ -200,6 +197,14 @@ class Bridge {
|
||||
let combinedBody = '';
|
||||
const multipartBoundary = 'payload-separator';
|
||||
const CLRF = '\r\n';
|
||||
/**
|
||||
* @type {Record<string, any>[]}
|
||||
*/
|
||||
const separateHeaders = [];
|
||||
/**
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
const allHeaderKeys = new Set();
|
||||
|
||||
// we execute the payloads one at a time to ensure
|
||||
// lambda semantics
|
||||
@@ -209,20 +214,49 @@ class Bridge {
|
||||
// build a combined body using multipart
|
||||
// https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
|
||||
combinedBody += `--${multipartBoundary}${CLRF}`;
|
||||
if (response.headers['content-type']) {
|
||||
combinedBody += `content-type: ${response.headers['content-type']}${CLRF}${CLRF}`;
|
||||
}
|
||||
combinedBody += response.body;
|
||||
combinedBody += `content-type: ${
|
||||
response.headers['content-type'] || 'text/plain'
|
||||
}${CLRF}${CLRF}`;
|
||||
combinedBody += response.body || '';
|
||||
combinedBody += CLRF;
|
||||
|
||||
if (i === normalizedEvent.payloads.length - 1) {
|
||||
combinedBody += `--${multipartBoundary}--${CLRF}`;
|
||||
}
|
||||
|
||||
statusCode = response.statusCode;
|
||||
headers = response.headers;
|
||||
// pass non-200 status code in header so it can be handled
|
||||
// separately from other payloads e.g. HTML payload redirects
|
||||
// (307) but data payload does not (200)
|
||||
if (response.statusCode !== 200) {
|
||||
headers[`x-vercel-payload-${i + 1}-status`] =
|
||||
response.statusCode + '';
|
||||
}
|
||||
separateHeaders.push(response.headers);
|
||||
Object.keys(response.headers).forEach(key => allHeaderKeys.add(key));
|
||||
}
|
||||
|
||||
allHeaderKeys.forEach(curKey => {
|
||||
/**
|
||||
* @type string | string[] | undefined
|
||||
*/
|
||||
const curValue = separateHeaders[0] && separateHeaders[0][curKey];
|
||||
const canDedupe = separateHeaders.every(
|
||||
headers => headers[curKey] === curValue
|
||||
);
|
||||
|
||||
if (canDedupe) {
|
||||
headers[curKey] = curValue;
|
||||
} else {
|
||||
// if a header is unique per payload ensure it is prefixed
|
||||
// so it can be parsed and provided separately
|
||||
separateHeaders.forEach((curHeaders, idx) => {
|
||||
if (curHeaders[curKey]) {
|
||||
headers[`x-vercel-payload-${idx + 1}-${curKey}`] =
|
||||
curHeaders[curKey];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
headers[
|
||||
'content-type'
|
||||
] = `multipart/mixed; boundary="${multipartBoundary}"`;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/node-bridge",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.1-canary.0",
|
||||
"license": "MIT",
|
||||
"main": "./index.js",
|
||||
"repository": {
|
||||
|
||||
37
packages/node-bridge/test/bridge.test.js
vendored
37
packages/node-bridge/test/bridge.test.js
vendored
@@ -86,6 +86,12 @@ test('`NowProxyEvent` normalizing', async () => {
|
||||
|
||||
test('multi-payload handling', async () => {
|
||||
const server = new Server((req, res) => {
|
||||
if (req.url === '/redirect') {
|
||||
res.setHeader('Location', '/somewhere');
|
||||
res.statusCode = 307;
|
||||
res.end('/somewhere');
|
||||
return;
|
||||
}
|
||||
res.setHeader(
|
||||
'content-type',
|
||||
req.url.includes('_next/data') ? 'application/json' : 'text/html'
|
||||
@@ -117,6 +123,11 @@ test('multi-payload handling', async () => {
|
||||
headers: { foo: 'baz' },
|
||||
path: '/_next/data/build-id/nowproxy.json',
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
headers: { foo: 'baz' },
|
||||
path: '/redirect',
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
@@ -137,20 +148,36 @@ test('multi-payload handling', async () => {
|
||||
!item.startsWith('content-type:') &&
|
||||
!item.startsWith('--payload')
|
||||
) {
|
||||
bodies.push(
|
||||
JSON.parse(
|
||||
Buffer.from(item.split('--payload-separator')[0], 'base64').toString()
|
||||
)
|
||||
);
|
||||
const content = Buffer.from(
|
||||
item.split('--payload-separator')[0],
|
||||
'base64'
|
||||
).toString();
|
||||
bodies.push(content.startsWith('{') ? JSON.parse(content) : content);
|
||||
}
|
||||
});
|
||||
|
||||
// ensure content-type is always specified as is required for
|
||||
// proper parsing of the multipart body
|
||||
assert(payloadParts.some(part => part.includes('content-type: text/plain')));
|
||||
|
||||
assert.equal(bodies[0].method, 'GET');
|
||||
assert.equal(bodies[0].path, '/nowproxy');
|
||||
assert.equal(bodies[0].headers.foo, 'baz');
|
||||
assert.equal(bodies[1].method, 'GET');
|
||||
assert.equal(bodies[1].path, '/_next/data/build-id/nowproxy.json');
|
||||
assert.equal(bodies[1].headers.foo, 'baz');
|
||||
assert.equal(bodies[2], '/somewhere');
|
||||
assert.equal(result.headers['x-vercel-payload-3-status'], '307');
|
||||
assert.equal(result.headers['x-vercel-payload-2-status'], undefined);
|
||||
assert.equal(result.headers['x-vercel-payload-1-status'], undefined);
|
||||
assert.equal(result.headers['x-vercel-payload-1-content-type'], 'text/html');
|
||||
assert.equal(
|
||||
result.headers['x-vercel-payload-2-content-type'],
|
||||
'application/json'
|
||||
);
|
||||
assert.equal(result.headers['x-vercel-payload-3-content-type'], undefined);
|
||||
assert.equal(result.headers['x-vercel-payload-3-location'], '/somewhere');
|
||||
assert.equal(result.headers['x-vercel-payload-2-location'], undefined);
|
||||
assert.equal(context.callbackWaitsForEmptyEventLoop, false);
|
||||
|
||||
server.close();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/node",
|
||||
"version": "1.14.1",
|
||||
"version": "1.14.2-canary.3",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
||||
@@ -19,7 +19,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"@vercel/node-bridge": "2.2.0",
|
||||
"@vercel/node-bridge": "2.2.1-canary.0",
|
||||
"ts-node": "8.9.1",
|
||||
"typescript": "4.3.4"
|
||||
},
|
||||
@@ -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.2",
|
||||
"@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.2",
|
||||
"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.2",
|
||||
"@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.2",
|
||||
"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.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.2",
|
||||
"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.2",
|
||||
"@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.2",
|
||||
"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.2",
|
||||
"@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));
|
||||
|
||||
@@ -212,7 +210,7 @@ function getPkg(entrypoint: string, workPath: string) {
|
||||
|
||||
try {
|
||||
const pkgPath = path.join(workPath, entrypoint);
|
||||
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as PackageJson;
|
||||
const pkg: PackageJson = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
||||
return pkg;
|
||||
} catch (err: any) {
|
||||
if (err.code !== 'ENOENT') throw err;
|
||||
@@ -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`, [], {
|
||||
@@ -278,17 +276,12 @@ export const build: BuildV2 = async ({
|
||||
let distPath = path.join(
|
||||
workPath,
|
||||
path.dirname(entrypoint),
|
||||
(config && (config.distDir as string)) ||
|
||||
(config.outputDirectory as string) ||
|
||||
'dist'
|
||||
(config.distDir as string) || config.outputDirectory || 'dist'
|
||||
);
|
||||
|
||||
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);
|
||||
@@ -331,7 +324,7 @@ export const build: BuildV2 = async ({
|
||||
distPath = path.join(
|
||||
workPath,
|
||||
path.dirname(entrypoint),
|
||||
(config.outputDirectory as string) || 'public'
|
||||
config.outputDirectory || 'public'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -387,12 +380,12 @@ 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
|
||||
*/
|
||||
if (framework && framework.slug === 'create-react-app') {
|
||||
if (framework?.slug === 'create-react-app') {
|
||||
if (!spawnOpts.env) {
|
||||
spawnOpts.env = {};
|
||||
}
|
||||
@@ -412,10 +405,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 +477,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 +623,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 +666,7 @@ export const build: BuildV2 = async ({
|
||||
}
|
||||
}
|
||||
|
||||
const extraOutputs = await readBuildOutputDirectory({
|
||||
const extraOutputs = await BuildOutputV1.readBuildOutputDirectory({
|
||||
workPath,
|
||||
nodeVersion,
|
||||
});
|
||||
@@ -750,7 +760,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"
|
||||
},
|
||||
|
||||
9
turbo.json
Normal file
9
turbo.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"baseBranch": "origin/main",
|
||||
"pipeline": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["dist/**"]
|
||||
}
|
||||
}
|
||||
}
|
||||
203
yarn.lock
203
yarn.lock
@@ -2,6 +2,11 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@alex_neo/jest-expect-message@1.0.5":
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@alex_neo/jest-expect-message/-/jest-expect-message-1.0.5.tgz#bdc96f5764cb42ab677941c1ac86b2e4bea96fa7"
|
||||
integrity sha512-1eBykZCd0pPGl5qKtV6Z5ARA6yuhXzHsVN2h5GH5/H6svYa37Jr7vMio5OFpiw1LBHtscrZs7amSkZkcwm0cvQ==
|
||||
|
||||
"@ava/babel-plugin-throws-helper@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@ava/babel-plugin-throws-helper/-/babel-plugin-throws-helper-3.0.0.tgz#2c933ec22da0c4ce1fc5369f2b95452c70420586"
|
||||
@@ -2164,6 +2169,21 @@
|
||||
dependencies:
|
||||
"@types/istanbul-lib-report" "*"
|
||||
|
||||
"@types/jest-expect-message@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest-expect-message/-/jest-expect-message-1.0.3.tgz#433ad89565c871acefafedeb957d0a8ec2a977a0"
|
||||
integrity sha512-sp70Lc8POkOcXHEcLERpX/7B/BtQiqIYz3AvC9ZMNKSaiDttr8hKvz9DljIn7N6WJi3ioVoTtB1utDAX46oPlg==
|
||||
dependencies:
|
||||
"@types/jest" "*"
|
||||
|
||||
"@types/jest@*":
|
||||
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/jest@27.0.1":
|
||||
version "27.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.0.1.tgz#fafcc997da0135865311bb1215ba16dba6bdf4ca"
|
||||
@@ -2180,6 +2200,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 +2588,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 +4705,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 +7193,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 +7256,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 +7313,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 +9598,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"
|
||||
@@ -11419,83 +11486,83 @@ tunnel-agent@^0.6.0:
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
turbo-darwin-64@1.1.9:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-1.1.9.tgz#5c642fec610d26d7acb7bbe51020804e1ff9bc73"
|
||||
integrity sha512-DAeQkBXvIVMwdlT/OvJYKNbhijUJQr4ug/sLJpNMEMPVW2Fs1aVZpr+ea4W+Xo90nOk+Ac0oCbv4nx3v8UZ1iQ==
|
||||
turbo-darwin-64@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-1.2.2.tgz#001151b708207141fe17371004b0871ca048dedb"
|
||||
integrity sha512-2hidknFGrm2OzTcE0FBcvPIb2oewrnq4WbOOuVj+9e19rBVIs5BdN4LLvxihIxQelQdZXiPvSTVGLvvg8+mSpg==
|
||||
|
||||
turbo-darwin-arm64@1.1.9:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-1.1.9.tgz#c3a7ad58d39af375fab6b944558da29cd2ac0e01"
|
||||
integrity sha512-15WTOOngcUALX1Pso6tG/vot8ksi1l5/Sljv4sXjfpFup9Xkc2m6AE6QlKllextL10SD4x52K2z01ClV7YIKEQ==
|
||||
turbo-darwin-arm64@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-1.2.2.tgz#b392a013338e946b6973240fe7e6e66751bd86fa"
|
||||
integrity sha512-R+7TAOdTSTDHZJoFVtL24GKUuWD4v+0qUcq8KzkLyAd7AeaPssTpwN+Tc0Nj/46RStf1PryBflu0J8ovi7SWbQ==
|
||||
|
||||
turbo-freebsd-64@1.1.9:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/turbo-freebsd-64/-/turbo-freebsd-64-1.1.9.tgz#88bf9eff412f6277e75cec6aadbb554c78a469f0"
|
||||
integrity sha512-DqDt4o79guYef7MlYQChHMOMxJNbvE+SVuBP0+CxG4WreSUnsT+D4+/+KBs9e/o2p1xHRoDOtuKbDai8tWgKaQ==
|
||||
turbo-freebsd-64@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-freebsd-64/-/turbo-freebsd-64-1.2.2.tgz#ef2e1da981f6b5f0485cc6c07fbd5385a621cf47"
|
||||
integrity sha512-EviJUMNfw9qah3Ko8Aow0qrglJ/qGadsv8LHfTuRL8SNix4cx3s2eEAkQ82kQey1eWjtfB6XSf4JTr5EzHko8w==
|
||||
|
||||
turbo-freebsd-arm64@1.1.9:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/turbo-freebsd-arm64/-/turbo-freebsd-arm64-1.1.9.tgz#a2bf29386b0bcf5ee43ce5a1cbf39e3d0550d7a0"
|
||||
integrity sha512-tiudLNRsOmmWD9guqWEoW63x3F0YKtkQis0DRrFqJBVcnMDfKYtMWIr6kP7DhK9PxKwnJY4Y1WIawXPIWl66EQ==
|
||||
turbo-freebsd-arm64@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-freebsd-arm64/-/turbo-freebsd-arm64-1.2.2.tgz#52f0d30a9ce43ef0200c848c32b52c1e53abb39e"
|
||||
integrity sha512-enz9o3qJq7SwD8pNU1EIcRIZaymNWih/SYQyrTnkD88kLcTS3LKsoenv8Jy2G0w4rCgoBsBLHLpepzRttGaxwQ==
|
||||
|
||||
turbo-linux-32@1.1.9:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/turbo-linux-32/-/turbo-linux-32-1.1.9.tgz#6fae00fffc171ebf990b28f9e1635da8800bc3ef"
|
||||
integrity sha512-u0VI41gqxRqGR41+nVmWvNZ6d2AS/KYfrfDeFbw8mRVsuAR2OP6emsbt7cUN++0ZzH9qsRgGcBR/k2Ge/EEx5g==
|
||||
turbo-linux-32@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-linux-32/-/turbo-linux-32-1.2.2.tgz#cabbd7991df856441cc15b34ae646446c435d480"
|
||||
integrity sha512-JyuJnFVT2u2wrvd3a5VpHigoKPJnGuNwGDnYYh6hJLpQLDk0mCRcdtViKInryPIQjbiq1oZYefYJfpPnU4MgtA==
|
||||
|
||||
turbo-linux-64@1.1.9:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-1.1.9.tgz#3d608908c2e5dfab18316a07427dd5f4369b8eab"
|
||||
integrity sha512-BU3ulyNivyrDwGVA2NnMcqUH9o4njk59DgantoF7rRSWNeyt5abneOG7CVAdaAy03JIc1gzErjVV+QSQ25H9ug==
|
||||
turbo-linux-64@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-1.2.2.tgz#11a1c9d765502ca84419ab073eec95d846a5205c"
|
||||
integrity sha512-UT7SiMcN+4fyVcM7Y/p11qdypNmCUkY6zqyEGpC6G3HLZHeNt3z+nq0FZDDPiMskEP0WUbLAv1sICUKvHC4wPA==
|
||||
|
||||
turbo-linux-arm64@1.1.9:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-1.1.9.tgz#7b7350c155c1ec3cea6be00840837ca020a8bcca"
|
||||
integrity sha512-bVD+8n8qDDlZxG6Z8rn58m50BjczIV4zCgjDQDIN2E6H9FjWuLOpKXk3mdTgM31w1Om8HYZgkbvla/jQSwWHrQ==
|
||||
turbo-linux-arm64@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-1.2.2.tgz#cc283cf69715366a6aeb4ab3e51df81af1eaa479"
|
||||
integrity sha512-RG/cXgrjLKowLYuYd5I7W0KS+RKoCpu6UI2YiIXShG8UGVHpGhKb1duJ2GC5gptYVWIJC3q1jqNgr5a93W+Rfw==
|
||||
|
||||
turbo-linux-arm@1.1.9:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/turbo-linux-arm/-/turbo-linux-arm-1.1.9.tgz#6f71462f3e3becf73cd1e2ded0936260b531075e"
|
||||
integrity sha512-sAI2BqkipyvH6fQhhnOGhY9C9z9AMUdTizOm0Luj6gmEMZhUmjfk5K8BsX3uKzY6cWx5+twaTPXFd1EGV/o7Pg==
|
||||
turbo-linux-arm@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-linux-arm/-/turbo-linux-arm-1.2.2.tgz#7b3ef11685f80185490702dfc1cbd0d0c3dad998"
|
||||
integrity sha512-HZPZG2eU1D+iHu7OvIIsMu2retLvERKgqAPb4mJ4R/xynXO0FUd07uUEJWU881ZH11mKPFWDwVfI4gjW95UuRg==
|
||||
|
||||
turbo-linux-mips64le@1.1.9:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/turbo-linux-mips64le/-/turbo-linux-mips64le-1.1.9.tgz#a3e08850bdf022720bef5b9d5d8a3111ba65851d"
|
||||
integrity sha512-/34pGwWKRes3FYAUpZn+s8rKIbi76ZHuGxvXWx7clIfmLwNw1dN04c7cXU/t2PwDv4CzsGkqPgVNMGitnLxeUQ==
|
||||
turbo-linux-mips64le@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-linux-mips64le/-/turbo-linux-mips64le-1.2.2.tgz#0c4081206c0ca559c0c46f4c6959c7144202ccd2"
|
||||
integrity sha512-r7M0e7AFBa8xBkifqsFfW3F+FLQanJkK4NiaSi2Y9IUgI1v4JEw6o6HKRGtUZ8wRRxGKY/4t8NyzzUH8a+PBXA==
|
||||
|
||||
turbo-linux-ppc64le@1.1.9:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/turbo-linux-ppc64le/-/turbo-linux-ppc64le-1.1.9.tgz#fd8740b87bbabcd363268040bdae6a9643d77b2a"
|
||||
integrity sha512-5yR59wLf+HTV2Zb7ycPHXloTgYql22F4lhL6H7cWBVmbDFzrWR3N1d/sPaNLHtaDEE4l6UtTOFd/WQCfPDq/Hw==
|
||||
turbo-linux-ppc64le@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-linux-ppc64le/-/turbo-linux-ppc64le-1.2.2.tgz#e3b94a009eb8189c26b72f146920d2c146966363"
|
||||
integrity sha512-NY96rIJxKHqQgUkvLYQCwSsXeUkkcUFmKyRqrBlsVP0BZO3CbNvLgt+EPKy1+/RLAI7wwdes9B57GoezLyZezQ==
|
||||
|
||||
turbo-windows-32@1.1.9:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/turbo-windows-32/-/turbo-windows-32-1.1.9.tgz#ddfd8c83cbd6693e89df6fe099ae3ba2b34c4b8f"
|
||||
integrity sha512-dVwjFWg/SsW+pVk1iOnfovXSzsMsIgiEQvkAEnN3+XNEkjtYhLQ73bntZhP/mO4IKpDEkPoBcOlV/092uwadOg==
|
||||
turbo-windows-32@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-windows-32/-/turbo-windows-32-1.2.2.tgz#0c889f0fb85a192e1a29922313e0b141da24f5da"
|
||||
integrity sha512-IQYdaKqP7BxSGyU3iFNO/wKuJ17NwHPExACJiYZmua1pxM4wkeNcUCmb1u5zuJ2QbPB/+m4iGnDgitAdxUmdLw==
|
||||
|
||||
turbo-windows-64@1.1.9:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-1.1.9.tgz#2ed9fbad8ab5384ce14bd0e87f4fa2fd2117f3e0"
|
||||
integrity sha512-fzjO2D9lzhrBNjnuqaFTBpRU9DjYMhhgFgvD3ynwNWiBgm4wcer58DpRMDEzjq51Q+0HwFIrsKzEMyVK4Ubu/Q==
|
||||
turbo-windows-64@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-1.2.2.tgz#446d96ce8c015d380dfa4d805296cc4e3ce6c00d"
|
||||
integrity sha512-o7jF8cCzfhTZXvGySudi/Tx72Dqv6mce8Qq58jXWXEpbMV8KJwYuX2BMhIZilNZWyCk0IUqofVPMmampga6nfQ==
|
||||
|
||||
turbo@1.1.9:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/turbo/-/turbo-1.1.9.tgz#cbff85b927558e4f7b731ee17b3739471dca0fae"
|
||||
integrity sha512-xFtKZ9EFEkENhRxn89RQWmgMKCnUvKWPwiOXz60ztp4HEKXAdOBgEmgCOhyn5osfgQYOiaOI6gBIjSf/UMorQQ==
|
||||
turbo@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo/-/turbo-1.2.2.tgz#27e9530cfa8457d2dae50c538073f6d8fcc2e78c"
|
||||
integrity sha512-gt+QuvZVAQVcrKhgSCDJObIdh12bpt4zWOPe0yiWD/zyRJjNzkiXxR6WKSuAEUl0uQIB4XrRn2OGTmrR7KROJw==
|
||||
optionalDependencies:
|
||||
turbo-darwin-64 "1.1.9"
|
||||
turbo-darwin-arm64 "1.1.9"
|
||||
turbo-freebsd-64 "1.1.9"
|
||||
turbo-freebsd-arm64 "1.1.9"
|
||||
turbo-linux-32 "1.1.9"
|
||||
turbo-linux-64 "1.1.9"
|
||||
turbo-linux-arm "1.1.9"
|
||||
turbo-linux-arm64 "1.1.9"
|
||||
turbo-linux-mips64le "1.1.9"
|
||||
turbo-linux-ppc64le "1.1.9"
|
||||
turbo-windows-32 "1.1.9"
|
||||
turbo-windows-64 "1.1.9"
|
||||
turbo-darwin-64 "1.2.2"
|
||||
turbo-darwin-arm64 "1.2.2"
|
||||
turbo-freebsd-64 "1.2.2"
|
||||
turbo-freebsd-arm64 "1.2.2"
|
||||
turbo-linux-32 "1.2.2"
|
||||
turbo-linux-64 "1.2.2"
|
||||
turbo-linux-arm "1.2.2"
|
||||
turbo-linux-arm64 "1.2.2"
|
||||
turbo-linux-mips64le "1.2.2"
|
||||
turbo-linux-ppc64le "1.2.2"
|
||||
turbo-windows-32 "1.2.2"
|
||||
turbo-windows-64 "1.2.2"
|
||||
|
||||
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
|
||||
version "0.14.5"
|
||||
|
||||
Reference in New Issue
Block a user