Compare commits

...

12 Commits

Author SHA1 Message Date
Nathan Rajlich
28f3bf9ef6 Publish Canary
- @vercel/build-utils@2.13.1-canary.2
 - vercel@23.1.3-canary.75
 - @vercel/client@10.2.3-canary.53
 - @vercel/go@1.2.4-canary.7
 - vercel-plugin-middleware@0.0.0-canary.27
 - @vercel/node@1.12.2-canary.10
 - vercel-plugin-go@1.0.0-canary.38
 - vercel-plugin-node@1.12.2-canary.42
 - vercel-plugin-python@1.0.0-canary.39
 - vercel-plugin-ruby@1.0.0-canary.38
 - @vercel/python@2.1.2-canary.5
 - @vercel/ruby@1.2.10-canary.3
2022-02-10 13:44:22 -08:00
Nathan Rajlich
a936e92b8b [build-utils] Retain support for passing in a zipBuffer directly to new Lambda() (#7422) 2022-02-10 13:44:02 -08:00
Nathan Rajlich
ab1decf79d Publish Canary
- @vercel/build-utils@2.13.1-canary.1
 - vercel@23.1.3-canary.74
 - @vercel/client@10.2.3-canary.52
 - @vercel/frameworks@0.5.1-canary.21
 - @vercel/go@1.2.4-canary.6
 - vercel-plugin-middleware@0.0.0-canary.26
 - @vercel/node@1.12.2-canary.9
 - vercel-plugin-go@1.0.0-canary.37
 - vercel-plugin-node@1.12.2-canary.41
 - vercel-plugin-python@1.0.0-canary.38
 - vercel-plugin-ruby@1.0.0-canary.37
 - @vercel/python@2.1.2-canary.4
 - @vercel/ruby@1.2.10-canary.2
2022-02-10 11:51:31 -08:00
Nathan Rajlich
34408a7902 [build-utils] De-couple zip file creation from Lambda class (#7417)
Makes the `Lambda` class itself just a reference of the files that are
needed to produce a zip file, and adds a `createZip()` function that
does the actual zip file creation. This will allow the zip file creation
to be done in the Vercel build container instead of directly in the
Builder itself.

* Adds `files` property to `Lambda` class
* Adds `createZip()` function to `Lambda` class
* Deprecates the `createLambda()` function
* Deprecates the `zipBuffer` property (only populated when `createLambda()` is used)
* Removes the private `FILES_SYMBOL` symbol, since `files` is the same thing
2022-02-10 11:45:05 -08:00
Dominik Ferber
dc2d814d0f Add $schema property to vercel.json (#7394) 2022-02-08 00:56:42 -08:00
Nathan Rajlich
2402db92eb [cli] Add vercel bisect command (#5778)
This PR adds a new `vercel bisect` command which is inspired by `git bisect`. This command takes two deployment URLs as its input (one "bad" deployment that exhibits a bug, and one "good" deployment where the bug does not occur) and steps through the deployments using a binary search. The command is useful for finding a deployment (and commit if the project is connected to a Git repo) where a bug has been introduced.

<img width="582" alt="Screen Shot 2022-01-26 at 12 46 29 PM" src="https://user-images.githubusercontent.com/71256/151244295-8e90c493-c45f-40b4-8e43-f39e7fcac262.png">
2022-02-01 18:39:10 +00:00
Lee Robinson
a1787c740d Add --port $PORT for Vite. (#7378) 2022-01-31 12:16:11 -06:00
Mark Glagola
17fd88e044 [cli] Remove legacy verification handling in alias command (#7305)
* [cli] Remove legacy verification handling in `alias` command

* Update get-domain.ts

Co-authored-by: Nathan Rajlich <n@n8.io>
2022-01-27 12:03:46 -06:00
Nathan Rajlich
03a8fbd3a7 [cli] Use @vercel/fetch-retry in CLI integration tests (#7360)
These integration tests have started consistently failing with
`ECONNRESET` errors and after some debugging it's been
difficult to point the finger at anything related to Vercel
infrastructure. So possibly it's related to GitHub actions infra
changes, but either way retrying seems to help.
2022-01-26 17:42:04 -08:00
Lee Robinson
8d37c1045f [docs] Update README. (#7356) 2022-01-23 23:02:29 -06:00
Jared Palmer
30c433d248 Add runCustomInstallCommand build-util (#7345) 2022-01-21 18:07:50 -05:00
Jiachi Liu
d89a79601c [examples] Update Next.js example CSS styling. (#7337) 2022-01-20 15:59:33 -06:00
39 changed files with 777 additions and 400 deletions

View File

@@ -2,11 +2,11 @@
When contributing to this repository, please first discuss the change you wish to make via [GitHub Discussions](https://github.com/vercel/vercel/discussions/new) with the owners of this repository before submitting a Pull Request.
Please read our [code of conduct](CODE_OF_CONDUCT.md) and follow it in all your interactions with the project.
Please read our [Code of Conduct](CODE_OF_CONDUCT.md) and follow it in all your interactions with the project.
## Local development
This project is configured in a monorepo pattern where one repo contains multiple npm packages. Dependencies are installed and managed with `yarn`, not `npm` CLI.
This project is configured in a monorepo, where one repository contains multiple npm packages. Dependencies are installed and managed with `yarn`, not `npm` CLI.
To get started, execute the following:
@@ -23,7 +23,7 @@ Make sure all the tests pass before making changes.
## Verifying your change
Once you are done with your changes (we even suggest doing it along the way ), make sure all the test still run by running
Once you are done with your changes (we even suggest doing it along the way), make sure all the test still run by running:
```
yarn build && yarn test

View File

@@ -3,13 +3,21 @@
<img src="https://assets.vercel.com/image/upload/v1588805858/repositories/vercel/logo.png" height="96">
<h3 align="center">Vercel</h3>
</a>
<p align="center">Develop. Preview. Ship.</p>
</p>
[![CI Status](https://github.com/vercel/vercel/actions/workflows/test-unit.yml/badge.svg)](https://github.com/vercel/vercel/actions/workflows/test-unit.yml)
[![Join the community on GitHub Discussions](https://badgen.net/badge/join%20the%20discussion/on%20github/black?icon=github)](https://github.com/vercel/vercel/discussions)
<p align="center">
Develop. Preview. Ship.
</p>
## Usage
<p align="center">
<a href="https://vercel.com/docs"><strong>Documentation</strong></a> ·
<a href="https://vercel.com/changelog"><strong>Changelog</strong></a> ·
<a href="https://vercel.com/templates"><strong>Templates</strong></a> ·
<a href="https://vercel.com/cli"><strong>CLI</strong></a>
</p>
<br/>
## Vercel
Vercel is a platform for **static sites and frontend frameworks**, built to integrate with your headless content, commerce, or database.
@@ -17,22 +25,16 @@ We provide a **frictionless developer experience** to take care of the hard thin
We make it easy for frontend teams to **develop, preview, and ship** delightful user experiences, where performance is the default.
Get started by [Importing a Git Project](https://vercel.com/new) and use `git push` to deploy. Alternatively, you can [install Vercel CLI](https://vercel.com/cli).
## Deploy
Get started by [importing a project](https://vercel.com/new) or using the [Vercel CLI](https://vercel.com/cli). Then, `git push` to deploy.
## Documentation
For details on how to use Vercel, check out our [documentation](https://vercel.com/docs).
## Caught a Bug?
## Contributing
1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device
2. Install dependencies with `yarn install`
3. Compile the code: `yarn build`
4. Link the package to the global module directory: `cd ./packages/cli && yarn link`
5. You can start using `vercel` anywhere inside the command line
As always, you should use `yarn test-unit` to run the tests and see if your changes have broken anything.
## How to Create a Release
If you have write access to this repository, you can read more about how to publish a release [here](https://github.com/vercel/vercel/wiki/Creating-a-Release).
- [Code of Conduct](https://github.com/vercel/vercel/blob/main/.github/CODE_OF_CONDUCT.md)
- [Contributing Guidelines](https://github.com/vercel/vercel/blob/main/.github/CONTRIBUTING.md)
- [MIT License](https://github.com/vercel/vercel/blob/main/LICENSE)

50
examples/README.md vendored
View File

@@ -1,28 +1,6 @@
# Vercel Examples
This is the public list of examples for **Vercel**.
All of these ready to deploy examples feature a frontend framework or static site, created with zero configuration using the CLI tools they provide.
The `+functions` examples feature an `/api` directory as well, highlighting how to use serverless functions on top of a framework, again with zero configuration required.
## What is Vercel?
Vercel is a cloud platform for static frontends and serverless functions. It enables developers to host websites and web applications that deploy instantly, scale automatically, and require no supervision.
## What Does this Repository Contain?
This repository consists of multiple examples, created for use with the [Vercel](https://vercel.com) platform. In addition to this, it also contains:
- [Code of Conduct](https://github.com/vercel/vercel/blob/main/.github/CODE_OF_CONDUCT.md) - our Code of Conduct, adapted from the [Contributor Covenant](http://contributor-covenant.org)
- [Contributing Guidelines](https://github.com/vercel/vercel/blob/main/.github/CONTRIBUTING.md) - a guide on how to contribute to the examples repository
- [License](https://github.com/vercel/vercel/blob/main/LICENSE) - the standard MIT license under which these examples are published
We recommend familiarizing yourself with the above sections, particularly if you are looking to make a contribution.
## Deploying Examples
To get started using any of these examples as your own project, [install Vercel](https://vercel.com/download) and use either of the following commands in your terminal:
To get started using any of these examples as your own project, [install Vercel](https://vercel.com/cli) and use either of the following commands in your terminal:
```sh
vercel init # Pick an example in the CLI
@@ -30,7 +8,7 @@ vercel init <example> # Create a new project from a specific <example>
vercel init <example> <name> # Create a new project from a specific <example> with a different folder <name>
```
Deploying your project takes seconds and can be done with **just a single command**:
Deploying your project can be done with **a single command**:
```sh
vercel # Deploy your project with the CLI
@@ -38,26 +16,6 @@ vercel # Deploy your project with the CLI
With the `vercel` command, your project will be built and served by Vercel, providing you with a URL that can be shared immediately.
## New Examples
We are continuously improving our examples based on best practices and feedback from the community. As a result, it is possible that example names will change and on occasion deprecated in favor of an improved implementation.
For example, the previous `nodejs` example showed a static frontend with a Node.js API. This is illustrated in the `svelte` example. Below is a table that lists some of the most popular previous examples and the equivalent replacement:
| Previous Example | New Example |
| ----------------- | ---------------------------------------------------------------------------------------- |
| **monorepo** | [gatsby-functions](https://github.com/vercel/vercel/tree/main/examples/gatsby) |
| **nodejs** | [svelte-functions](https://github.com/vercel/vercel/tree/main/examples/svelte) |
| **nextjs-static** | [nextjs](https://github.com/vercel/vercel/tree/main/examples/nextjs) |
| **vanilla-go** | [create-react-app](https://github.com/vercel/vercel/tree/main/examples/create-react-app) |
| **typescript** | [gatsby-functions](https://github.com/vercel/vercel/tree/main/examples/gatsby) |
## Migrating and Upgrading
If you have an existing project you would like to deploy with Vercel, we recommend reading our guide on [migrating to Vercel and zero configuration](https://vercel.com/guides/migrate-to-vercel). By combining the guide with this repository, you will quickly be able to understand how to deploy your application.
If you would like to upgrade a project to take advantage of zero configuration, you may find the [upgrade guide](https://vercel.com/guides/upgrade-to-zero-configuration) useful. The upgrade guide covers how to remove configuration from existing projects along with how to use the `/api` directory.
## How to Contribute
Contributing examples should be an enjoyable experience, as such we have created a set of [contributing guidelines](https://github.com/vercel/vercel/blob/main/.github/CONTRIBUTING.md) to help you do so.
@@ -74,10 +32,6 @@ An issue can be raised by clicking the 'Issues' tab at the top of the repository
When submitting an issue, please thoroughly and concisely describe the problem you are experiencing so that we may easily understand and resolve the issue in a timely manner.
## License
This repository is an open source project. See the [License](https://github.com/vercel/vercel/blob/main/LICENSE).
## Get In Touch
If you have any questions that are not covered by raising an issue then please get in touch with us on [GitHub Discussions](https://github.com/vercel/vercel/discussions). There you will find both members of the community and staff who are happy to help answer questions on anything Vercel related.

View File

@@ -33,7 +33,7 @@ export default function Home() {
</a>
<a
href="https://github.com/vercel/next.js/tree/master/examples"
href="https://github.com/vercel/next.js/tree/canary/examples"
className={styles.card}
>
<h2>Examples &rarr;</h2>

View File

@@ -1,15 +1,10 @@
.container {
min-height: 100vh;
padding: 0 0.5rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
padding: 0 2rem;
}
.main {
padding: 5rem 0;
min-height: 100vh;
padding: 4rem 0;
flex: 1;
display: flex;
flex-direction: column;
@@ -18,10 +13,10 @@
}
.footer {
width: 100%;
height: 100px;
border-top: 1px solid #eaeaea;
display: flex;
flex: 1;
padding: 2rem 0;
border-top: 1px solid #eaeaea;
justify-content: center;
align-items: center;
}
@@ -56,6 +51,7 @@
}
.description {
margin: 4rem 0;
line-height: 1.5;
font-size: 1.5rem;
}
@@ -75,7 +71,6 @@
justify-content: center;
flex-wrap: wrap;
max-width: 800px;
margin-top: 3rem;
}
.card {
@@ -87,7 +82,7 @@
border: 1px solid #eaeaea;
border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease;
width: 45%;
max-width: 300px;
}
.card:hover,

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "2.13.1-canary.0",
"version": "2.13.1-canary.2",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
@@ -30,7 +30,7 @@
"@types/node-fetch": "^2.1.6",
"@types/semver": "6.0.0",
"@types/yazl": "^2.4.1",
"@vercel/frameworks": "0.5.1-canary.20",
"@vercel/frameworks": "0.5.1-canary.21",
"@vercel/ncc": "0.24.0",
"aggregate-error": "3.0.1",
"async-retry": "1.2.3",

View File

@@ -2,8 +2,8 @@ import fs from 'fs-extra';
import { join, parse, relative, dirname, basename, extname } from 'path';
import glob from './fs/glob';
import { normalizePath } from './fs/normalize-path';
import { FILES_SYMBOL, Lambda } from './lambda';
import type { BuildOptions, Files } from './types';
import { Lambda } from './lambda';
import type { BuildOptions } from './types';
import { debug, getIgnoreFilter } from '.';
// `.output` was already created by the Build Command, so we have
@@ -116,8 +116,7 @@ export function _experimental_convertRuntimeToPlugin(
},
});
// @ts-ignore This symbol is a private API
const lambdaFiles: Files = output[FILES_SYMBOL];
const lambdaFiles = output.files;
// When deploying, the `files` that are passed to the Legacy Runtimes already
// have certain files that are ignored stripped, but locally, that list of

View File

@@ -328,8 +328,12 @@ export async function runNpmInstall(
const opts: SpawnOptionsExtended = { cwd: destPath, ...spawnOpts };
const env = opts.env ? { ...opts.env } : { ...process.env };
delete env.NODE_ENV;
opts.env = env;
opts.env = getEnvForPackageManager({
cliType,
lockfileVersion,
nodeVersion,
env,
});
let commandArgs: string[];
if (cliType === 'npm') {
@@ -337,25 +341,9 @@ export async function runNpmInstall(
commandArgs = args
.filter(a => a !== '--prefer-offline')
.concat(['install', '--no-audit', '--unsafe-perm']);
// If the lockfile version is 2 or greater and the node version is less than 16 than we will force npm7 to be used
if (
typeof lockfileVersion === 'number' &&
lockfileVersion >= 2 &&
(nodeVersion?.major || 0) < 16
) {
// Ensure that npm 7 is at the beginning of the `$PATH`
env.PATH = `/node16/bin-npm7:${env.PATH}`;
console.log('Detected `package-lock.json` generated by npm 7...');
}
} else {
opts.prettyCommand = 'yarn install';
commandArgs = ['install', ...args];
// Yarn v2 PnP mode may be activated, so force "node-modules" linker style
if (!env.YARN_NODE_LINKER) {
env.YARN_NODE_LINKER = 'node-modules';
}
}
if (process.env.NPM_ONLY_PRODUCTION) {
@@ -365,6 +353,65 @@ export async function runNpmInstall(
return spawnAsync(cliType, commandArgs, opts);
}
export function getEnvForPackageManager({
cliType,
lockfileVersion,
nodeVersion,
env,
}: {
cliType: CliType;
lockfileVersion: number | undefined;
nodeVersion: NodeVersion | undefined;
env: { [x: string]: string | undefined };
}) {
const newEnv: { [x: string]: string | undefined } = { ...env };
if (cliType === 'npm') {
if (
typeof lockfileVersion === 'number' &&
lockfileVersion >= 2 &&
(nodeVersion?.major || 0) < 16
) {
// Ensure that npm 7 is at the beginning of the `$PATH`
newEnv.PATH = `/node16/bin-npm7:${env.PATH}`;
console.log('Detected `package-lock.json` generated by npm 7...');
}
} else {
// Yarn v2 PnP mode may be activated, so force "node-modules" linker style
if (!env.YARN_NODE_LINKER) {
newEnv.YARN_NODE_LINKER = 'node-modules';
}
}
return newEnv;
}
export async function runCustomInstallCommand({
destPath,
installCommand,
nodeVersion,
spawnOpts,
}: {
destPath: string;
installCommand: string;
nodeVersion: NodeVersion;
spawnOpts?: SpawnOptions;
}) {
console.log(`Running "install" command: \`${installCommand}\`...`);
const { cliType, lockfileVersion } = await scanParentDirs(destPath);
const env = getEnvForPackageManager({
cliType,
lockfileVersion,
nodeVersion,
env: spawnOpts?.env || {},
});
debug(`Running with $PATH:`, env?.PATH || '');
await execCommand(installCommand, {
...spawnOpts,
env,
cwd: destPath,
});
}
export async function runPackageJsonScript(
destPath: string,
scriptNames: string | Iterable<string>,
@@ -385,23 +432,24 @@ export async function runPackageJsonScript(
debug('Running user script...');
const runScriptTime = Date.now();
const opts: SpawnOptionsExtended = { cwd: destPath, ...spawnOpts };
const env = (opts.env = { ...process.env, ...opts.env });
const opts: SpawnOptionsExtended = {
cwd: destPath,
...spawnOpts,
env: getEnvForPackageManager({
cliType,
lockfileVersion,
nodeVersion: undefined,
env: {
...process.env,
...spawnOpts?.env,
},
}),
};
if (cliType === 'npm') {
opts.prettyCommand = `npm run ${scriptName}`;
if (typeof lockfileVersion === 'number' && lockfileVersion >= 2) {
// Ensure that npm 7 is at the beginning of the `$PATH`
env.PATH = `/node16/bin-npm7:${env.PATH}`;
}
} else {
opts.prettyCommand = `yarn run ${scriptName}`;
// Yarn v2 PnP mode may be activated, so force "node-modules" linker style
if (!env.YARN_NODE_LINKER) {
env.YARN_NODE_LINKER = 'node-modules';
}
}
console.log(`Running "${opts.prettyCommand}"`);

View File

@@ -20,6 +20,8 @@ import {
runBundleInstall,
runPipInstall,
runShellScript,
runCustomInstallCommand,
getEnvForPackageManager,
getNodeVersion,
getSpawnOptions,
getNodeBinPath,
@@ -61,6 +63,8 @@ export {
runBundleInstall,
runPipInstall,
runShellScript,
runCustomInstallCommand,
getEnvForPackageManager,
getNodeVersion,
getLatestNodeVersion,
getDiscontinuedNodeVersions,

View File

@@ -13,17 +13,6 @@ interface Environment {
}
interface LambdaOptions {
zipBuffer: Buffer;
handler: string;
runtime: string;
memory?: number;
maxDuration?: number;
environment: Environment;
allowQuery?: string[];
regions?: string[];
}
interface CreateLambdaOptions {
files: Files;
handler: string;
runtime: string;
@@ -32,6 +21,10 @@ interface CreateLambdaOptions {
environment?: Environment;
allowQuery?: string[];
regions?: string[];
/**
* @deprecated Use `files` property instead.
*/
zipBuffer?: Buffer;
}
interface GetLambdaOptionsFromFunctionOptions {
@@ -39,11 +32,9 @@ interface GetLambdaOptionsFromFunctionOptions {
config?: Pick<Config, 'functions'>;
}
export const FILES_SYMBOL = Symbol('files');
export class Lambda {
public type: 'Lambda';
public zipBuffer: Buffer;
public files: Files;
public handler: string;
public runtime: string;
public memory?: number;
@@ -51,19 +42,54 @@ export class Lambda {
public environment: Environment;
public allowQuery?: string[];
public regions?: string[];
/**
* @deprecated Use `await lambda.createZip()` instead.
*/
public zipBuffer?: Buffer;
constructor({
zipBuffer,
files,
handler,
runtime,
maxDuration,
memory,
environment,
environment = {},
allowQuery,
regions,
zipBuffer,
}: LambdaOptions) {
if (!zipBuffer) {
assert(typeof files === 'object', '"files" must be an object');
}
assert(typeof handler === 'string', '"handler" is not a string');
assert(typeof runtime === 'string', '"runtime" is not a string');
assert(typeof environment === 'object', '"environment" is not an object');
if (memory !== undefined) {
assert(typeof memory === 'number', '"memory" is not a number');
}
if (maxDuration !== undefined) {
assert(typeof maxDuration === 'number', '"maxDuration" is not a number');
}
if (allowQuery !== undefined) {
assert(Array.isArray(allowQuery), '"allowQuery" is not an Array');
assert(
allowQuery.every(q => typeof q === 'string'),
'"allowQuery" is not a string Array'
);
}
if (regions !== undefined) {
assert(Array.isArray(regions), '"regions" is not an Array');
assert(
regions.every(r => typeof r === 'string'),
'"regions" is not a string Array'
);
}
this.type = 'Lambda';
this.zipBuffer = zipBuffer;
this.files = files;
this.handler = handler;
this.runtime = runtime;
this.memory = memory;
@@ -71,70 +97,36 @@ export class Lambda {
this.environment = environment;
this.allowQuery = allowQuery;
this.regions = regions;
this.zipBuffer = zipBuffer;
}
async createZip(): Promise<Buffer> {
let { zipBuffer } = this;
if (!zipBuffer) {
await sema.acquire();
try {
zipBuffer = await createZip(this.files);
} finally {
sema.release();
}
}
return zipBuffer;
}
}
const sema = new Sema(10);
const mtime = new Date(1540000000000);
export async function createLambda({
files,
handler,
runtime,
memory,
maxDuration,
environment = {},
allowQuery,
regions,
}: CreateLambdaOptions): Promise<Lambda> {
assert(typeof files === 'object', '"files" must be an object');
assert(typeof handler === 'string', '"handler" is not a string');
assert(typeof runtime === 'string', '"runtime" is not a string');
assert(typeof environment === 'object', '"environment" is not an object');
/**
* @deprecated Use `new Lambda()` instead.
*/
export async function createLambda(opts: LambdaOptions): Promise<Lambda> {
const lambda = new Lambda(opts);
if (memory !== undefined) {
assert(typeof memory === 'number', '"memory" is not a number');
}
// backwards compat
lambda.zipBuffer = await lambda.createZip();
if (maxDuration !== undefined) {
assert(typeof maxDuration === 'number', '"maxDuration" is not a number');
}
if (allowQuery !== undefined) {
assert(Array.isArray(allowQuery), '"allowQuery" is not an Array');
assert(
allowQuery.every(q => typeof q === 'string'),
'"allowQuery" is not a string Array'
);
}
if (regions !== undefined) {
assert(Array.isArray(regions), '"regions" is not an Array');
assert(
regions.every(r => typeof r === 'string'),
'"regions" is not a string Array'
);
}
await sema.acquire();
try {
const zipBuffer = await createZip(files);
const lambda = new Lambda({
zipBuffer,
handler,
runtime,
memory,
maxDuration,
environment,
regions,
});
// @ts-ignore This symbol is a private API
lambda[FILES_SYMBOL] = files;
return lambda;
} finally {
sema.release();
}
return lambda;
}
export async function createZip(files: Files): Promise<Buffer> {
@@ -177,7 +169,7 @@ export async function getLambdaOptionsFromFunction({
}: GetLambdaOptionsFromFunctionOptions): Promise<
Pick<LambdaOptions, 'memory' | 'maxDuration'>
> {
if (config && config.functions) {
if (config?.functions) {
for (const [pattern, fn] of Object.entries(config.functions)) {
if (sourceFile === pattern || minimatch(sourceFile, pattern)) {
return {

View File

@@ -0,0 +1,109 @@
import assert from 'assert';
import { getEnvForPackageManager, NodeVersion } from '../src';
import { CliType } from '../src/fs/run-user-scripts';
describe('Test `getEnvForPackageManager()`', () => {
const cases = [
{
name: 'should do nothing to env for npm < 6 and node < 16',
args: {
cliType: 'npm' as CliType,
nodeVersion: {
major: 14,
} as NodeVersion,
lockfileVersion: 1,
env: {
FOO: 'bar',
},
},
want: {
FOO: 'bar',
},
},
{
name: 'should set path if npm 7+ is detected and node < 16',
args: {
cliType: 'npm' as CliType,
nodeVersion: {
major: 14,
} as NodeVersion,
lockfileVersion: 2,
env: {
FOO: 'bar',
PATH: 'foo',
},
},
want: {
FOO: 'bar',
PATH: `/node16/bin-npm7:foo`,
},
},
{
name: 'should not set path if node is 16 and npm 7+ is detected',
args: {
cliType: 'npm' as CliType,
nodeVersion: {
major: 16,
} as NodeVersion,
lockfileVersion: 2,
env: {
FOO: 'bar',
PATH: 'foo',
},
},
want: {
FOO: 'bar',
PATH: 'foo',
},
},
{
name: 'should set YARN_NODE_LINKER w/yarn if it is not already defined',
args: {
cliType: 'yarn' as CliType,
nodeVersion: {
major: 16,
} as NodeVersion,
lockfileVersion: 2,
env: {
FOO: 'bar',
},
},
want: {
FOO: 'bar',
YARN_NODE_LINKER: 'node-modules',
},
},
{
name: 'should not set YARN_NODE_LINKER if it already exists',
args: {
cliType: 'yarn' as CliType,
nodeVersion: {
major: 16,
} as NodeVersion,
lockfileVersion: 2,
env: {
FOO: 'bar',
YARN_NODE_LINKER: 'exists',
},
},
want: {
FOO: 'bar',
YARN_NODE_LINKER: 'exists',
},
},
];
for (const { name, want, args } of cases) {
it(name, () => {
assert.deepStrictEqual(
getEnvForPackageManager({
cliType: args.cliType,
lockfileVersion: args.lockfileVersion,
nodeVersion: args.nodeVersion,
env: args.env,
}),
want
);
});
}
});

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "23.1.3-canary.73",
"version": "23.1.3-canary.75",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -43,14 +43,14 @@
"node": ">= 12"
},
"dependencies": {
"@vercel/build-utils": "2.13.1-canary.0",
"@vercel/go": "1.2.4-canary.5",
"@vercel/node": "1.12.2-canary.8",
"@vercel/python": "2.1.2-canary.3",
"@vercel/ruby": "1.2.10-canary.1",
"@vercel/build-utils": "2.13.1-canary.2",
"@vercel/go": "1.2.4-canary.7",
"@vercel/node": "1.12.2-canary.10",
"@vercel/python": "2.1.2-canary.5",
"@vercel/ruby": "1.2.10-canary.3",
"update-notifier": "4.1.0",
"vercel-plugin-middleware": "0.0.0-canary.25",
"vercel-plugin-node": "1.12.2-canary.40"
"vercel-plugin-middleware": "0.0.0-canary.27",
"vercel-plugin-node": "1.12.2-canary.42"
},
"devDependencies": {
"@next/env": "11.1.2",
@@ -90,8 +90,9 @@
"@types/update-notifier": "5.1.0",
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@vercel/client": "10.2.3-canary.51",
"@vercel/frameworks": "0.5.1-canary.20",
"@vercel/client": "10.2.3-canary.53",
"@vercel/fetch-retry": "5.0.3",
"@vercel/frameworks": "0.5.1-canary.21",
"@vercel/ncc": "0.24.0",
"@vercel/nft": "0.17.0",
"@zeit/fun": "0.11.2",
@@ -142,7 +143,7 @@
"ms": "2.1.2",
"node-fetch": "2.6.1",
"npm-package-arg": "6.1.0",
"open": "8.2.0",
"open": "8.4.0",
"ora": "3.4.0",
"pcre-to-regexp": "1.0.0",
"pluralize": "7.0.0",

View File

@@ -6,7 +6,6 @@ import { Output } from '../../util/output';
import * as ERRORS from '../../util/errors-ts';
import assignAlias from '../../util/alias/assign-alias';
import Client from '../../util/client';
import formatNSTable from '../../util/format-ns-table';
import getDeploymentByIdOrHost from '../../util/deploy/get-deployment-by-id-or-host';
import { getDeploymentForAlias } from '../../util/alias/get-deployment-by-alias';
import getScope from '../../util/get-scope';
@@ -226,29 +225,6 @@ function handleSetupDomainError<T>(
output: Output,
error: SetupDomainError | T
): T | 1 {
if (
error instanceof ERRORS.DomainVerificationFailed ||
error instanceof ERRORS.DomainNsNotVerifiedForWildcard
) {
const { nsVerification, domain } = error.meta;
output.error(
`We could not alias since the domain ${domain} could not be verified due to the following reasons:\n`
);
output.print(
`Nameservers verification failed since we see a different set than the intended set:`
);
output.print(
`\n${formatNSTable(
nsVerification.intendedNameservers,
nsVerification.nameservers,
{ extraSpace: ' ' }
)}\n\n`
);
output.print(' Read more: https://err.sh/vercel/domain-verification\n');
return 1;
}
if (error instanceof ERRORS.DomainPermissionDenied) {
output.error(
`You don't have permissions over domain ${chalk.underline(

View File

@@ -0,0 +1,411 @@
import open from 'open';
import boxen from 'boxen';
import execa from 'execa';
import plural from 'pluralize';
import inquirer from 'inquirer';
import { resolve } from 'path';
import chalk, { Chalk } from 'chalk';
import { URLSearchParams, parse } from 'url';
import sleep from '../../util/sleep';
import formatDate from '../../util/format-date';
import link from '../../util/output/link';
import logo from '../../util/output/logo';
import getArgs from '../../util/get-args';
import Client from '../../util/client';
import { getPkgName } from '../../util/pkg-name';
import { Output } from '../../util/output';
import { Deployment, PaginationOptions } from '../../types';
interface DeploymentV6
extends Pick<
Deployment,
'url' | 'target' | 'projectId' | 'ownerId' | 'meta' | 'inspectorUrl'
> {
createdAt: number;
}
interface Deployments {
deployments: DeploymentV6[];
pagination: PaginationOptions;
}
const pkgName = getPkgName();
const help = () => {
console.log(`
${chalk.bold(`${logo} ${pkgName} bisect`)} [options]
${chalk.dim('Options:')}
-h, --help Output usage information
-d, --debug Debug mode [off]
-b, --bad Known bad URL
-g, --good Known good URL
-o, --open Automatically open each URL in the browser
-p, --path Subpath of the deployment URL to test
-r, --run Test script to run for each deployment
${chalk.dim('Examples:')}
${chalk.gray('')} Bisect the current project interactively
${chalk.cyan(`$ ${pkgName} bisect`)}
${chalk.gray('')} Bisect with a known bad deployment
${chalk.cyan(`$ ${pkgName} bisect --bad example-310pce9i0.vercel.app`)}
${chalk.gray('')} Bisect specifying a deployment that was working 3 days ago
${chalk.cyan(`$ ${pkgName} bisect --good 3d`)}
${chalk.gray('')} Automated bisect with a run script
${chalk.cyan(`$ ${pkgName} bisect --run ./test.sh`)}
`);
};
export default async function main(client: Client): Promise<number> {
const { output } = client;
const argv = getArgs(client.argv.slice(2), {
'--bad': String,
'-b': '--bad',
'--good': String,
'-g': '--good',
'--open': Boolean,
'-o': '--open',
'--path': String,
'-p': '--path',
'--run': String,
'-r': '--run',
});
if (argv['--help']) {
help();
return 2;
}
let bad =
argv['--bad'] ||
(await prompt(output, `Specify a URL where the bug occurs:`));
let good =
argv['--good'] ||
(await prompt(output, `Specify a URL where the bug does not occur:`));
let subpath = argv['--path'] || '';
let run = argv['--run'] || '';
const openEnabled = argv['--open'] || false;
if (run) {
run = resolve(run);
}
if (!bad.startsWith('https://')) {
bad = `https://${bad}`;
}
let parsed = parse(bad);
if (!parsed.hostname) {
output.error('Invalid input: no hostname provided');
return 1;
}
bad = parsed.hostname;
if (typeof parsed.path === 'string' && parsed.path !== '/') {
if (subpath && subpath !== parsed.path) {
output.note(
`Ignoring subpath ${chalk.bold(
parsed.path
)} in favor of \`--path\` argument ${chalk.bold(subpath)}`
);
} else {
subpath = parsed.path;
}
}
const badDeploymentPromise = getDeployment(client, bad).catch(err => err);
if (!good.startsWith('https://')) {
good = `https://${good}`;
}
parsed = parse(good);
if (!parsed.hostname) {
output.error('Invalid input: no hostname provided');
return 1;
}
good = parsed.hostname;
if (
typeof parsed.path === 'string' &&
parsed.path !== '/' &&
subpath &&
subpath !== parsed.path
) {
output.note(
`Ignoring subpath ${chalk.bold(
parsed.path
)} which does not match ${chalk.bold(subpath)}`
);
}
const goodDeploymentPromise = getDeployment(client, good).catch(err => err);
if (!subpath) {
subpath = await prompt(
output,
`Specify the URL subpath where the bug occurs:`
);
}
output.spinner('Retrieving deployments…');
const [badDeployment, goodDeployment] = await Promise.all([
badDeploymentPromise,
goodDeploymentPromise,
]);
if (badDeployment) {
if (badDeployment instanceof Error) {
badDeployment.message += ` "${bad}"`;
output.prettyError(badDeployment);
return 1;
}
bad = badDeployment.url;
} else {
output.error(`Failed to retrieve ${chalk.bold('bad')} Deployment: ${bad}`);
return 1;
}
const { projectId } = badDeployment;
if (goodDeployment) {
if (goodDeployment instanceof Error) {
goodDeployment.message += ` "${good}"`;
output.prettyError(goodDeployment);
return 1;
}
good = goodDeployment.url;
} else {
output.error(
`Failed to retrieve ${chalk.bold('good')} Deployment: ${good}`
);
return 1;
}
if (projectId !== goodDeployment.projectId) {
output.error(`Good and Bad deployments must be from the same Project`);
return 1;
}
if (badDeployment.createdAt < goodDeployment.createdAt) {
output.error(`Good deployment must be older than the Bad deployment`);
return 1;
}
if (badDeployment.target !== goodDeployment.target) {
output.error(
`Bad deployment target "${badDeployment.target || 'preview'}" does not match good deployment target "${goodDeployment.target || 'preview'}"`
);
return 1;
}
// Fetch all the project's "READY" deployments with the pagination API
let deployments: DeploymentV6[] = [];
let next: number | undefined = badDeployment.createdAt + 1;
do {
const query = new URLSearchParams();
query.set('projectId', projectId);
if (badDeployment.target) {
query.set('target', badDeployment.target);
}
query.set('limit', '100');
query.set('state', 'READY');
if (next) {
query.set('until', String(next));
}
const res = await client.fetch<Deployments>(`/v6/deployments?${query}`, {
accountId: badDeployment.ownerId,
});
next = res.pagination.next;
let newDeployments = res.deployments;
// If we have the "good" deployment in this chunk, then we're done
for (let i = 0; i < newDeployments.length; i++) {
if (newDeployments[i].url === good) {
newDeployments = newDeployments.slice(0, i + 1);
next = undefined;
break;
}
}
deployments = deployments.concat(newDeployments);
if (next) {
// Small sleep to avoid rate limiting
await sleep(100);
}
} while (next);
if (!deployments.length) {
output.error(
'Cannot bisect because this project does not have any deployments'
);
return 1;
}
// The first deployment is the one that was marked
// as "bad", so that one does not need to be tested
let lastBad = deployments.shift()!;
while (deployments.length > 0) {
// Add a blank space before the next step
output.print('\n');
const middleIndex = Math.floor(deployments.length / 2);
const deployment = deployments[middleIndex];
const rem = plural('deployment', deployments.length, true);
const steps = Math.floor(Math.log2(deployments.length));
const pSteps = plural('step', steps, true);
output.log(
chalk.magenta(
`${chalk.bold(
'Bisecting:'
)} ${rem} left to test after this (roughly ${pSteps})`
),
chalk.magenta
);
const testUrl = `https://${deployment.url}${subpath}`;
output.log(`${chalk.bold('Deployment URL:')} ${link(testUrl)}`);
output.log(`${chalk.bold('Date:')} ${formatDate(deployment.createdAt)}`);
const commit = getCommit(deployment);
if (commit) {
const shortSha = commit.sha.substring(0, 7);
const firstLine = commit.message.split('\n')[0];
output.log(`${chalk.bold('Commit:')} [${shortSha}] ${firstLine}`);
}
let action: string;
if (run) {
const proc = await execa(run, [testUrl], {
stdio: 'inherit',
reject: false,
env: {
...process.env,
HOST: deployment.url,
URL: testUrl,
},
});
if (proc instanceof Error && typeof proc.exitCode !== 'number') {
// Script does not exist or is not executable, so exit
output.prettyError(proc);
return 1;
}
const { exitCode } = proc;
let color: Chalk;
if (exitCode === 0) {
color = chalk.green;
action = 'good';
} else if (exitCode === 125) {
action = 'skip';
color = chalk.grey;
} else {
action = 'bad';
color = chalk.red;
}
output.log(
`Run script returned exit code ${chalk.bold(String(exitCode))}: ${color(
action
)}`
);
} else {
if (openEnabled) {
await open(testUrl);
}
const answer = await inquirer.prompt({
type: 'expand',
name: 'action',
message: 'Select an action:',
choices: [
{ key: 'g', name: 'Good', value: 'good' },
{ key: 'b', name: 'Bad', value: 'bad' },
{ key: 's', name: 'Skip', value: 'skip' },
],
});
action = answer.action;
}
if (action === 'good') {
deployments = deployments.slice(0, middleIndex);
} else if (action === 'bad') {
lastBad = deployment;
deployments = deployments.slice(middleIndex + 1);
} else if (action === 'skip') {
deployments.splice(middleIndex, 1);
}
}
output.print('\n');
let result = [
chalk.bold(
`The first bad deployment is: ${link(`https://${lastBad.url}`)}`
),
'',
` ${chalk.bold('Date:')} ${formatDate(lastBad.createdAt)}`,
];
const commit = getCommit(lastBad);
if (commit) {
const shortSha = commit.sha.substring(0, 7);
const firstLine = commit.message.split('\n')[0];
result.push(` ${chalk.bold('Commit:')} [${shortSha}] ${firstLine}`);
}
result.push(`${chalk.bold('Inspect:')} ${link(lastBad.inspectorUrl)}`);
output.print(boxen(result.join('\n'), { padding: 1 }));
output.print('\n');
return 0;
}
function getDeployment(
client: Client,
hostname: string
): Promise<DeploymentV6> {
const query = new URLSearchParams();
query.set('url', hostname);
query.set('resolve', '1');
query.set('noState', '1');
return client.fetch<DeploymentV6>(`/v10/deployments/get?${query}`);
}
function getCommit(deployment: DeploymentV6) {
const sha =
deployment.meta?.githubCommitSha ||
deployment.meta?.gitlabCommitSha ||
deployment.meta?.bitbucketCommitSha;
if (!sha) return null;
const message =
deployment.meta?.githubCommitMessage ||
deployment.meta?.gitlabCommitMessage ||
deployment.meta?.bitbucketCommitMessage;
return { sha, message };
}
async function prompt(output: Output, message: string): Promise<string> {
// eslint-disable-next-line no-constant-condition
while (true) {
const { val } = await inquirer.prompt({
type: 'input',
name: 'val',
message,
});
if (val) {
return val;
} else {
output.error('A value must be specified');
}
}
}

View File

@@ -28,6 +28,7 @@ export const help = () => `
${chalk.dim('Advanced')}
rm | remove [id] Removes a deployment
bisect Use binary search to find the deployment that introduced a bug
domains [name] Manages your domain names
projects Manages your Projects
dns [name] Manages your DNS records

View File

@@ -2,6 +2,7 @@ export default new Map([
['alias', 'alias'],
['aliases', 'alias'],
['billing', 'billing'],
['bisect', 'bisect'],
['build', 'build'],
['cc', 'billing'],
['cert', 'certs'],

View File

@@ -627,6 +627,9 @@ const main = async () => {
case 'billing':
func = await import('./commands/billing');
break;
case 'bisect':
func = await import('./commands/bisect');
break;
case 'build':
func = await import('./commands/build');
break;

View File

@@ -85,10 +85,6 @@ export type Domain = {
transferredAt?: number | null;
orderedAt?: number;
serviceType: 'zeit.world' | 'external' | 'na';
verified: boolean;
nsVerifiedAt: number | null;
txtVerifiedAt: number | null;
verificationRecord: string;
nameservers: string[];
intendedNameservers: string[];
creator: {
@@ -133,6 +129,13 @@ export type Deployment = {
created: number;
createdAt: number;
creator: { uid: string; username: string };
target: string | null;
ownerId: string;
projectId: string;
inspectorUrl: string;
meta: {
[key: string]: any;
};
};
export type Alias = {

View File

@@ -35,6 +35,7 @@ import {
BuildResultV3,
BuilderOutputs,
EnvConfigs,
BuiltLambda,
} from './types';
import { normalizeRoutes } from '@vercel/routing-utils';
import getUpdateCommand from '../get-update-command';
@@ -288,7 +289,7 @@ export async function executeBuild(
// subclass type instances.
for (const name of Object.keys(output)) {
const obj = output[name] as File;
let lambda: Lambda;
let lambda: BuiltLambda;
let fileRef: FileFsRef;
let fileBlob: FileBlob;
switch (obj.type) {
@@ -302,7 +303,7 @@ export async function executeBuild(
output[name] = fileBlob;
break;
case 'Lambda':
lambda = Object.assign(Object.create(Lambda.prototype), obj) as Lambda;
lambda = Object.assign(Object.create(Lambda.prototype), obj);
// Convert the JSON-ified Buffer object back into an actual Buffer
lambda.zipBuffer = Buffer.from((obj as any).zipBuffer.data);
output[name] = lambda;

View File

@@ -66,6 +66,7 @@ export interface BuilderInputs {
}
export interface BuiltLambda extends Lambda {
zipBuffer: Buffer;
fn?: FunLambda;
}

View File

@@ -16,7 +16,7 @@ export async function getDomain(
);
try {
const { domain } = await client.fetch<Response>(
`/v4/domains/${domainName}`
`/v5/domains/${domainName}`
);
return domain;

View File

@@ -7,9 +7,7 @@ import addDomain from './add-domain';
import Client from '../client';
import maybeGetDomainByName from './maybe-get-domain-by-name';
import purchaseDomainIfAvailable from './purchase-domain-if-available';
import verifyDomain from './verify-domain';
import extractDomain from '../alias/extract-domain';
import isWildcardAlias from '../alias/is-wildcard-alias';
export default async function setupDomain(
output: Output,
@@ -27,36 +25,6 @@ export default async function setupDomain(
if (info) {
const { name: domain } = info;
output.debug(`Domain ${domain} found for the given context`);
if (!info.verified || (!info.nsVerifiedAt && isWildcardAlias(alias))) {
output.debug(
`Domain ${domain} is not verified, trying to perform a verification`
);
const verificationResult = await verifyDomain(
client,
domain,
contextName
);
if (verificationResult instanceof ERRORS.DomainVerificationFailed) {
output.debug(`Domain ${domain} verification failed`);
return verificationResult;
}
if (!verificationResult.nsVerifiedAt && isWildcardAlias(alias)) {
return new ERRORS.DomainNsNotVerifiedForWildcard({
domain,
nsVerification: {
intendedNameservers: verificationResult.intendedNameservers,
nameservers: verificationResult.nameservers,
},
});
}
output.debug(`Domain ${domain} successfuly verified`);
return maybeGetDomainByName(client, contextName, domain) as Promise<
Domain
>;
}
output.debug(`Domain ${domain} is already verified`);
return info;
}
@@ -92,21 +60,6 @@ export default async function setupDomain(
return addResult;
}
if (!addResult.verified) {
const verificationResult = await verifyDomain(
client,
domain,
contextName
);
if (verificationResult instanceof ERRORS.DomainVerificationFailed) {
output.debug(`Domain ${domain} was added but it couldn't be verified`);
return verificationResult;
}
output.debug(`Domain ${domain} successfuly added and manually verified`);
return verificationResult;
}
output.debug(
`Domain ${domain} successfuly added and automatically verified`
);
@@ -120,23 +73,6 @@ export default async function setupDomain(
aliasDomain
)) as Domain;
const { name: domain } = purchasedDomain;
if (!purchasedDomain.verified) {
const verificationResult = await verifyDomain(client, domain, contextName);
if (verificationResult instanceof ERRORS.DomainVerificationFailed) {
output.debug(
`Domain ${domain} was purchased but verification is still pending`
);
return new ERRORS.DomainVerificationFailed({
domain: verificationResult.meta.domain,
nsVerification: verificationResult.meta.nsVerification,
txtVerification: verificationResult.meta.txtVerification,
purchased: true,
});
}
output.debug(`Domain ${domain} was purchased and it was manually verified`);
return maybeGetDomainByName(client, contextName, domain) as Promise<Domain>;
}
output.debug(
`Domain ${domain} was purchased and it is automatically verified`

View File

@@ -1,47 +0,0 @@
import chalk from 'chalk';
import retry from 'async-retry';
import { Domain } from '../../types';
import * as ERRORS from '../errors-ts';
import Client from '../client';
export default async function verifyDomain(
client: Client,
domainName: string,
contextName: string
) {
client.output.spinner(
`Verifying domain ${domainName} under ${chalk.bold(contextName)}`
);
try {
const { domain } = await performVerifyDomain(client, domainName);
return domain;
} catch (error) {
if (error.code === 'verification_failed') {
return new ERRORS.DomainVerificationFailed({
purchased: false,
domain: error.name as string,
nsVerification: error.nsVerification as ERRORS.NSVerificationError,
txtVerification: error.txtVerification as ERRORS.TXTVerificationError,
});
}
throw error;
}
}
type Response = {
domain: Domain;
};
async function performVerifyDomain(client: Client, domain: string) {
return retry(
async () =>
client.fetch<Response>(
`/v4/domains/${encodeURIComponent(domain)}/verify`,
{
body: {},
method: 'POST',
}
),
{ retries: 5, maxTimeout: 8000 }
);
}

View File

@@ -260,31 +260,6 @@ export type TXTVerificationError = {
values: string[];
};
/**
* This error is returned when the domain is not verified by nameservers for wildcard alias.
*/
export class DomainNsNotVerifiedForWildcard extends NowError<
'DOMAIN_NS_NOT_VERIFIED_FOR_WILDCARD',
{
domain: string;
nsVerification: NSVerificationError;
}
> {
constructor({
domain,
nsVerification,
}: {
domain: string;
nsVerification: NSVerificationError;
}) {
super({
code: 'DOMAIN_NS_NOT_VERIFIED_FOR_WILDCARD',
meta: { domain, nsVerification },
message: `The domain ${domain} is not verified by nameservers for wildcard alias.`,
});
}
}
/**
* Used when a domain is validated because we tried to add it to an account
* via API or for any other reason.

View File

@@ -7,9 +7,10 @@ import { Readable } from 'stream';
import { homedir } from 'os';
import _execa from 'execa';
import XDGAppPaths from 'xdg-app-paths';
import fetch from 'node-fetch';
import nodeFetch from 'node-fetch';
import tmp from 'tmp-promise';
import retry from 'async-retry';
import createFetchRetry from '@vercel/fetch-retry';
import fs, {
writeFile,
readFile,
@@ -24,6 +25,8 @@ import pkg from '../package';
import prepareFixtures from './helpers/prepare';
import { fetchTokenWithRetry } from '../../../test/lib/deployment/now-deploy';
const fetch = createFetchRetry(nodeFetch);
// log command when running `execa`
function execa(file, args, options) {
console.log(`$ vercel ${args.join(' ')}`);

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "10.2.3-canary.51",
"version": "10.2.3-canary.53",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -40,7 +40,7 @@
]
},
"dependencies": {
"@vercel/build-utils": "2.13.1-canary.0",
"@vercel/build-utils": "2.13.1-canary.2",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",
"async-sema": "3.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/frameworks",
"version": "0.5.1-canary.20",
"version": "0.5.1-canary.21",
"main": "./dist/frameworks.js",
"types": "./dist/frameworks.d.ts",
"files": [

View File

@@ -1941,7 +1941,7 @@ export const frameworks = [
},
devCommand: {
placeholder: 'vite',
value: 'vite',
value: 'vite --port $PORT',
},
outputDirectory: {
value: 'dist',

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/go",
"version": "1.2.4-canary.5",
"version": "1.2.4-canary.7",
"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.13.1-canary.0",
"@vercel/build-utils": "2.13.1-canary.2",
"@vercel/ncc": "0.24.0",
"async-retry": "1.3.1",
"execa": "^1.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "vercel-plugin-middleware",
"version": "0.0.0-canary.25",
"version": "0.0.0-canary.27",
"license": "MIT",
"main": "./dist/index",
"homepage": "",
@@ -30,7 +30,7 @@
"@types/node-fetch": "^2",
"@types/ua-parser-js": "0.7.36",
"@types/uuid": "8.3.1",
"@vercel/build-utils": "2.13.1-canary.0",
"@vercel/build-utils": "2.13.1-canary.2",
"@vercel/ncc": "0.24.0",
"cookie": "0.4.1",
"formdata-node": "4.3.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/node",
"version": "1.12.2-canary.8",
"version": "1.12.2-canary.10",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
@@ -32,7 +32,7 @@
"@types/cookie": "0.3.3",
"@types/etag": "1.8.0",
"@types/test-listen": "1.1.0",
"@vercel/build-utils": "2.13.1-canary.0",
"@vercel/build-utils": "2.13.1-canary.2",
"@vercel/ncc": "0.24.0",
"@vercel/nft": "0.14.0",
"@vercel/node-bridge": "2.1.1-canary.2",

View File

@@ -1,7 +1,7 @@
{
"private": false,
"name": "vercel-plugin-go",
"version": "1.0.0-canary.36",
"version": "1.0.0-canary.38",
"main": "dist/index.js",
"license": "MIT",
"files": [
@@ -17,8 +17,8 @@
"prepublishOnly": "tsc"
},
"dependencies": {
"@vercel/build-utils": "2.13.1-canary.0",
"@vercel/go": "1.2.4-canary.5"
"@vercel/build-utils": "2.13.1-canary.2",
"@vercel/go": "1.2.4-canary.7"
},
"devDependencies": {
"@types/node": "*",

View File

@@ -1,6 +1,6 @@
{
"name": "vercel-plugin-node",
"version": "1.12.2-canary.40",
"version": "1.12.2-canary.42",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
@@ -34,7 +34,7 @@
"@types/node-fetch": "2",
"@types/test-listen": "1.1.0",
"@types/yazl": "2.4.2",
"@vercel/build-utils": "2.13.1-canary.0",
"@vercel/build-utils": "2.13.1-canary.2",
"@vercel/fun": "1.0.3",
"@vercel/ncc": "0.24.0",
"@vercel/nft": "0.14.0",

View File

@@ -1,7 +1,7 @@
{
"private": false,
"name": "vercel-plugin-python",
"version": "1.0.0-canary.37",
"version": "1.0.0-canary.39",
"main": "dist/index.js",
"license": "MIT",
"files": [
@@ -17,8 +17,8 @@
"prepublishOnly": "tsc"
},
"dependencies": {
"@vercel/build-utils": "2.13.1-canary.0",
"@vercel/python": "2.1.2-canary.3"
"@vercel/build-utils": "2.13.1-canary.2",
"@vercel/python": "2.1.2-canary.5"
},
"devDependencies": {
"@types/node": "*",

View File

@@ -1,7 +1,7 @@
{
"private": false,
"name": "vercel-plugin-ruby",
"version": "1.0.0-canary.36",
"version": "1.0.0-canary.38",
"main": "dist/index.js",
"license": "MIT",
"files": [
@@ -17,8 +17,8 @@
"prepublishOnly": "tsc"
},
"dependencies": {
"@vercel/build-utils": "2.13.1-canary.0",
"@vercel/ruby": "1.2.10-canary.1"
"@vercel/build-utils": "2.13.1-canary.2",
"@vercel/ruby": "1.2.10-canary.3"
},
"devDependencies": {
"@types/node": "*",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/python",
"version": "2.1.2-canary.3",
"version": "2.1.2-canary.5",
"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.13.1-canary.0",
"@vercel/build-utils": "2.13.1-canary.2",
"@vercel/ncc": "0.24.0",
"execa": "^1.0.0",
"typescript": "4.3.4"

View File

@@ -1,7 +1,7 @@
{
"name": "@vercel/ruby",
"author": "Nathan Cahill <nathan@nathancahill.com>",
"version": "1.2.10-canary.1",
"version": "1.2.10-canary.3",
"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.13.1-canary.0",
"@vercel/build-utils": "2.13.1-canary.2",
"@vercel/ncc": "0.24.0",
"execa": "2.0.4",
"fs-extra": "^7.0.1",

View File

@@ -1,4 +1,5 @@
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"rewrites": [
{
"source": "/",

View File

@@ -2613,6 +2613,14 @@
"@typescript-eslint/types" "4.28.0"
eslint-visitor-keys "^2.0.0"
"@vercel/fetch-retry@5.0.3":
version "5.0.3"
resolved "https://registry.yarnpkg.com/@vercel/fetch-retry/-/fetch-retry-5.0.3.tgz#cce5d23f6e64f6f525c24e2ac7c78f65d6c5b1f4"
integrity sha512-DIIoBY92r+sQ6iHSf5WjKiYvkdsDIMPWKYATlE0KcUAj2RV6SZK9UWpUzBRKsofXqedOqpVjrI0IE6AWL7JRtg==
dependencies:
async-retry "^1.3.1"
debug "^3.1.0"
"@vercel/fun@1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@vercel/fun/-/fun-1.0.3.tgz#5e5c4921a3a3a35ee129ce063e6b3f5c4b1eae48"
@@ -9346,10 +9354,10 @@ onetime@^5.1.2:
dependencies:
mimic-fn "^2.1.0"
open@8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/open/-/open-8.2.0.tgz#d6a4788b00009a9d60df471ecb89842a15fdcfc1"
integrity sha512-O8uInONB4asyY3qUcEytpgwxQG3O0fJ/hlssoUHsBboOIRVZzT6Wq+Rwj5nffbeUhOdMjpXeISpDDzHCMRDuOQ==
open@8.4.0:
version "8.4.0"
resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8"
integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==
dependencies:
define-lazy-prop "^2.0.0"
is-docker "^2.1.1"