Compare commits

...

16 Commits

Author SHA1 Message Date
Ethan Arrowood
b61f049f11 Publish Canary
- @vercel/build-utils@2.15.2-canary.2
 - vercel@24.1.1-canary.3
 - @vercel/client@10.4.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/redwood@0.7.1-canary.2
 - @vercel/ruby@1.3.3-canary.2
 - @vercel/static-build@0.23.2-canary.2
2022-04-15 15:26:55 -06:00
Ethan Arrowood
16e28f326b [cli] Allow projectSettings override in vercel.json (#7619)
This PR adds the required changes to the CLI to support overridable `vercel.json` `projectSettings` values. This PR is linked with changes on the API side too.

- The core changes are in `packages/cli/src/commands/deploy/index.ts`
- An unused code path was removed from `packages/client/src/create-deployment.ts`, `packages/cli/src/util/deploy/process-deployment.ts` and `packages/cli/src/util/index.ts`.

This will only work for *existing* deployment (not *new* ones). This is implemented by checking the `status` of the deployment (is it linked or not). An error is thrown if the user attempts to send overrided settings to a new project. 

A warning is outputted when overrided settings are discovered and shares what settings are being overridden.

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-04-15 21:20:00 +00:00
Sean Massa
276397c940 [examples] fixed vercel deploy link (#7684) 2022-04-15 13:55:42 -05:00
Nathan Rajlich
85d7311199 [static-build] Misc TypeScript cleanup (#7682)
This was just sitting on my local checkout so might as well push it.
2022-04-15 09:42:32 -07:00
Sean Massa
b8114b8b39 [cli] set environment properly in vc pull (#7656)
This PR sets environment properly in `vc pull`. The system env vars were not respecting the target `--environment`.

Adds tests for related `vc env` logic.
2022-04-15 04:41:29 +00:00
QianyuPan
d63e8d3187 [go] Add support for version 1.18 (#7590)
Go 1.18 has been released, but I find that I can't deploy my project on Vercel when `go.mod` has `go 1.18` in it.

1.18 adds some very useful features. It would be very valuable for developers to have Vercel support 1.18.
2022-04-14 19:37:01 -04:00
JJ Kasper
11d3dd04aa Publish Canary
- vercel@24.1.1-canary.2
 - @vercel/node-bridge@2.2.1-canary.0
 - @vercel/node@1.14.2-canary.2
2022-04-14 17:04:05 -05:00
JJ Kasper
46bf95ee36 [node-bridge] Ensure content-type is always set with multi-payloads (#7681)
### Related Issues

This ensures a content-type is always set inside of each part of the multi-part payload as it's needed for proper parsing. This also ensures non-200 status codes/headers are passed back separate when they differ so that they can be handled independently without assuming they all match. 

x-ref: https://vercel.slack.com/archives/C03AYHB6MA9
x-ref: https://github.com/vercel/vercel/pull/7507

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-04-14 22:00:10 +00:00
Nathan Rajlich
92252468c2 [tests] Add turbo.json and bump to turbo 1.2.2 (#7620)
* Add `turbo.json`

Ran `npx @turbo/codemod create-turbo-config` like the warnings have been telling us to.

* [tests] Bump `turbo` and allow `turbo.json` in `.vercelignore` (#7623)

Co-authored-by: Jared Palmer <jared@jaredpalmer.com>
Co-authored-by: Steven <steven@ceriously.com>
2022-04-13 11:09:47 -04:00
Nathan Rajlich
71b83d5587 Publish Canary
- @vercel/build-utils@2.15.2-canary.1
 - vercel@24.1.1-canary.1
 - @vercel/client@10.4.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/redwood@0.7.1-canary.1
 - @vercel/ruby@1.3.3-canary.1
 - @vercel/static-build@0.23.2-canary.1
2022-04-12 15:57:36 -07:00
Nathan Rajlich
d9e5fdc5e4 [build-utils] Move "Installing dependencies..." log to runNpmInstall() (#7672)
Follow-up to #7671. Since `runNpmInstall()` might now be de-duped, only
print "Installing dependencies..." when the dependencies are actually
being installed. This avoids printing the log message unnecessarily when
the command won't actually be run, and also removes some duplication in
the Builders' code.
2022-04-12 16:23:01 -04:00
Nathan Rajlich
58f479c603 [static-build] Add support for Build Output v3 detection (#7669)
Adds detection logic for a framework / build script outputting Build Output v3
format to the filesystem. In this case, `static-build` will simply stop processing
after the Build Command since deserialization happens in the build-container side
of things (this is different compared to the v1 output which gets handled in this
Builder. The reason for that is because the v3 output matches what `vc build`
outputs vs. v1 which is a different format).
2022-04-12 09:09:50 -07:00
Nathan Rajlich
d62461d952 [build-utils] Only allow runNpmInstall() to run once per package.json (#7671)
Adds a best-effort optimization to only run `npm install` once per
`pacakge.json` file. This will save a lot of time in the single-sandbox
build world (i.e. `vc build`).
2022-04-12 09:09:10 -07:00
Steven
e7f524defb Publish Canary
- @vercel/build-utils@2.15.2-canary.0
 - vercel@24.1.1-canary.0
 - @vercel/client@10.4.2-canary.0
 - @vercel/go@1.3.3-canary.0
 - @vercel/node@1.14.2-canary.0
 - @vercel/python@2.2.3-canary.0
 - @vercel/redwood@0.7.1-canary.0
 - @vercel/ruby@1.3.3-canary.0
 - @vercel/static-build@0.23.2-canary.0
2022-04-12 08:58:38 -04:00
Steven
bdefd0d05d [docs] Fix links to docs (#7668) 2022-04-12 07:52:12 -04:00
Ethan Arrowood
ca522fc9f1 [node][redwood] update @vercel/nft to 0.18.1 (#7670)
### Related Issues

Updates the @vercel/nft dependency across the repo to v0.18.1.

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-04-11 22:07:45 +00:00
62 changed files with 945 additions and 305 deletions

View File

@@ -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/**

View File

@@ -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)

View File

@@ -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).

View File

@@ -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.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/vercel/tree/main/dojo&template=dojo)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/vercel/tree/main/examples/dojo&template=dojo)
### How We Created This Example

View File

@@ -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",

View File

@@ -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",

View File

@@ -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',
};
}

View File

@@ -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({

View File

@@ -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;
}

View File

@@ -0,0 +1,4 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1

View File

@@ -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);
});

View File

@@ -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": [

View File

@@ -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;

View File

@@ -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;

View File

@@ -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));

View File

@@ -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 =

View File

@@ -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 = {

View File

@@ -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);
}

View File

@@ -160,7 +160,6 @@ export default class Now extends EventEmitter {
uploadStamp,
deployStamp,
quiet,
nowConfig,
force: forceNew,
withCache,
org,

View File

@@ -227,6 +227,7 @@ export default async function setupAndLink(
return { status: 'linked', org, project };
} catch (err) {
handleError(err);
return { status: 'error', exitCode: 1 };
}
}

View 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();
});
});
});

View File

@@ -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',

View File

@@ -0,0 +1,2 @@
.next
yarn.lock

View File

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

View 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"
}
}

View 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);

View File

@@ -0,0 +1,10 @@
{
"version": 2,
"name": "vercel-env-pull",
"routes": [
{
"src": "/(.*)",
"dest": "/index?route-param=b"
}
]
}

View File

@@ -1,3 +1,2 @@
.next
yarn.lock
.vercel

View File

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

View File

@@ -1,7 +1,6 @@
{
"version": 2,
"name": "vercel-pull-next",
"builds": [{ "src": "package.json", "use": "@vercel/next@canary" }],
"routes": [
{
"src": "/(.*)",

View File

@@ -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)) {

View File

@@ -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`));
});

View File

@@ -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();
});

View File

@@ -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') {

View File

@@ -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);
});
}

View File

@@ -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",

View File

@@ -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) {

View File

@@ -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'],

View File

@@ -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",

View File

@@ -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"
}
]
}

View File

@@ -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}"`;

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/node-bridge",
"version": "2.2.0",
"version": "2.2.1-canary.0",
"license": "MIT",
"main": "./index.js",
"repository": {

View File

@@ -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();

View File

@@ -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",

View File

@@ -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 };
}

View File

@@ -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"

View File

@@ -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"
}
}

View File

@@ -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) {

View File

@@ -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",

View File

@@ -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",

View File

@@ -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',
});

View 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;
}

View File

@@ -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 = {};
}

View 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>'
);

View File

@@ -0,0 +1,7 @@
{
"name": "09-build-output-v3",
"private": true,
"scripts": {
"build": "node build.js"
}
}

View 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.`
);
});
});

View File

@@ -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();

View File

@@ -0,0 +1,4 @@
{
"extends": "../tsconfig.json",
"include": ["*.test.ts"]
}

View File

@@ -11,7 +11,7 @@
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "dist",
"types": ["node"],
"types": ["node", "jest"],
"strict": true,
"target": "es2018"
},

9
turbo.json Normal file
View File

@@ -0,0 +1,9 @@
{
"baseBranch": "origin/main",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}

203
yarn.lock
View File

@@ -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"