Compare commits

..

30 Commits

Author SHA1 Message Date
jj@jjsweb.site
2ad27eefb0 Publish Canary
- @vercel/build-utils@2.12.3-canary.0
 - vercel@23.1.3-canary.0
 - @vercel/client@10.2.3-canary.0
 - @vercel/frameworks@0.5.1-canary.1
 - @vercel/go@1.2.4-canary.0
 - @vercel/node-bridge@2.1.1-canary.0
 - @vercel/node@1.12.2-canary.0
 - @vercel/python@2.0.6-canary.0
 - @vercel/ruby@1.2.8-canary.0
2021-08-24 09:36:40 -05:00
Nathan Rajlich
578fe8a930 [cli] Support --project flag in vc link command (#6614)
To make setting up local dev README instructions easier for new users being introduced to a Vercel project, support flags to make the `vc link` command be non-interactive, in the case where the project name does not match the name of the directory where the code is located:

```
$ vc link --scope acme --project docs
```

Related to https://github.com/vercel/front/pull/10732.
2021-08-23 23:22:57 +00:00
JJ Kasper
04ea3bb88d [node-bridge] Fix error in node-bridge from decoded path (#6583)
* Fix error in node-bridge from decoded path

* Apply suggestions from code review

Co-authored-by: Steven <steven@ceriously.com>
2021-08-23 14:50:18 -05:00
Steven
46116022b7 [cli] Update readme (#6622) 2021-08-23 12:04:38 -04:00
Lee Robinson
f80539df82 Update README (#6618) 2021-08-22 23:39:04 +00:00
Johan Eliasson
daf06307b4 Fix deprecation message for NowRequestBody (#6613) 2021-08-20 12:58:47 -07:00
Nathan Rajlich
0bd028cd84 [cli] Show server-side errorMessage upon deployment UNEXPECTED_ERROR (#6606)
Before:

```
Error! Unexpected error. Please try again later. ()
```

After:

```
Error! An unexpected error happened when running this build. We have been notified of the problem. If you have any questions, please contact support@vercel.com.
```
2021-08-17 18:28:56 -07:00
Nathan Rajlich
1c48030e1e [cli] Stop installing @now/build-utils for vc dev (#6604)
Users should not be using any legacy Runtimes that depend on `@now/build-utils` at this point, since those have not received updates in a long time.
2021-08-17 17:35:55 +00:00
Nathan Rajlich
1dc05428d7 [go][node][python][ruby] Remove @now/build-utils backwards compat hack (#6603)
`@now/build-utils` has not been published in a long time, so let's remove this logic.
2021-08-17 15:29:52 +00:00
Nathan Rajlich
288dca045c [cli] Refactor a handful of util modules to TypeScript (#6584) 2021-08-13 20:26:02 +00:00
Steven
8c5bc04fde Fix publish script for Windows (#6591)
* Fix publish script for Windows

* Update changelog.js
2021-08-13 12:03:54 -04:00
Steven
a07e6fc103 [examples] Bump Next.js to 11.1.0 (#6582) 2021-08-11 15:41:38 -04:00
Nathan Rajlich
9af3054d41 [cli] Use client.fetch() in vc init (#6575)
Gives us debug logging and retries, etc.
2021-08-11 19:04:01 +00:00
Nathan Rajlich
9fb254e14a [cli] Attempt to fix the "lambda-with-3-second-timeout" E2E test (#6579) 2021-08-11 00:50:57 -07:00
William Li
3616bdf17a bump react-scripts to @latest (#6577) 2021-08-11 11:11:43 +08:00
Nathan Rajlich
52a89fd4b7 [cli] Remove withSpinner() helper (#6576)
It's not really necessary and doesn't use the common `Output` instance.
2021-08-10 10:41:44 -07:00
Nathan Rajlich
d4db6635f1 [cli] Remove unnecessary await in Dev tests (#6572)
This is the result of VS Code's "remove all unneeded await calls" formatter.
2021-08-10 02:01:56 -07:00
Nathan Rajlich
f1009a80cd [api] Fix GET /api/examples/list output (#6574)
A regression from #6554 caused the return value to contain a nested
object with a `name` property for the `name` key of the response in the
list.
2021-08-09 22:24:17 -07:00
Steven
2756d1e323 [client][frameworks][api] Update readdir() call with withFileTypes (#6554)
Avoids running unnecessary code such as `stat().isDirectory()`.
2021-08-09 14:27:50 -07:00
Nathan Rajlich
5b61b16bd1 Publish Stable
- @vercel/build-utils@2.12.2
 - vercel@23.1.2
 - @vercel/client@10.2.2
 - @vercel/node@1.12.1
2021-08-09 11:21:40 -07:00
Nathan Rajlich
41c61f8f8b Publish Canary
- vercel@23.1.2-canary.1
2021-08-09 10:00:43 -07:00
Nathan Rajlich
6c52e1fad7 [cli] Add "outDir" to tsconfig.json (#6566)
Fixes TypeScript errors.
2021-08-09 09:58:30 -07:00
Nathan Rajlich
d2e82fdc3a [cli] Fix in-flight re-login when there are no existing credentials (#6565) 2021-08-08 00:57:23 -07:00
Nathan Rajlich
a60b1b225b Publish Canary
- @vercel/build-utils@2.12.2-canary.0
 - vercel@23.1.2-canary.0
 - @vercel/client@10.2.2-canary.0
 - @vercel/node@1.12.1-canary.0
2021-08-06 17:15:13 -07:00
Kaitlyn
18bec983ae [build-utils] check to see that the node version is less then 16 befo… (#6553)
### Related Issues

Check the project's nodeVersion setting for when we release Node 16 and later

### 📋 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
2021-08-07 00:14:21 +00:00
Nathan Rajlich
e6fb2ffe05 Publish Stable
- @vercel/build-utils@2.12.1
 - vercel@23.1.1
 - @vercel/client@10.2.1
2021-08-06 13:00:16 -07:00
Nathan Rajlich
0533cfd566 [build-utils] Fix test name 2021-08-06 12:48:58 -07:00
Nathan Rajlich
3db8618885 Publish Canary
- @vercel/build-utils@2.12.1-canary.0
 - vercel@23.1.1-canary.0
 - @vercel/client@10.2.1-canary.0
 - @vercel/frameworks@0.5.1-canary.0
2021-08-06 11:52:56 -07:00
Nathan Rajlich
4722ea5ad6 [build-utils] Make scanParentDirs() work with npm Workspaces (#6559)
Currently, when `scanParentDirs()` finds the closest `package.json` file, it stops iteration regardless of whether or not a lockfile was found. For npm Workspaces, this is problematic because if you are deploying a subdirectory of a monorepo, then there will be no lockfile in the subdirectory (the `package-lock.json` file is at the root of the project). So instead of stopping once the `package.json` file is found, instead stop when the first lockfile is found.
2021-08-06 18:48:23 +00:00
Nathan Rajlich
c7bd6f3266 [tests] Define jest/ts-jest deps at the root level (#6556)
And remove `jest`/`ts-jest` from the individual packages. It is important to have a consistent version of `jest` being used throughout the monorepo, otherwise bizarre errors surface that cause jest to crash, such as [here](https://github.com/vercel/vercel/runs/3255999061?check_suite_focus=true):

```
TypeError: this._environment.runScript is not a function

  at Runtime._execModule (../../node_modules/jest-runner/node_modules/jest-runtime/build/index.js:856:41)
```
2021-08-05 21:06:18 +00:00
106 changed files with 4170 additions and 2981 deletions

View File

@@ -11,7 +11,11 @@
## Usage ## Usage
Vercel is the optimal workflow for frontend teams. All-in-one: Static and Jamstack deployment, Serverless Functions, and Global CDN. Vercel is a platform for **static sites and frontend frameworks**, built to integrate with your headless content, commerce, or database.
We provide a **frictionless developer experience** to take care of the hard things: deploy instantly, scale automatically, and serve personalized content around the globe.
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). 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).

View File

@@ -2,19 +2,19 @@
* Get example list from extracted folder * Get example list from extracted folder
*/ */
import { join } from 'path';
import { lstatSync, existsSync, readdirSync } from 'fs'; import { lstatSync, existsSync, readdirSync } from 'fs';
const exists = (path: string) => existsSync(path); const exists = (path: string) => existsSync(path);
const isDotFile = (name: string) => name.startsWith('.'); const isDotFile = (name: string) => name.startsWith('.');
const isDirectory = (path: string) => lstatSync(path).isDirectory(); const isDirectory = (path: string) => lstatSync(path).isDirectory();
export function summary(source: string) { export function summary(source: string): string[] {
if (!exists(source) || !isDirectory(source)) { if (!exists(source) || !isDirectory(source)) {
return []; return [];
} }
return readdirSync(source) return readdirSync(source, { withFileTypes: true })
.filter(name => !isDotFile(name)) .filter(d => !isDotFile(d.name))
.filter(name => isDirectory(join(source, name))); .filter(d => d.isDirectory())
.map(d => d.name);
} }

View File

@@ -5,7 +5,7 @@
"dependencies": { "dependencies": {
"react": "^16.6.1", "react": "^16.6.1",
"react-dom": "^16.6.1", "react-dom": "^16.6.1",
"react-scripts": "2.1.1" "react-scripts": "^4.0.3"
}, },
"scripts": { "scripts": {
"dev": "BROWSER=none react-scripts start", "dev": "BROWSER=none react-scripts start",

View File

@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}

View File

@@ -29,6 +29,6 @@ You can check out [the Next.js GitHub repository](https://github.com/vercel/next
## Deploy on Vercel ## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/import?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

View File

@@ -0,0 +1,3 @@
module.exports = {
reactStrictMode: true,
}

View File

@@ -5,11 +5,16 @@
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "next build",
"start": "next start" "start": "next start",
"lint": "next lint"
}, },
"dependencies": { "dependencies": {
"next": "10.x", "next": "11.1.0",
"react": "17.x", "react": "17.0.2",
"react-dom": "17.x" "react-dom": "17.0.2"
},
"devDependencies": {
"eslint": "7.32.0",
"eslint-config-next": "11.1.0"
} }
} }

View File

@@ -1,6 +1,5 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
export default (req, res) => { export default function handler(req, res) {
res.statusCode = 200 res.status(200).json({ name: 'John Doe' })
res.json({ name: 'John Doe' })
} }

View File

@@ -1,4 +1,5 @@
import Head from 'next/head' import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css' import styles from '../styles/Home.module.css'
export default function Home() { export default function Home() {
@@ -6,6 +7,7 @@ export default function Home() {
<div className={styles.container}> <div className={styles.container}>
<Head> <Head>
<title>Create Next App</title> <title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
</Head> </Head>
@@ -21,12 +23,12 @@ export default function Home() {
<div className={styles.grid}> <div className={styles.grid}>
<a href="https://nextjs.org/docs" className={styles.card}> <a href="https://nextjs.org/docs" className={styles.card}>
<h3>Documentation &rarr;</h3> <h2>Documentation &rarr;</h2>
<p>Find in-depth information about Next.js features and API.</p> <p>Find in-depth information about Next.js features and API.</p>
</a> </a>
<a href="https://nextjs.org/learn" className={styles.card}> <a href="https://nextjs.org/learn" className={styles.card}>
<h3>Learn &rarr;</h3> <h2>Learn &rarr;</h2>
<p>Learn about Next.js in an interactive course with quizzes!</p> <p>Learn about Next.js in an interactive course with quizzes!</p>
</a> </a>
@@ -34,15 +36,15 @@ export default function Home() {
href="https://github.com/vercel/next.js/tree/master/examples" href="https://github.com/vercel/next.js/tree/master/examples"
className={styles.card} className={styles.card}
> >
<h3>Examples &rarr;</h3> <h2>Examples &rarr;</h2>
<p>Discover and deploy boilerplate example Next.js projects.</p> <p>Discover and deploy boilerplate example Next.js projects.</p>
</a> </a>
<a <a
href="https://vercel.com/import?filter=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app" href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className={styles.card} className={styles.card}
> >
<h3>Deploy &rarr;</h3> <h2>Deploy &rarr;</h2>
<p> <p>
Instantly deploy your Next.js site to a public URL with Vercel. Instantly deploy your Next.js site to a public URL with Vercel.
</p> </p>
@@ -57,7 +59,9 @@ export default function Home() {
rel="noopener noreferrer" rel="noopener noreferrer"
> >
Powered by{' '} Powered by{' '}
<img src="/vercel.svg" alt="Vercel Logo" className={styles.logo} /> <span className={styles.logo}>
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
</span>
</a> </a>
</footer> </footer>
</div> </div>

View File

@@ -5,6 +5,7 @@
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
height: 100vh;
} }
.main { .main {
@@ -25,14 +26,11 @@
align-items: center; align-items: center;
} }
.footer img {
margin-left: 0.5rem;
}
.footer a { .footer a {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
flex-grow: 1;
} }
.title a { .title a {
@@ -82,7 +80,6 @@
.card { .card {
margin: 1rem; margin: 1rem;
flex-basis: 45%;
padding: 1.5rem; padding: 1.5rem;
text-align: left; text-align: left;
color: inherit; color: inherit;
@@ -90,6 +87,7 @@
border: 1px solid #eaeaea; border: 1px solid #eaeaea;
border-radius: 10px; border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease; transition: color 0.15s ease, border-color 0.15s ease;
width: 45%;
} }
.card:hover, .card:hover,
@@ -99,7 +97,7 @@
border-color: #0070f3; border-color: #0070f3;
} }
.card h3 { .card h2 {
margin: 0 0 1rem 0; margin: 0 0 1rem 0;
font-size: 1.5rem; font-size: 1.5rem;
} }
@@ -112,6 +110,7 @@
.logo { .logo {
height: 1em; height: 1em;
margin-left: 0.5rem;
} }
@media (max-width: 600px) { @media (max-width: 600px) {

File diff suppressed because it is too large Load Diff

View File

@@ -25,17 +25,19 @@
"eslint-config-prettier": "8.3.0", "eslint-config-prettier": "8.3.0",
"eslint-plugin-jest": "24.3.6", "eslint-plugin-jest": "24.3.6",
"husky": "6.0.0", "husky": "6.0.0",
"jest": "27.0.6",
"json5": "2.1.1", "json5": "2.1.1",
"lint-staged": "9.2.5", "lint-staged": "9.2.5",
"node-fetch": "2.6.1", "node-fetch": "2.6.1",
"npm-package-arg": "6.1.0", "npm-package-arg": "6.1.0",
"prettier": "2.3.1" "prettier": "2.3.1",
"ts-jest": "27.0.4"
}, },
"scripts": { "scripts": {
"lerna": "lerna", "lerna": "lerna",
"bootstrap": "lerna bootstrap", "bootstrap": "lerna bootstrap",
"publish-stable": "echo 'Run `yarn changelog` for instructions'", "publish-stable": "echo 'Run `yarn changelog` for instructions'",
"publish-canary": "git checkout main && git pull && lerna version prerelease --preid canary --message 'Publish Canary' --exact", "publish-canary": "git checkout main && git pull && lerna version prerelease --preid canary --message \"Publish Canary\" --exact",
"publish-from-github": "./utils/publish.sh", "publish-from-github": "./utils/publish.sh",
"changelog": "node utils/changelog.js", "changelog": "node utils/changelog.js",
"build": "node utils/run.js build all", "build": "node utils/run.js build all",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/build-utils", "name": "@vercel/build-utils",
"version": "2.12.0", "version": "2.12.3-canary.0",
"license": "MIT", "license": "MIT",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.js", "types": "./dist/index.d.js",
@@ -30,7 +30,7 @@
"@types/node-fetch": "^2.1.6", "@types/node-fetch": "^2.1.6",
"@types/semver": "6.0.0", "@types/semver": "6.0.0",
"@types/yazl": "^2.4.1", "@types/yazl": "^2.4.1",
"@vercel/frameworks": "0.5.0", "@vercel/frameworks": "0.5.1-canary.1",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"aggregate-error": "3.0.1", "aggregate-error": "3.0.1",
"async-retry": "1.2.3", "async-retry": "1.2.3",
@@ -41,13 +41,11 @@
"fs-extra": "7.0.0", "fs-extra": "7.0.0",
"glob": "7.1.3", "glob": "7.1.3",
"into-stream": "5.0.0", "into-stream": "5.0.0",
"jest": "27.0.6",
"js-yaml": "3.13.1", "js-yaml": "3.13.1",
"minimatch": "3.0.4", "minimatch": "3.0.4",
"multistream": "2.1.1", "multistream": "2.1.1",
"node-fetch": "2.6.1", "node-fetch": "2.6.1",
"semver": "6.1.1", "semver": "6.1.1",
"ts-jest": "27.0.4",
"typescript": "4.3.4", "typescript": "4.3.4",
"yazl": "2.4.3" "yazl": "2.4.3"
} }

View File

@@ -244,10 +244,13 @@ export async function scanParentDirs(
const packageJsonPath = path.join(currentDestPath, 'package.json'); const packageJsonPath = path.join(currentDestPath, 'package.json');
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
if (await fs.pathExists(packageJsonPath)) { if (await fs.pathExists(packageJsonPath)) {
// eslint-disable-next-line no-await-in-loop // Only read the contents of the *first* `package.json` file found,
if (readPackageJson) { // since that's the one related to this installation.
if (readPackageJson && !packageJson) {
// eslint-disable-next-line no-await-in-loop
packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')); packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
} }
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
const [packageLockJson, hasYarnLock] = await Promise.all([ const [packageLockJson, hasYarnLock] = await Promise.all([
fs fs
@@ -256,9 +259,8 @@ export async function scanParentDirs(
// If the file doesn't exist, fail gracefully otherwise error // If the file doesn't exist, fail gracefully otherwise error
if (error.code === 'ENOENT') { if (error.code === 'ENOENT') {
return null; return null;
} else {
throw error;
} }
throw error;
}), }),
fs.pathExists(path.join(currentDestPath, 'yarn.lock')), fs.pathExists(path.join(currentDestPath, 'yarn.lock')),
]); ]);
@@ -267,7 +269,13 @@ export async function scanParentDirs(
cliType = 'npm'; cliType = 'npm';
lockfileVersion = packageLockJson.lockfileVersion; lockfileVersion = packageLockJson.lockfileVersion;
} }
break;
// Only stop iterating if a lockfile was found, because it's possible
// that the lockfile is in a higher path than where the `package.json`
// file was found.
if (packageLockJson || hasYarnLock) {
break;
}
} }
const newDestPath = path.dirname(currentDestPath); const newDestPath = path.dirname(currentDestPath);
@@ -305,7 +313,8 @@ export async function runNpmInstall(
destPath: string, destPath: string,
args: string[] = [], args: string[] = [],
spawnOpts?: SpawnOptions, spawnOpts?: SpawnOptions,
meta?: Meta meta?: Meta,
nodeVersion?: NodeVersion
) { ) {
if (meta?.isDev) { if (meta?.isDev) {
debug('Skipping dependency installation because dev mode is enabled'); debug('Skipping dependency installation because dev mode is enabled');
@@ -329,7 +338,12 @@ export async function runNpmInstall(
.filter(a => a !== '--prefer-offline') .filter(a => a !== '--prefer-offline')
.concat(['install', '--no-audit', '--unsafe-perm']); .concat(['install', '--no-audit', '--unsafe-perm']);
if (typeof lockfileVersion === 'number' && lockfileVersion >= 2) { // 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` // Ensure that npm 7 is at the beginning of the `$PATH`
env.PATH = `/node16/bin-npm7:${env.PATH}`; env.PATH = `/node16/bin-npm7:${env.PATH}`;
console.log('Detected `package-lock.json` generated by npm 7...'); console.log('Detected `package-lock.json` generated by npm 7...');

View File

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

View File

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

View File

@@ -0,0 +1,782 @@
{
"name": "21-npm-workspaces",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"version": "1.0.0",
"workspaces": [
"a",
"b"
]
},
"a": {
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"debug": "^4.3.2"
}
},
"b": {
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"cowsay": "^1.5.0"
}
},
"node_modules/a": {
"resolved": "a",
"link": true
},
"node_modules/ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"engines": {
"node": ">=4"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/b": {
"resolved": "b",
"link": true
},
"node_modules/camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"engines": {
"node": ">=6"
}
},
"node_modules/cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
}
},
"node_modules/cliui/node_modules/ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"engines": {
"node": ">=8"
}
},
"node_modules/cliui/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"engines": {
"node": ">=8"
}
},
"node_modules/cliui/node_modules/string-width": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/cliui/node_modules/strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dependencies": {
"ansi-regex": "^5.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/cowsay": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/cowsay/-/cowsay-1.5.0.tgz",
"integrity": "sha512-8Ipzr54Z8zROr/62C8f0PdhQcDusS05gKTS87xxdji8VbWefWly0k8BwGK7+VqamOrkv3eGsCkPtvlHzrhWsCA==",
"dependencies": {
"get-stdin": "8.0.0",
"string-width": "~2.1.1",
"strip-final-newline": "2.0.0",
"yargs": "15.4.1"
},
"bin": {
"cowsay": "cli.js",
"cowthink": "cli.js"
},
"engines": {
"node": ">= 4"
}
},
"node_modules/debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-stdin": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz",
"integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"engines": {
"node": ">=4"
}
},
"node_modules/locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dependencies": {
"p-locate": "^4.1.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dependencies": {
"p-try": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dependencies": {
"p-limit": "^2.2.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"engines": {
"node": ">=6"
}
},
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"engines": {
"node": ">=8"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
},
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"node_modules/string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dependencies": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dependencies": {
"ansi-regex": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/strip-final-newline": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
"engines": {
"node": ">=6"
}
},
"node_modules/which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"node_modules/wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi/node_modules/ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi/node_modules/string-width": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi/node_modules/strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dependencies": {
"ansi-regex": "^5.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
},
"node_modules/yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"dependencies": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dependencies": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/yargs/node_modules/ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"engines": {
"node": ">=8"
}
},
"node_modules/yargs/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"engines": {
"node": ">=8"
}
},
"node_modules/yargs/node_modules/string-width": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yargs/node_modules/strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dependencies": {
"ansi-regex": "^5.0.0"
},
"engines": {
"node": ">=8"
}
}
},
"dependencies": {
"a": {
"version": "file:a",
"requires": {
"debug": "^4.3.2"
}
},
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"color-convert": "^2.0.1"
}
},
"b": {
"version": "file:b",
"requires": {
"cowsay": "^1.5.0"
}
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
},
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"string-width": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"requires": {
"ansi-regex": "^5.0.0"
}
}
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"cowsay": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/cowsay/-/cowsay-1.5.0.tgz",
"integrity": "sha512-8Ipzr54Z8zROr/62C8f0PdhQcDusS05gKTS87xxdji8VbWefWly0k8BwGK7+VqamOrkv3eGsCkPtvlHzrhWsCA==",
"requires": {
"get-stdin": "8.0.0",
"string-width": "~2.1.1",
"strip-final-newline": "2.0.0",
"yargs": "15.4.1"
}
},
"debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"requires": {
"ms": "2.1.2"
}
},
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"requires": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
}
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"get-stdin": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz",
"integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg=="
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"requires": {
"p-locate": "^4.1.0"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"requires": {
"p-limit": "^2.2.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
},
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
},
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"requires": {
"ansi-regex": "^3.0.0"
}
},
"strip-final-newline": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="
},
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"string-width": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"requires": {
"ansi-regex": "^5.0.0"
}
}
}
},
"y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
},
"yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"requires": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"string-width": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"requires": {
"ansi-regex": "^5.0.0"
}
}
}
},
"yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
}

View File

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

View File

@@ -30,6 +30,7 @@ const skipFixtures: string[] = [
'06-zero-config-hugo', '06-zero-config-hugo',
'07-zero-config-jekyll', '07-zero-config-jekyll',
'08-zero-config-middleman', '08-zero-config-middleman',
'21-npm-workspaces',
]; ];
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax

View File

@@ -298,23 +298,29 @@ it(
); );
it('should return lockfileVersion 2 with npm7', async () => { it('should return lockfileVersion 2 with npm7', async () => {
const packageLockJsonPath = path.join(__dirname, 'fixtures', '20-npm-7'); const fixture = path.join(__dirname, 'fixtures', '20-npm-7');
const result = await scanParentDirs(packageLockJsonPath); const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('npm');
expect(result.lockfileVersion).toEqual(2); expect(result.lockfileVersion).toEqual(2);
}); });
it('should not return lockfileVersion with yarn', async () => { it('should not return lockfileVersion with yarn', async () => {
const packageLockJsonPath = path.join(__dirname, 'fixtures', '19-yarn-v2'); const fixture = path.join(__dirname, 'fixtures', '19-yarn-v2');
const result = await scanParentDirs(packageLockJsonPath); const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('yarn');
expect(result.lockfileVersion).toEqual(undefined); expect(result.lockfileVersion).toEqual(undefined);
}); });
it('should return lockfileVersion 1 with older versions of npm', async () => { it('should return lockfileVersion 1 with older versions of npm', async () => {
const packageLockJsonPath = path.join( const fixture = path.join(__dirname, 'fixtures', '08-yarn-npm/with-npm');
__dirname, const result = await scanParentDirs(fixture);
'fixtures', expect(result.cliType).toEqual('npm');
'08-yarn-npm/with-npm'
);
const result = await scanParentDirs(packageLockJsonPath);
expect(result.lockfileVersion).toEqual(1); expect(result.lockfileVersion).toEqual(1);
}); });
it('should detect npm Workspaces', async () => {
const fixture = path.join(__dirname, 'fixtures', '21-npm-workspaces/a');
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('npm');
expect(result.lockfileVersion).toEqual(2);
});

View File

@@ -10,9 +10,13 @@
## Usage ## Usage
Vercel is the optimal workflow for frontend teams. All-in-one: Static and Jamstack deployment, Serverless Functions, and Global CDN. Vercel is a platform for **static sites and frontend frameworks**, built to integrate with your headless content, commerce, or database.
To install the latest version of Vercel CLI, visit [vercel.com/download](https://vercel.com/download) or run this command: We provide a **frictionless developer experience** to take care of the hard things: deploy instantly, scale automatically, and serve personalized content around the globe.
We make it easy for frontend teams to **develop, preview, and ship** delightful user experiences, where performance is the default.
To install the latest version of Vercel CLI, run this command:
```bash ```bash
npm i -g vercel npm i -g vercel
@@ -26,6 +30,8 @@ cd <PROJECT> # Change directory to the new project
vercel # Deploy to the cloud vercel # Deploy to the cloud
``` ```
Finally, [connect your Git repository to Vercel](https://vercel.com/docs/git) and deploy with `git push`.
## Documentation ## Documentation
For details on how to use Vercel CLI, check out our [documentation](https://vercel.com/docs). For details on how to use Vercel CLI, check out our [documentation](https://vercel.com/docs).

View File

@@ -1,6 +1,6 @@
{ {
"name": "vercel", "name": "vercel",
"version": "23.1.0", "version": "23.1.3-canary.0",
"preferGlobal": true, "preferGlobal": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "The command-line interface for Vercel", "description": "The command-line interface for Vercel",
@@ -61,11 +61,11 @@
"node": ">= 12" "node": ">= 12"
}, },
"dependencies": { "dependencies": {
"@vercel/build-utils": "2.12.0", "@vercel/build-utils": "2.12.3-canary.0",
"@vercel/go": "1.2.3", "@vercel/go": "1.2.4-canary.0",
"@vercel/node": "1.12.0", "@vercel/node": "1.12.2-canary.0",
"@vercel/python": "2.0.5", "@vercel/python": "2.0.6-canary.0",
"@vercel/ruby": "1.2.7", "@vercel/ruby": "1.2.8-canary.0",
"update-notifier": "4.1.0" "update-notifier": "4.1.0"
}, },
"devDependencies": { "devDependencies": {
@@ -97,10 +97,11 @@
"@types/semver": "6.0.1", "@types/semver": "6.0.1",
"@types/tar-fs": "1.16.1", "@types/tar-fs": "1.16.1",
"@types/text-table": "0.2.0", "@types/text-table": "0.2.0",
"@types/title": "3.4.1",
"@types/universal-analytics": "0.4.2", "@types/universal-analytics": "0.4.2",
"@types/which": "1.3.2", "@types/which": "1.3.2",
"@types/write-json-file": "2.2.1", "@types/write-json-file": "2.2.1",
"@vercel/frameworks": "0.5.0", "@vercel/frameworks": "0.5.1-canary.1",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"@zeit/fun": "0.11.2", "@zeit/fun": "0.11.2",
"@zeit/source-map-support": "0.6.2", "@zeit/source-map-support": "0.6.2",

View File

@@ -6,12 +6,11 @@ import cardBrands from '../../util/billing/card-brands';
import success from '../../util/output/success'; import success from '../../util/output/success';
import wait from '../../util/output/wait'; import wait from '../../util/output/wait';
import chars from '../../util/output/chars'; import chars from '../../util/output/chars';
import rightPad from '../../util/output/right-pad';
import error from '../../util/output/error'; import error from '../../util/output/error';
const expDateMiddleware = data => data; const expDateMiddleware = data => data;
export default async function({ creditCards, clear = false, contextName }) { export default async function ({ creditCards, clear = false, contextName }) {
const state = { const state = {
error: undefined, error: undefined,
cardGroupLabel: `> ${chalk.bold( cardGroupLabel: `> ${chalk.bold(
@@ -19,13 +18,13 @@ export default async function({ creditCards, clear = false, contextName }) {
)}`, )}`,
name: { name: {
label: rightPad('Full Name', 12), label: 'Full Name'.padEnd(12),
placeholder: 'John Appleseed', placeholder: 'John Appleseed',
validateValue: data => data.trim().length > 0, validateValue: data => data.trim().length > 0,
}, },
cardNumber: { cardNumber: {
label: rightPad('Number', 12), label: 'Number'.padEnd(12),
mask: 'cc', mask: 'cc',
placeholder: '#### #### #### ####', placeholder: '#### #### #### ####',
validateKeypress: (data, value) => /\d/.test(data) && value.length < 19, validateKeypress: (data, value) => /\d/.test(data) && value.length < 19,
@@ -40,7 +39,7 @@ export default async function({ creditCards, clear = false, contextName }) {
}, },
ccv: { ccv: {
label: rightPad('CCV', 12), label: 'CCV'.padEnd(12),
mask: 'ccv', mask: 'ccv',
placeholder: '###', placeholder: '###',
validateValue: data => { validateValue: data => {
@@ -50,7 +49,7 @@ export default async function({ creditCards, clear = false, contextName }) {
}, },
expDate: { expDate: {
label: rightPad('Exp. Date', 12), label: 'Exp. Date'.padEnd(12),
mask: 'expDate', mask: 'expDate',
placeholder: 'mm / yyyy', placeholder: 'mm / yyyy',
middleware: expDateMiddleware, middleware: expDateMiddleware,
@@ -147,9 +146,9 @@ export default async function({ creditCards, clear = false, contextName }) {
console.log( console.log(
success( success(
`${state.cardNumber.brand || `${state.cardNumber.brand || state.cardNumber.card.brand} ending in ${
state.cardNumber.card.brand} ending in ${res.last4 || res.last4 || res.card.last4
res.card.last4} was added to ${chalk.bold(contextName)}` } was added to ${chalk.bold(contextName)}`
) )
); );
} catch (err) { } catch (err) {

View File

@@ -3,7 +3,7 @@ import ms from 'ms';
import plural from 'pluralize'; import plural from 'pluralize';
import { error } from '../../util/error'; import { error } from '../../util/error';
import NowCreditCards from '../../util/credit-cards'; import NowCreditCards from '../../util/credit-cards';
import indent from '../../util/indent'; import indent from '../../util/output/indent';
import listInput from '../../util/input/list'; import listInput from '../../util/input/list';
import success from '../../util/output/success'; import success from '../../util/output/success';
import promptBool from '../../util/input/prompt-bool'; import promptBool from '../../util/input/prompt-bool';

View File

@@ -1,17 +1,72 @@
import ms from 'ms';
import fs from 'fs-extra'; import fs from 'fs-extra';
import { resolve, basename } from 'path'; import bytes from 'bytes';
import { VercelConfig, fileNameSymbol } from '@vercel/client'; import chalk from 'chalk';
import { join, resolve, basename } from 'path';
import { Dictionary, fileNameSymbol, VercelConfig } from '@vercel/client';
import code from '../../util/output/code'; import code from '../../util/output/code';
import highlight from '../../util/output/highlight'; import highlight from '../../util/output/highlight';
import { readLocalConfig } from '../../util/config/files'; import { readLocalConfig } from '../../util/config/files';
import getArgs from '../../util/get-args'; import getArgs from '../../util/get-args';
import { handleError } from '../../util/error'; import { handleError } from '../../util/error';
import { help } from './args';
import deploy from './latest';
import Client from '../../util/client'; import Client from '../../util/client';
import { write as copy } from 'clipboardy';
import { getPrettyError } from '@vercel/build-utils';
import toHumanPath from '../../util/humanize-path';
import Now from '../../util';
import stamp from '../../util/output/stamp';
import createDeploy from '../../util/deploy/create-deploy';
import getDeploymentByIdOrHost from '../../util/deploy/get-deployment-by-id-or-host';
import parseMeta from '../../util/parse-meta';
import linkStyle from '../../util/output/link';
import param from '../../util/output/param';
import {
BuildsRateLimited,
DeploymentNotFound,
DeploymentPermissionDenied,
InvalidDeploymentId,
DomainNotFound,
DomainNotVerified,
DomainPermissionDenied,
DomainVerificationFailed,
InvalidDomain,
TooManyRequests,
UserAborted,
DeploymentsRateLimited,
AliasDomainConfigured,
MissingBuildScript,
ConflictingFilePath,
ConflictingPathSegment,
BuildError,
NotDomainOwner,
} from '../../util/errors-ts';
import { SchemaValidationFailed } from '../../util/errors';
import purchaseDomainIfAvailable from '../../util/domains/purchase-domain-if-available';
import confirm from '../../util/input/confirm';
import editProjectSettings from '../../util/input/edit-project-settings';
import {
getLinkedProject,
linkFolderToProject,
} from '../../util/projects/link';
import getProjectName from '../../util/get-project-name';
import selectOrg from '../../util/input/select-org';
import inputProject from '../../util/input/input-project';
import { prependEmoji, emoji } from '../../util/emoji';
import { inputRootDirectory } from '../../util/input/input-root-directory';
import validatePaths, {
validateRootDirectory,
} from '../../util/validate-paths';
import { getCommandName } from '../../util/pkg-name';
import { getPreferredPreviewURL } from '../../util/deploy/get-preferred-preview-url';
import { Output } from '../../util/output';
import { help } from './args';
export default async (client: Client) => { export default async (client: Client) => {
const { output } = client; const {
apiUrl,
output,
authConfig: { token },
} = client;
let argv = null; let argv = null;
@@ -105,5 +160,739 @@ export default async (client: Client) => {
} }
} }
return deploy(client, paths, localConfig, argv); const { log, debug, error, warn } = output;
const debugEnabled = argv['--debug'];
const { isTTY } = process.stdout;
const quiet = !isTTY;
// check paths
const pathValidation = await validatePaths(output, paths);
if (!pathValidation.valid) {
return pathValidation.exitCode;
}
const { isFile, path } = pathValidation;
const autoConfirm = argv['--confirm'] || isFile;
// deprecate --name
if (argv['--name']) {
output.print(
`${prependEmoji(
`The ${param(
'--name'
)} option is deprecated (https://vercel.link/name-flag)`,
emoji('warning')
)}\n`
);
}
// retrieve `project` and `org` from .vercel
const link = await getLinkedProject(client, path);
if (link.status === 'error') {
return link.exitCode;
}
let { org, project, status } = link;
let newProjectName = null;
let rootDirectory = project ? project.rootDirectory : null;
let sourceFilesOutsideRootDirectory = true;
if (status === 'not_linked') {
const shouldStartSetup =
autoConfirm ||
(await confirm(
`Set up and deploy ${chalk.cyan(`${toHumanPath(path)}`)}?`,
true
));
if (!shouldStartSetup) {
output.print(`Aborted. Project not set up.\n`);
return 0;
}
try {
org = await selectOrg(
client,
'Which scope do you want to deploy to?',
autoConfirm
);
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
return 1;
}
throw err;
}
// We use `localConfig` here to read the name
// even though the `vercel.json` file can change
// afterwards, this is fine since the property
// will be deprecated and can be replaced with
// user input.
const detectedProjectName = getProjectName({
argv,
nowConfig: localConfig || {},
isFile,
paths,
});
const projectOrNewProjectName = await inputProject(
client,
org,
detectedProjectName,
autoConfirm
);
if (typeof projectOrNewProjectName === 'string') {
newProjectName = projectOrNewProjectName;
rootDirectory = await inputRootDirectory(path, output, autoConfirm);
} else {
project = projectOrNewProjectName;
rootDirectory = project.rootDirectory;
sourceFilesOutsideRootDirectory = project.sourceFilesOutsideRootDirectory;
// we can already link the project
await linkFolderToProject(
output,
path,
{
projectId: project.id,
orgId: org.id,
},
project.name,
org.slug
);
status = 'linked';
}
}
// At this point `org` should be populated
if (!org) {
throw new Error(`"org" is not defined`);
}
// Set the `contextName` and `currentTeam` as specified by the
// Project Settings, so that API calls happen with the proper scope
const contextName = org.slug;
client.config.currentTeam = org.type === 'team' ? org.id : undefined;
// if we have `sourceFilesOutsideRootDirectory` set to `true`, we use the current path
// and upload the entire directory.
const sourcePath =
rootDirectory && !sourceFilesOutsideRootDirectory
? join(path, rootDirectory)
: path;
if (
rootDirectory &&
(await validateRootDirectory(
output,
path,
sourcePath,
project
? `To change your Project Settings, go to https://vercel.com/${org?.slug}/${project.name}/settings`
: ''
)) === false
) {
return 1;
}
// If Root Directory is used we'll try to read the config
// from there instead and use it if it exists.
if (rootDirectory) {
const rootDirectoryConfig = readLocalConfig(join(path, rootDirectory));
if (rootDirectoryConfig) {
debug(`Read local config from root directory (${rootDirectory})`);
localConfig = rootDirectoryConfig;
} else if (localConfig) {
output.print(
`${prependEmoji(
`The ${highlight(
localConfig[fileNameSymbol]!
)} file should be inside of the provided root directory.`,
emoji('warning')
)}\n`
);
}
}
localConfig = localConfig || {};
if (localConfig.name) {
output.print(
`${prependEmoji(
`The ${code('name')} property in ${highlight(
localConfig[fileNameSymbol]!
)} is deprecated (https://vercel.link/name-prop)`,
emoji('warning')
)}\n`
);
}
// build `env`
const isObject = (item: any) =>
Object.prototype.toString.call(item) === '[object Object]';
// This validation needs to happen on the client side because
// the data is merged with other data before it is passed to the API (which
// also does schema validation).
if (typeof localConfig.env !== 'undefined' && !isObject(localConfig.env)) {
error(
`The ${code('env')} property in ${highlight(
localConfig[fileNameSymbol]!
)} needs to be an object`
);
return 1;
}
if (typeof localConfig.build !== 'undefined') {
if (!isObject(localConfig.build)) {
error(
`The ${code('build')} property in ${highlight(
localConfig[fileNameSymbol]!
)} needs to be an object`
);
return 1;
}
if (
typeof localConfig.build.env !== 'undefined' &&
!isObject(localConfig.build.env)
) {
error(
`The ${code('build.env')} property in ${highlight(
localConfig[fileNameSymbol]!
)} needs to be an object`
);
return 1;
}
}
// build `meta`
const meta = Object.assign(
{},
parseMeta(localConfig.meta),
parseMeta(argv['--meta'])
);
// Merge dotenv config, `env` from vercel.json, and `--env` / `-e` arguments
const deploymentEnv = Object.assign(
{},
parseEnv(localConfig.env),
parseEnv(argv['--env'])
);
// Merge build env out of `build.env` from vercel.json, and `--build-env` args
const deploymentBuildEnv = Object.assign(
{},
parseEnv(localConfig.build && localConfig.build.env),
parseEnv(argv['--build-env'])
);
// If there's any undefined values, then inherit them from this process
try {
await addProcessEnv(log, deploymentEnv);
await addProcessEnv(log, deploymentBuildEnv);
} catch (err) {
error(err.message);
return 1;
}
// build `regions`
const regionFlag = (argv['--regions'] || '')
.split(',')
.map((s: string) => s.trim())
.filter(Boolean);
const regions = regionFlag.length > 0 ? regionFlag : localConfig.regions;
// build `target`
let target;
if (argv['--target']) {
const deprecatedTarget = argv['--target'];
if (!['staging', 'production'].includes(deprecatedTarget)) {
error(
`The specified ${param('--target')} ${code(
deprecatedTarget
)} is not valid`
);
return 1;
}
if (deprecatedTarget === 'production') {
warn(
'We recommend using the much shorter `--prod` option instead of `--target production` (deprecated)'
);
}
output.debug(`Setting target to ${deprecatedTarget}`);
target = deprecatedTarget;
} else if (argv['--prod']) {
output.debug('Setting target to production');
target = 'production';
}
const currentTeam = org?.type === 'team' ? org.id : undefined;
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
let deployStamp = stamp();
let deployment = null;
try {
const createArgs: any = {
name: project ? project.name : newProjectName,
env: deploymentEnv,
build: { env: deploymentBuildEnv },
forceNew: argv['--force'],
withCache: argv['--with-cache'],
quiet,
wantsPublic: argv['--public'] || localConfig.public,
isFile,
type: null,
nowConfig: localConfig,
regions,
meta,
deployStamp,
target,
skipAutoDetectionConfirmation: autoConfirm,
};
if (!localConfig.builds || localConfig.builds.length === 0) {
// Only add projectSettings for zero config deployments
createArgs.projectSettings = { sourceFilesOutsideRootDirectory };
}
deployment = await createDeploy(
client,
now,
contextName,
[sourcePath],
createArgs,
org,
!project && !isFile,
path
);
if (deployment.code === 'missing_project_settings') {
let { projectSettings, framework } = deployment;
if (rootDirectory) {
projectSettings.rootDirectory = rootDirectory;
}
if (typeof sourceFilesOutsideRootDirectory !== 'undefined') {
projectSettings.sourceFilesOutsideRootDirectory =
sourceFilesOutsideRootDirectory;
}
const settings = await editProjectSettings(
output,
projectSettings,
framework
);
// deploy again, but send projectSettings this time
createArgs.projectSettings = settings;
deployStamp = stamp();
createArgs.deployStamp = deployStamp;
deployment = await createDeploy(
client,
now,
contextName,
[sourcePath],
createArgs,
org,
false,
path
);
}
if (deployment instanceof NotDomainOwner) {
output.error(deployment.message);
return 1;
}
if (deployment instanceof Error) {
output.error(
deployment.message ||
'An unexpected error occurred while deploying your project',
undefined,
'https://vercel.link/help',
'Contact Support'
);
return 1;
}
if (deployment.readyState === 'CANCELED') {
output.print('The deployment has been canceled.\n');
return 1;
}
const deploymentResponse = await getDeploymentByIdOrHost(
client,
contextName,
deployment.id,
'v10'
);
if (
deploymentResponse instanceof DeploymentNotFound ||
deploymentResponse instanceof DeploymentPermissionDenied ||
deploymentResponse instanceof InvalidDeploymentId
) {
output.error(deploymentResponse.message);
return 1;
}
if (deployment === null) {
error('Uploading failed. Please try again.');
return 1;
}
} catch (err) {
debug(`Error: ${err}\n${err.stack}`);
if (err instanceof NotDomainOwner) {
output.error(err.message);
return 1;
}
if (err instanceof DomainNotFound && err.meta && err.meta.domain) {
output.debug(
`The domain ${err.meta.domain} was not found, trying to purchase it`
);
const purchase = await purchaseDomainIfAvailable(
output,
client,
err.meta.domain,
contextName
);
if (purchase === true) {
output.success(`Successfully purchased the domain ${err.meta.domain}!`);
// We exit if the purchase is completed since
// the domain verification can take some time
return 0;
}
if (purchase === false || purchase instanceof UserAborted) {
handleCreateDeployError(output, deployment, localConfig);
return 1;
}
handleCreateDeployError(output, purchase, localConfig);
return 1;
}
if (
err instanceof DomainNotFound ||
err instanceof DomainNotVerified ||
err instanceof NotDomainOwner ||
err instanceof DomainPermissionDenied ||
err instanceof DomainVerificationFailed ||
err instanceof SchemaValidationFailed ||
err instanceof InvalidDomain ||
err instanceof DeploymentNotFound ||
err instanceof BuildsRateLimited ||
err instanceof DeploymentsRateLimited ||
err instanceof AliasDomainConfigured ||
err instanceof MissingBuildScript ||
err instanceof ConflictingFilePath ||
err instanceof ConflictingPathSegment
) {
handleCreateDeployError(output, err, localConfig);
return 1;
}
if (err instanceof BuildError) {
output.error(err.message || 'Build failed');
output.error(
`Check your logs at https://${now.url}/_logs or run ${getCommandName(
`logs ${now.url}`
)}`
);
return 1;
}
if (err.keyword === 'additionalProperties' && err.dataPath === '.scale') {
const { additionalProperty = '' } = err.params || {};
const message = `Invalid DC name for the scale option: ${additionalProperty}`;
error(message);
}
if (err.code === 'size_limit_exceeded') {
const { sizeLimit = 0 } = err;
const message = `File size limit exceeded (${bytes(sizeLimit)})`;
error(message);
return 1;
}
handleError(err);
return 1;
}
return printDeploymentStatus(
output,
client,
deployment,
deployStamp,
!argv['--no-clipboard'],
isFile
);
};
function handleCreateDeployError(
output: Output,
error: Error,
localConfig: VercelConfig
) {
if (error instanceof InvalidDomain) {
output.error(`The domain ${error.meta.domain} is not valid`);
return 1;
}
if (error instanceof DomainVerificationFailed) {
output.error(
`The domain used as a suffix ${chalk.underline(
error.meta.domain
)} is not verified and can't be used as custom suffix.`
);
return 1;
}
if (error instanceof DomainPermissionDenied) {
output.error(
`You don't have permissions to access the domain used as a suffix ${chalk.underline(
error.meta.domain
)}.`
);
return 1;
}
if (error instanceof SchemaValidationFailed) {
const niceError = getPrettyError(error.meta);
const fileName = localConfig[fileNameSymbol] || 'vercel.json';
niceError.message = `Invalid ${fileName} - ${niceError.message}`;
output.prettyError(niceError);
return 1;
}
if (error instanceof TooManyRequests) {
output.error(
`Too many requests detected for ${error.meta.api} API. Try again in ${ms(
error.meta.retryAfter * 1000,
{
long: true,
}
)}.`
);
return 1;
}
if (error instanceof DomainNotVerified) {
output.error(
`The domain used as an alias ${chalk.underline(
error.meta.domain
)} is not verified yet. Please verify it.`
);
return 1;
}
if (error instanceof BuildsRateLimited) {
output.error(error.message);
output.note(
`Run ${getCommandName('upgrade')} to increase your builds limit.`
);
return 1;
}
if (
error instanceof DeploymentNotFound ||
error instanceof NotDomainOwner ||
error instanceof DeploymentsRateLimited ||
error instanceof AliasDomainConfigured ||
error instanceof MissingBuildScript ||
error instanceof ConflictingFilePath ||
error instanceof ConflictingPathSegment
) {
output.error(error.message);
return 1;
}
return error;
}
const addProcessEnv = async (
log: (str: string) => void,
env: typeof process.env
) => {
let val;
for (const key of Object.keys(env)) {
if (typeof env[key] !== 'undefined') {
continue;
}
val = process.env[key];
if (typeof val === 'string') {
log(
`Reading ${chalk.bold(
`"${chalk.bold(key)}"`
)} from your env (as no value was specified)`
);
// Escape value if it begins with @
env[key] = val.replace(/^@/, '\\@');
} else {
throw new Error(
`No value specified for env ${chalk.bold(
`"${chalk.bold(key)}"`
)} and it was not found in your env.`
);
}
}
};
const printDeploymentStatus = async (
output: Output,
client: Client,
{
readyState,
alias: aliasList,
aliasError,
target,
indications,
url: deploymentUrl,
aliasWarning,
}: {
readyState: string;
alias: string[];
aliasError: Error;
target: string;
indications: any;
url: string;
aliasWarning?: {
code: string;
message: string;
link?: string;
action?: string;
};
},
deployStamp: () => string,
isClipboardEnabled: boolean,
isFile: boolean
) => {
indications = indications || [];
const isProdDeployment = target === 'production';
if (readyState !== 'READY') {
output.error(
`Your deployment failed. Please retry later. More: https://err.sh/vercel/deployment-error`
);
return 1;
}
if (aliasError) {
output.warn(
`Failed to assign aliases${
aliasError.message ? `: ${aliasError.message}` : ''
}`
);
} else {
// print preview/production url
let previewUrl: string;
let isWildcard: boolean;
if (!isFile && Array.isArray(aliasList) && aliasList.length > 0) {
const previewUrlInfo = await getPreferredPreviewURL(client, aliasList);
if (previewUrlInfo) {
isWildcard = previewUrlInfo.isWildcard;
previewUrl = previewUrlInfo.previewUrl;
} else {
isWildcard = false;
previewUrl = `https://${deploymentUrl}`;
}
} else {
// fallback to deployment url
isWildcard = false;
previewUrl = `https://${deploymentUrl}`;
}
// copy to clipboard
let isCopiedToClipboard = false;
if (isClipboardEnabled && !isWildcard) {
try {
await copy(previewUrl);
isCopiedToClipboard = true;
} catch (err) {
output.debug(`Error copyind to clipboard: ${err}`);
}
}
output.print(
prependEmoji(
`${isProdDeployment ? 'Production' : 'Preview'}: ${chalk.bold(
previewUrl
)}${
isCopiedToClipboard ? chalk.gray(` [copied to clipboard]`) : ''
} ${deployStamp()}`,
emoji('success')
) + `\n`
);
}
if (aliasWarning?.message) {
indications.push({
type: 'warning',
payload: aliasWarning.message,
link: aliasWarning.link,
action: aliasWarning.action,
});
}
const newline = '\n';
for (let indication of indications) {
const message =
prependEmoji(chalk.dim(indication.payload), emoji(indication.type)) +
newline;
let link = '';
if (indication.link)
link =
chalk.dim(
`${indication.action || 'Learn More'}: ${linkStyle(indication.link)}`
) + newline;
output.print(message + link);
}
};
// Converts `env` Arrays, Strings and Objects into env Objects.
const parseEnv = (env?: string[] | Dictionary<string>) => {
if (!env) {
return {};
}
if (typeof env === 'string') {
// a single `--env` arg comes in as a String
env = [env];
}
if (Array.isArray(env)) {
return env.reduce((o, e) => {
let key;
let value;
const equalsSign = e.indexOf('=');
if (equalsSign === -1) {
key = e;
} else {
key = e.substr(0, equalsSign);
value = e.substr(equalsSign + 1);
}
o[key] = value;
return o;
}, {} as Dictionary<string | undefined>);
}
// assume it's already an Object
return env;
}; };

View File

@@ -1,808 +0,0 @@
import ms from 'ms';
import bytes from 'bytes';
import { join } from 'path';
import { write as copy } from 'clipboardy';
import chalk from 'chalk';
import { Dictionary, fileNameSymbol, VercelConfig } from '@vercel/client';
import { getPrettyError } from '@vercel/build-utils';
import { handleError } from '../../util/error';
import toHumanPath from '../../util/humanize-path';
import Now from '../../util';
import stamp from '../../util/output/stamp';
import createDeploy from '../../util/deploy/create-deploy';
import getDeploymentByIdOrHost from '../../util/deploy/get-deployment-by-id-or-host';
import parseMeta from '../../util/parse-meta';
import code from '../../util/output/code';
import linkStyle from '../../util/output/link';
import param from '../../util/output/param';
import highlight from '../../util/output/highlight';
import {
BuildsRateLimited,
DeploymentNotFound,
DeploymentPermissionDenied,
InvalidDeploymentId,
DomainNotFound,
DomainNotVerified,
DomainPermissionDenied,
DomainVerificationFailed,
InvalidDomain,
TooManyRequests,
UserAborted,
DeploymentsRateLimited,
AliasDomainConfigured,
MissingBuildScript,
ConflictingFilePath,
ConflictingPathSegment,
BuildError,
NotDomainOwner,
} from '../../util/errors-ts';
import { SchemaValidationFailed } from '../../util/errors';
import purchaseDomainIfAvailable from '../../util/domains/purchase-domain-if-available';
import confirm from '../../util/input/confirm';
import editProjectSettings from '../../util/input/edit-project-settings';
import {
getLinkedProject,
linkFolderToProject,
} from '../../util/projects/link';
import getProjectName from '../../util/get-project-name';
import selectOrg from '../../util/input/select-org';
import inputProject from '../../util/input/input-project';
import { prependEmoji, emoji } from '../../util/emoji';
import { inputRootDirectory } from '../../util/input/input-root-directory';
import validatePaths, {
validateRootDirectory,
} from '../../util/validate-paths';
import { readLocalConfig } from '../../util/config/files';
import { getCommandName } from '../../util/pkg-name';
import { getPreferredPreviewURL } from '../../util/deploy/get-preferred-preview-url';
import { Output } from '../../util/output';
import Client from '../../util/client';
const addProcessEnv = async (
log: (str: string) => void,
env: typeof process.env
) => {
let val;
for (const key of Object.keys(env)) {
if (typeof env[key] !== 'undefined') {
continue;
}
val = process.env[key];
if (typeof val === 'string') {
log(
`Reading ${chalk.bold(
`"${chalk.bold(key)}"`
)} from your env (as no value was specified)`
);
// Escape value if it begins with @
env[key] = val.replace(/^@/, '\\@');
} else {
throw new Error(
`No value specified for env ${chalk.bold(
`"${chalk.bold(key)}"`
)} and it was not found in your env.`
);
}
}
};
const printDeploymentStatus = async (
output: Output,
client: Client,
{
readyState,
alias: aliasList,
aliasError,
target,
indications,
url: deploymentUrl,
aliasWarning,
}: {
readyState: string;
alias: string[];
aliasError: Error;
target: string;
indications: any;
url: string;
aliasWarning?: {
code: string;
message: string;
link?: string;
action?: string;
};
},
deployStamp: () => string,
isClipboardEnabled: boolean,
isFile: boolean
) => {
indications = indications || [];
const isProdDeployment = target === 'production';
if (readyState !== 'READY') {
output.error(
`Your deployment failed. Please retry later. More: https://err.sh/vercel/deployment-error`
);
return 1;
}
if (aliasError) {
output.warn(
`Failed to assign aliases${
aliasError.message ? `: ${aliasError.message}` : ''
}`
);
} else {
// print preview/production url
let previewUrl: string;
let isWildcard: boolean;
if (!isFile && Array.isArray(aliasList) && aliasList.length > 0) {
const previewUrlInfo = await getPreferredPreviewURL(client, aliasList);
if (previewUrlInfo) {
isWildcard = previewUrlInfo.isWildcard;
previewUrl = previewUrlInfo.previewUrl;
} else {
isWildcard = false;
previewUrl = `https://${deploymentUrl}`;
}
} else {
// fallback to deployment url
isWildcard = false;
previewUrl = `https://${deploymentUrl}`;
}
// copy to clipboard
let isCopiedToClipboard = false;
if (isClipboardEnabled && !isWildcard) {
try {
await copy(previewUrl);
isCopiedToClipboard = true;
} catch (err) {
output.debug(`Error copyind to clipboard: ${err}`);
}
}
output.print(
prependEmoji(
`${isProdDeployment ? 'Production' : 'Preview'}: ${chalk.bold(
previewUrl
)}${
isCopiedToClipboard ? chalk.gray(` [copied to clipboard]`) : ''
} ${deployStamp()}`,
emoji('success')
) + `\n`
);
}
if (aliasWarning?.message) {
indications.push({
type: 'warning',
payload: aliasWarning.message,
link: aliasWarning.link,
action: aliasWarning.action,
});
}
const newline = '\n';
for (let indication of indications) {
const message =
prependEmoji(chalk.dim(indication.payload), emoji(indication.type)) +
newline;
let link = '';
if (indication.link)
link =
chalk.dim(
`${indication.action || 'Learn More'}: ${linkStyle(indication.link)}`
) + newline;
output.print(message + link);
}
};
// Converts `env` Arrays, Strings and Objects into env Objects.
const parseEnv = (env?: string[] | Dictionary<string>) => {
if (!env) {
return {};
}
if (typeof env === 'string') {
// a single `--env` arg comes in as a String
env = [env];
}
if (Array.isArray(env)) {
return env.reduce((o, e) => {
let key;
let value;
const equalsSign = e.indexOf('=');
if (equalsSign === -1) {
key = e;
} else {
key = e.substr(0, equalsSign);
value = e.substr(equalsSign + 1);
}
o[key] = value;
return o;
}, {} as Dictionary<string | undefined>);
}
// assume it's already an Object
return env;
};
export default async function main(
client: Client,
paths: string[],
localConfig: VercelConfig | null,
argv: any
) {
const {
apiUrl,
output,
authConfig: { token },
} = client;
const { log, debug, error, warn } = output;
const debugEnabled = argv['--debug'];
const { isTTY } = process.stdout;
const quiet = !isTTY;
// check paths
const pathValidation = await validatePaths(output, paths);
if (!pathValidation.valid) {
return pathValidation.exitCode;
}
const { isFile, path } = pathValidation;
const autoConfirm = argv['--confirm'] || isFile;
// deprecate --name
if (argv['--name']) {
output.print(
`${prependEmoji(
`The ${param(
'--name'
)} option is deprecated (https://vercel.link/name-flag)`,
emoji('warning')
)}\n`
);
}
// retrieve `project` and `org` from .vercel
const link = await getLinkedProject(client, path);
if (link.status === 'error') {
return link.exitCode;
}
let { org, project, status } = link;
let newProjectName = null;
let rootDirectory = project ? project.rootDirectory : null;
let sourceFilesOutsideRootDirectory = true;
if (status === 'not_linked') {
const shouldStartSetup =
autoConfirm ||
(await confirm(
`Set up and deploy ${chalk.cyan(`${toHumanPath(path)}`)}?`,
true
));
if (!shouldStartSetup) {
output.print(`Aborted. Project not set up.\n`);
return 0;
}
try {
org = await selectOrg(
client,
'Which scope do you want to deploy to?',
autoConfirm
);
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
return 1;
}
throw err;
}
// We use `localConfig` here to read the name
// even though the `vercel.json` file can change
// afterwards, this is fine since the property
// will be deprecated and can be replaced with
// user input.
const detectedProjectName = getProjectName({
argv,
nowConfig: localConfig || {},
isFile,
paths,
});
const projectOrNewProjectName = await inputProject(
output,
client,
org,
detectedProjectName,
autoConfirm
);
if (typeof projectOrNewProjectName === 'string') {
newProjectName = projectOrNewProjectName;
rootDirectory = await inputRootDirectory(path, output, autoConfirm);
} else {
project = projectOrNewProjectName;
rootDirectory = project.rootDirectory;
sourceFilesOutsideRootDirectory = project.sourceFilesOutsideRootDirectory;
// we can already link the project
await linkFolderToProject(
output,
path,
{
projectId: project.id,
orgId: org.id,
},
project.name,
org.slug
);
status = 'linked';
}
}
// At this point `org` should be populated
if (!org) {
throw new Error(`"org" is not defined`);
}
// Set the `contextName` and `currentTeam` as specified by the
// Project Settings, so that API calls happen with the proper scope
const contextName = org.slug;
client.config.currentTeam = org.type === 'team' ? org.id : undefined;
// if we have `sourceFilesOutsideRootDirectory` set to `true`, we use the current path
// and upload the entire directory.
const sourcePath =
rootDirectory && !sourceFilesOutsideRootDirectory
? join(path, rootDirectory)
: path;
if (
rootDirectory &&
(await validateRootDirectory(
output,
path,
sourcePath,
project
? `To change your Project Settings, go to https://vercel.com/${org?.slug}/${project.name}/settings`
: ''
)) === false
) {
return 1;
}
// If Root Directory is used we'll try to read the config
// from there instead and use it if it exists.
if (rootDirectory) {
const rootDirectoryConfig = readLocalConfig(join(path, rootDirectory));
if (rootDirectoryConfig) {
debug(`Read local config from root directory (${rootDirectory})`);
localConfig = rootDirectoryConfig;
} else if (localConfig) {
output.print(
`${prependEmoji(
`The ${highlight(
localConfig[fileNameSymbol]!
)} file should be inside of the provided root directory.`,
emoji('warning')
)}\n`
);
}
}
localConfig = localConfig || {};
if (localConfig.name) {
output.print(
`${prependEmoji(
`The ${code('name')} property in ${highlight(
localConfig[fileNameSymbol]!
)} is deprecated (https://vercel.link/name-prop)`,
emoji('warning')
)}\n`
);
}
// build `env`
const isObject = (item: any) =>
Object.prototype.toString.call(item) === '[object Object]';
// This validation needs to happen on the client side because
// the data is merged with other data before it is passed to the API (which
// also does schema validation).
if (typeof localConfig.env !== 'undefined' && !isObject(localConfig.env)) {
error(
`The ${code('env')} property in ${highlight(
localConfig[fileNameSymbol]!
)} needs to be an object`
);
return 1;
}
if (typeof localConfig.build !== 'undefined') {
if (!isObject(localConfig.build)) {
error(
`The ${code('build')} property in ${highlight(
localConfig[fileNameSymbol]!
)} needs to be an object`
);
return 1;
}
if (
typeof localConfig.build.env !== 'undefined' &&
!isObject(localConfig.build.env)
) {
error(
`The ${code('build.env')} property in ${highlight(
localConfig[fileNameSymbol]!
)} needs to be an object`
);
return 1;
}
}
// build `meta`
const meta = Object.assign(
{},
parseMeta(localConfig.meta),
parseMeta(argv['--meta'])
);
// Merge dotenv config, `env` from vercel.json, and `--env` / `-e` arguments
const deploymentEnv = Object.assign(
{},
parseEnv(localConfig.env),
parseEnv(argv['--env'])
);
// Merge build env out of `build.env` from vercel.json, and `--build-env` args
const deploymentBuildEnv = Object.assign(
{},
parseEnv(localConfig.build && localConfig.build.env),
parseEnv(argv['--build-env'])
);
// If there's any undefined values, then inherit them from this process
try {
await addProcessEnv(log, deploymentEnv);
await addProcessEnv(log, deploymentBuildEnv);
} catch (err) {
error(err.message);
return 1;
}
// build `regions`
const regionFlag = (argv['--regions'] || '')
.split(',')
.map((s: string) => s.trim())
.filter(Boolean);
const regions = regionFlag.length > 0 ? regionFlag : localConfig.regions;
// build `target`
let target;
if (argv['--target']) {
const deprecatedTarget = argv['--target'];
if (!['staging', 'production'].includes(deprecatedTarget)) {
error(
`The specified ${param('--target')} ${code(
deprecatedTarget
)} is not valid`
);
return 1;
}
if (deprecatedTarget === 'production') {
warn(
'We recommend using the much shorter `--prod` option instead of `--target production` (deprecated)'
);
}
output.debug(`Setting target to ${deprecatedTarget}`);
target = deprecatedTarget;
} else if (argv['--prod']) {
output.debug('Setting target to production');
target = 'production';
}
const currentTeam = org?.type === 'team' ? org.id : undefined;
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
let deployStamp = stamp();
let deployment = null;
try {
const createArgs: any = {
name: project ? project.name : newProjectName,
env: deploymentEnv,
build: { env: deploymentBuildEnv },
forceNew: argv['--force'],
withCache: argv['--with-cache'],
quiet,
wantsPublic: argv['--public'] || localConfig.public,
isFile,
type: null,
nowConfig: localConfig,
regions,
meta,
deployStamp,
target,
skipAutoDetectionConfirmation: autoConfirm,
};
if (!localConfig.builds || localConfig.builds.length === 0) {
// Only add projectSettings for zero config deployments
createArgs.projectSettings = { sourceFilesOutsideRootDirectory };
}
deployment = await createDeploy(
client,
now,
contextName,
[sourcePath],
createArgs,
org,
!project && !isFile,
path
);
if (deployment.code === 'missing_project_settings') {
let { projectSettings, framework } = deployment;
if (rootDirectory) {
projectSettings.rootDirectory = rootDirectory;
}
if (typeof sourceFilesOutsideRootDirectory !== 'undefined') {
projectSettings.sourceFilesOutsideRootDirectory =
sourceFilesOutsideRootDirectory;
}
const settings = await editProjectSettings(
output,
projectSettings,
framework
);
// deploy again, but send projectSettings this time
createArgs.projectSettings = settings;
deployStamp = stamp();
createArgs.deployStamp = deployStamp;
deployment = await createDeploy(
client,
now,
contextName,
[sourcePath],
createArgs,
org,
false,
path
);
}
if (deployment instanceof NotDomainOwner) {
output.error(deployment.message);
return 1;
}
if (deployment instanceof Error) {
output.error(
deployment.message ||
'An unexpected error occurred while deploying your project',
undefined,
'https://vercel.link/help',
'Contact Support'
);
return 1;
}
if (deployment.readyState === 'CANCELED') {
output.print('The deployment has been canceled.\n');
return 1;
}
const deploymentResponse = await getDeploymentByIdOrHost(
client,
contextName,
deployment.id,
'v10'
);
if (
deploymentResponse instanceof DeploymentNotFound ||
deploymentResponse instanceof DeploymentPermissionDenied ||
deploymentResponse instanceof InvalidDeploymentId
) {
output.error(deploymentResponse.message);
return 1;
}
if (deployment === null) {
error('Uploading failed. Please try again.');
return 1;
}
} catch (err) {
debug(`Error: ${err}\n${err.stack}`);
if (err instanceof NotDomainOwner) {
output.error(err.message);
return 1;
}
if (err instanceof DomainNotFound && err.meta && err.meta.domain) {
output.debug(
`The domain ${err.meta.domain} was not found, trying to purchase it`
);
const purchase = await purchaseDomainIfAvailable(
output,
client,
err.meta.domain,
contextName
);
if (purchase === true) {
output.success(`Successfully purchased the domain ${err.meta.domain}!`);
// We exit if the purchase is completed since
// the domain verification can take some time
return 0;
}
if (purchase === false || purchase instanceof UserAborted) {
handleCreateDeployError(output, deployment, localConfig);
return 1;
}
handleCreateDeployError(output, purchase, localConfig);
return 1;
}
if (
err instanceof DomainNotFound ||
err instanceof DomainNotVerified ||
err instanceof NotDomainOwner ||
err instanceof DomainPermissionDenied ||
err instanceof DomainVerificationFailed ||
err instanceof SchemaValidationFailed ||
err instanceof InvalidDomain ||
err instanceof DeploymentNotFound ||
err instanceof BuildsRateLimited ||
err instanceof DeploymentsRateLimited ||
err instanceof AliasDomainConfigured ||
err instanceof MissingBuildScript ||
err instanceof ConflictingFilePath ||
err instanceof ConflictingPathSegment
) {
handleCreateDeployError(output, err, localConfig);
return 1;
}
if (err instanceof BuildError) {
output.error(err.message || 'Build failed');
output.error(
`Check your logs at https://${now.url}/_logs or run ${getCommandName(
`logs ${now.url}`
)}`
);
return 1;
}
if (err.keyword === 'additionalProperties' && err.dataPath === '.scale') {
const { additionalProperty = '' } = err.params || {};
const message = `Invalid DC name for the scale option: ${additionalProperty}`;
error(message);
}
if (err.code === 'size_limit_exceeded') {
const { sizeLimit = 0 } = err;
const message = `File size limit exceeded (${bytes(sizeLimit)})`;
error(message);
return 1;
}
handleError(err);
return 1;
}
return printDeploymentStatus(
output,
client,
deployment,
deployStamp,
!argv['--no-clipboard'],
isFile
);
}
function handleCreateDeployError(
output: Output,
error: Error,
localConfig: VercelConfig
) {
if (error instanceof InvalidDomain) {
output.error(`The domain ${error.meta.domain} is not valid`);
return 1;
}
if (error instanceof DomainVerificationFailed) {
output.error(
`The domain used as a suffix ${chalk.underline(
error.meta.domain
)} is not verified and can't be used as custom suffix.`
);
return 1;
}
if (error instanceof DomainPermissionDenied) {
output.error(
`You don't have permissions to access the domain used as a suffix ${chalk.underline(
error.meta.domain
)}.`
);
return 1;
}
if (error instanceof SchemaValidationFailed) {
const niceError = getPrettyError(error.meta);
const fileName = localConfig[fileNameSymbol] || 'vercel.json';
niceError.message = `Invalid ${fileName} - ${niceError.message}`;
output.prettyError(niceError);
return 1;
}
if (error instanceof TooManyRequests) {
output.error(
`Too many requests detected for ${error.meta.api} API. Try again in ${ms(
error.meta.retryAfter * 1000,
{
long: true,
}
)}.`
);
return 1;
}
if (error instanceof DomainNotVerified) {
output.error(
`The domain used as an alias ${chalk.underline(
error.meta.domain
)} is not verified yet. Please verify it.`
);
return 1;
}
if (error instanceof BuildsRateLimited) {
output.error(error.message);
output.note(
`Run ${getCommandName('upgrade')} to increase your builds limit.`
);
return 1;
}
if (
error instanceof DeploymentNotFound ||
error instanceof NotDomainOwner ||
error instanceof DeploymentsRateLimited ||
error instanceof AliasDomainConfigured ||
error instanceof MissingBuildScript ||
error instanceof ConflictingFilePath ||
error instanceof ConflictingPathSegment
) {
output.error(error.message);
return 1;
}
return error;
}

View File

@@ -36,17 +36,11 @@ export default async function dev(
]); ]);
if (link.status === 'not_linked' && !process.env.__VERCEL_SKIP_DEV_CMD) { if (link.status === 'not_linked' && !process.env.__VERCEL_SKIP_DEV_CMD) {
const autoConfirm = opts['--confirm'] || false; link = await setupAndLink(client, cwd, {
const forceDelete = false; autoConfirm: opts['--confirm'],
successEmoji: 'link',
link = await setupAndLink( setupMsg: 'Set up and develop',
client, });
cwd,
forceDelete,
autoConfirm,
'link',
'Set up and develop'
);
if (link.status === 'not_linked') { if (link.status === 'not_linked') {
// User aborted project linking questions // User aborted project linking questions

View File

@@ -5,7 +5,6 @@ import { User, Team } from '../../types';
import * as ERRORS from '../../util/errors-ts'; import * as ERRORS from '../../util/errors-ts';
import Client from '../../util/client'; import Client from '../../util/client';
import getScope from '../../util/get-scope'; import getScope from '../../util/get-scope';
import withSpinner from '../../util/with-spinner';
import moveOutDomain from '../../util/domains/move-out-domain'; import moveOutDomain from '../../util/domains/move-out-domain';
import isRootDomain from '../../util/is-root-domain'; import isRootDomain from '../../util/is-root-domain';
import textInput from '../../util/input/text'; import textInput from '../../util/input/text';
@@ -106,9 +105,14 @@ export default async function move(
} }
const context = contextName; const context = contextName;
const moveTokenResult = await withSpinner('Moving', () => { output.spinner('Moving');
return moveOutDomain(client, context, domainName, matchId || destination); const moveTokenResult = await moveOutDomain(
}); client,
context,
domainName,
matchId || destination
);
if (moveTokenResult instanceof ERRORS.DomainMoveConflict) { if (moveTokenResult instanceof ERRORS.DomainMoveConflict) {
const { suffix, pendingAsyncPurchase } = moveTokenResult.meta; const { suffix, pendingAsyncPurchase } = moveTokenResult.meta;
if (suffix) { if (suffix) {

View File

@@ -7,7 +7,6 @@ import param from '../../util/output/param';
import transferInDomain from '../../util/domains/transfer-in-domain'; import transferInDomain from '../../util/domains/transfer-in-domain';
import stamp from '../../util/output/stamp'; import stamp from '../../util/output/stamp';
import getAuthCode from '../../util/domains/get-auth-code'; import getAuthCode from '../../util/domains/get-auth-code';
import withSpinner from '../../util/with-spinner';
import getDomainPrice from '../../util/domains/get-domain-price'; import getDomainPrice from '../../util/domains/get-domain-price';
import checkTransfer from '../../util/domains/check-transfer'; import checkTransfer from '../../util/domains/check-transfer';
import promptBool from '../../util/input/prompt-bool'; import promptBool from '../../util/input/prompt-bool';
@@ -89,9 +88,13 @@ export default async function transferIn(
} }
const transferStamp = stamp(); const transferStamp = stamp();
const transferInResult = await withSpinner( output.spinner(`Initiating transfer for domain ${domainName}`);
`Initiating transfer for domain ${domainName}`,
() => transferInDomain(client, domainName, authCode, price) const transferInResult = await transferInDomain(
client,
domainName,
authCode,
price
); );
if (transferInResult instanceof ERRORS.InvalidDomain) { if (transferInResult instanceof ERRORS.InvalidDomain) {

View File

@@ -13,7 +13,6 @@ import {
} from '../../util/env/env-target'; } from '../../util/env/env-target';
import readStandardInput from '../../util/input/read-standard-input'; import readStandardInput from '../../util/input/read-standard-input';
import param from '../../util/output/param'; import param from '../../util/output/param';
import withSpinner from '../../util/with-spinner';
import { emoji, prependEmoji } from '../../util/emoji'; import { emoji, prependEmoji } from '../../util/emoji';
import { isKnownError } from '../../util/env/known-error'; import { isKnownError } from '../../util/env/known-error';
import { getCommandName } from '../../util/pkg-name'; import { getCommandName } from '../../util/pkg-name';
@@ -142,17 +141,16 @@ export default async function add(
const addStamp = stamp(); const addStamp = stamp();
try { try {
await withSpinner('Saving', () => output.spinner('Saving');
addEnvRecord( await addEnvRecord(
output, output,
client, client,
project.id, project.id,
ProjectEnvType.Encrypted, ProjectEnvType.Encrypted,
envName, envName,
envValue, envValue,
envTargets, envTargets,
envGitBranch envGitBranch
)
); );
} catch (error) { } catch (error) {
if (isKnownError(error) && error.serverMessage) { if (isKnownError(error) && error.serverMessage) {

View File

@@ -6,7 +6,6 @@ import Client from '../../util/client';
import stamp from '../../util/output/stamp'; import stamp from '../../util/output/stamp';
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records'; import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
import param from '../../util/output/param'; import param from '../../util/output/param';
import withSpinner from '../../util/with-spinner';
import { join } from 'path'; import { join } from 'path';
import { promises, openSync, closeSync, readSync } from 'fs'; import { promises, openSync, closeSync, readSync } from 'fs';
import { emoji, prependEmoji } from '../../util/emoji'; import { emoji, prependEmoji } from '../../util/emoji';
@@ -83,19 +82,16 @@ export default async function pull(
project.name project.name
)}\n` )}\n`
); );
const pullStamp = stamp();
const [ const pullStamp = stamp();
{ envs: projectEnvs }, output.spinner('Downloading');
{ systemEnvValues },
] = await withSpinner('Downloading', () => const [{ envs: projectEnvs }, { systemEnvValues }] = await Promise.all([
Promise.all([ getDecryptedEnvRecords(output, client, project.id),
getDecryptedEnvRecords(output, client, project.id), project.autoExposeSystemEnvs
project.autoExposeSystemEnvs ? getSystemEnvValues(output, client, project.id)
? getSystemEnvValues(output, client, project.id) : { systemEnvValues: [] },
: { systemEnvValues: [] }, ]);
])
);
const records = exposeSystemEnvs( const records = exposeSystemEnvs(
projectEnvs, projectEnvs,
@@ -120,6 +116,7 @@ export default async function pull(
emoji('success') emoji('success')
)}\n` )}\n`
); );
return 0; return 0;
} }

View File

@@ -13,7 +13,6 @@ import {
import Client from '../../util/client'; import Client from '../../util/client';
import stamp from '../../util/output/stamp'; import stamp from '../../util/output/stamp';
import param from '../../util/output/param'; import param from '../../util/output/param';
import withSpinner from '../../util/with-spinner';
import { emoji, prependEmoji } from '../../util/emoji'; import { emoji, prependEmoji } from '../../util/emoji';
import { isKnownError } from '../../util/env/known-error'; import { isKnownError } from '../../util/env/known-error';
import { getCommandName } from '../../util/pkg-name'; import { getCommandName } from '../../util/pkg-name';
@@ -112,9 +111,8 @@ export default async function rm(
const rmStamp = stamp(); const rmStamp = stamp();
try { try {
await withSpinner('Removing', async () => { output.spinner('Removing');
await removeEnvRecord(output, client, project.id, env); await removeEnvRecord(output, client, project.id, env);
});
} catch (error) { } catch (error) {
if (isKnownError(error) && error.serverMessage) { if (isKnownError(error) && error.serverMessage) {
output.error(error.serverMessage); output.error(error.serverMessage);

View File

@@ -5,7 +5,6 @@ import getSubcommand from '../../util/get-subcommand';
import Client from '../../util/client'; import Client from '../../util/client';
import handleError from '../../util/handle-error'; import handleError from '../../util/handle-error';
import logo from '../../util/output/logo'; import logo from '../../util/output/logo';
import error from '../../util/output/error';
import init from './init'; import init from './init';
import { getPkgName } from '../../util/pkg-name'; import { getPkgName } from '../../util/pkg-name';
@@ -44,6 +43,7 @@ const help = () => {
}; };
export default async function main(client: Client) { export default async function main(client: Client) {
const { output } = client;
let argv; let argv;
let args; let args;
@@ -64,15 +64,15 @@ export default async function main(client: Client) {
} }
if (argv._.length > 3) { if (argv._.length > 3) {
client.output.error('Too much arguments.'); output.error('Too much arguments.');
return 1; return 1;
} }
try { try {
return await init(client, argv, args); return await init(client, argv, args);
} catch (err) { } catch (err) {
console.log(error(err.message)); output.prettyError(err);
client.output.debug(err.stack); output.debug(err.stack);
return 1; return 1;
} }
} }

View File

@@ -2,16 +2,13 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import tar from 'tar-fs'; import tar from 'tar-fs';
import chalk from 'chalk'; import chalk from 'chalk';
import fetch from 'node-fetch';
// @ts-ignore // @ts-ignore
import listInput from '../../util/input/list'; import listInput from '../../util/input/list';
import listItem from '../../util/output/list-item'; import listItem from '../../util/output/list-item';
import promptBool from '../../util/input/prompt-bool'; import promptBool from '../../util/input/prompt-bool';
import toHumanPath from '../../util/humanize-path'; import toHumanPath from '../../util/humanize-path';
import { Output } from '../../util/output';
import Client from '../../util/client'; import Client from '../../util/client';
import success from '../../util/output/success';
import info from '../../util/output/info'; import info from '../../util/output/info';
import cmd from '../../util/output/cmd'; import cmd from '../../util/output/cmd';
import didYouMean from '../../util/init/did-you-mean'; import didYouMean from '../../util/init/did-you-mean';
@@ -40,7 +37,7 @@ export default async function init(
const [name, dir] = args; const [name, dir] = args;
const force = opts['-f'] || opts['--force']; const force = opts['-f'] || opts['--force'];
const examples = await fetchExampleList(output); const examples = await fetchExampleList(client);
if (!examples) { if (!examples) {
throw new Error(`Could not fetch example list.`); throw new Error(`Could not fetch example list.`);
@@ -56,47 +53,37 @@ export default async function init(
return 0; return 0;
} }
return extractExample(output, chosen, dir, force); return extractExample(client, chosen, dir, force);
} }
if (exampleList.includes(name)) { if (exampleList.includes(name)) {
return extractExample(output, name, dir, force); return extractExample(client, name, dir, force);
} }
const oldExample = examples.find(x => !x.visible && x.name === name); const oldExample = examples.find(x => !x.visible && x.name === name);
if (oldExample) { if (oldExample) {
return extractExample(output, name, dir, force, 'v1'); return extractExample(client, name, dir, force, 'v1');
} }
const found = await guess(exampleList, name); const found = await guess(exampleList, name);
if (typeof found === 'string') { if (typeof found === 'string') {
return extractExample(output, found, dir, force); return extractExample(client, found, dir, force);
} }
console.log(info('No changes made.')); output.log(info('No changes made.'));
return 0; return 0;
} }
/** /**
* Fetch example list json * Fetch example list json
*/ */
async function fetchExampleList(output: Output) { async function fetchExampleList(client: Client) {
output.spinner('Fetching examples'); client.output.spinner('Fetching examples');
const url = `${EXAMPLE_API}/v2/list.json`; const url = `${EXAMPLE_API}/v2/list.json`;
try { const body = await client.fetch<Example[]>(url);
const resp = await fetch(url); return body;
output.stopSpinner();
if (resp.status !== 200) {
throw new Error(`Failed fetching list.json (${resp.statusText}).`);
}
return (await resp.json()) as Example[];
} catch (e) {
output.stopSpinner();
}
} }
/** /**
@@ -119,31 +106,33 @@ async function chooseFromDropdown(message: string, exampleList: string[]) {
* Extract example to directory * Extract example to directory
*/ */
async function extractExample( async function extractExample(
output: Output, client: Client,
name: string, name: string,
dir: string, dir: string,
force?: boolean, force?: boolean,
ver: string = 'v2' ver: string = 'v2'
) { ) {
const { output } = client;
const folder = prepareFolder(process.cwd(), dir || name, force); const folder = prepareFolder(process.cwd(), dir || name, force);
output.spinner(`Fetching ${name}`); output.spinner(`Fetching ${name}`);
const url = `${EXAMPLE_API}/${ver}/download/${name}.tar.gz`; const url = `${EXAMPLE_API}/${ver}/download/${name}.tar.gz`;
return fetch(url) return client
.then(async resp => { .fetch(url, { json: false })
.then(async res => {
output.stopSpinner(); output.stopSpinner();
if (resp.status !== 200) { if (res.status !== 200) {
throw new Error(`Could not get ${name}.tar.gz`); throw new Error(`Could not get ${name}.tar.gz`);
} }
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
const extractor = tar.extract(folder); const extractor = tar.extract(folder);
resp.body.on('error', reject); res.body.on('error', reject);
extractor.on('error', reject); extractor.on('error', reject);
extractor.on('finish', resolve); extractor.on('finish', resolve);
resp.body.pipe(extractor); res.body.pipe(extractor);
}); });
const successLog = `Initialized "${chalk.bold( const successLog = `Initialized "${chalk.bold(
@@ -158,7 +147,7 @@ async function extractExample(
`cd ${folderRel}` `cd ${folderRel}`
)} and run ${getCommandName()}.` )} and run ${getCommandName()}.`
); );
console.log(success(`${successLog}\n${deployHint}`)); output.success(`${successLog}\n${deployHint}`);
return 0; return 0;
}) })
.catch(e => { .catch(e => {

View File

@@ -11,6 +11,7 @@ import { getPkgName, getCommandName } from '../util/pkg-name';
import Client from '../util/client'; import Client from '../util/client';
import { getDeployment } from '../util/get-deployment'; import { getDeployment } from '../util/get-deployment';
import { Deployment } from '@vercel/client'; import { Deployment } from '@vercel/client';
import { Build } from '../types';
const help = () => { const help = () => {
console.log(` console.log(`
@@ -116,7 +117,9 @@ export default async function main(client: Client) {
const { builds } = const { builds } =
deployment.version === 2 deployment.version === 2
? await client.fetch(`/v1/now/deployments/${id}/builds`) ? await client.fetch<{ builds: Build[] }>(
`/v1/now/deployments/${id}/builds`
)
: { builds: [] }; : { builds: [] };
log( log(
@@ -146,7 +149,8 @@ export default async function main(client: Client) {
for (const build of builds) { for (const build of builds) {
const { id, createdAt, readyStateAt } = build; const { id, createdAt, readyStateAt } = build;
times[id] = createdAt ? elapsed(readyStateAt - createdAt) : null; times[id] =
createdAt && readyStateAt ? elapsed(readyStateAt - createdAt) : null;
} }
print(chalk.bold(' Builds\n\n')); print(chalk.bold(' Builds\n\n'));

View File

@@ -1,8 +1,6 @@
import chalk from 'chalk'; import chalk from 'chalk';
import Client from '../../util/client'; import Client from '../../util/client';
import getArgs from '../../util/get-args'; import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import handleError from '../../util/handle-error';
import logo from '../../util/output/logo'; import logo from '../../util/output/logo';
import { getPkgName } from '../../util/pkg-name'; import { getPkgName } from '../../util/pkg-name';
import setupAndLink from '../../util/link/setup-and-link'; import setupAndLink from '../../util/link/setup-and-link';
@@ -24,6 +22,9 @@ const help = () => {
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline( -t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
'TOKEN' 'TOKEN'
)} Login token )} Login token
-p ${chalk.bold.underline('NAME')}, --project=${chalk.bold.underline(
'NAME'
)} Project name
--confirm Confirm default options and skip questions --confirm Confirm default options and skip questions
${chalk.dim('Examples:')} ${chalk.dim('Examples:')}
@@ -44,40 +45,26 @@ const help = () => {
`); `);
}; };
const COMMAND_CONFIG = {
// No subcommands yet
};
export default async function main(client: Client) { export default async function main(client: Client) {
let argv; const argv = getArgs(client.argv.slice(2), {
'--confirm': Boolean,
try { '--project': String,
argv = getArgs(client.argv.slice(2), { '-p': '--project',
'--confirm': Boolean, });
});
} catch (error) {
handleError(error);
return 1;
}
if (argv['--help']) { if (argv['--help']) {
help(); help();
return 2; return 2;
} }
const { args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG); const cwd = argv._[1] || process.cwd();
const path = args[0] || process.cwd(); const link = await setupAndLink(client, cwd, {
const autoConfirm = argv['--confirm'] || false; forceDelete: true,
const forceDelete = true; autoConfirm: argv['--confirm'],
projectName: argv['--project'],
const link = await setupAndLink( successEmoji: 'success',
client, setupMsg: 'Set up',
path, });
forceDelete,
autoConfirm,
'success',
'Set up'
);
if (link.status === 'error') { if (link.status === 'error') {
return link.exitCode; return link.exitCode;

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk'; import chalk from 'chalk';
import stamp from '../../util/output/stamp.ts'; import stamp from '../../util/output/stamp.ts';
import info from '../../util/output/info'; import info from '../../util/output/info';
import rightPad from '../../util/output/right-pad';
import eraseLines from '../../util/output/erase-lines'; import eraseLines from '../../util/output/erase-lines';
import chars from '../../util/output/chars'; import chars from '../../util/output/chars';
import note from '../../util/output/note'; import note from '../../util/output/note';
@@ -30,8 +29,8 @@ const gracefulExit = () => {
return 0; return 0;
}; };
const teamUrlPrefix = rightPad('Team URL', 14) + chalk.gray('vercel.com/'); const teamUrlPrefix = 'Team URL'.padEnd(14) + chalk.gray('vercel.com/');
const teamNamePrefix = rightPad('Team Name', 14); const teamNamePrefix = 'Team Name'.padEnd(14);
export default async function add(client, teams) { export default async function add(client, teams) {
let slug; let slug;

View File

@@ -4,7 +4,6 @@ import cmd from '../../util/output/cmd.ts';
import stamp from '../../util/output/stamp.ts'; import stamp from '../../util/output/stamp.ts';
import param from '../../util/output/param.ts'; import param from '../../util/output/param.ts';
import chars from '../../util/output/chars'; import chars from '../../util/output/chars';
import rightPad from '../../util/output/right-pad';
import textInput from '../../util/input/text'; import textInput from '../../util/input/text';
import eraseLines from '../../util/output/erase-lines'; import eraseLines from '../../util/output/erase-lines';
import getUser from '../../util/get-user.ts'; import getUser from '../../util/get-user.ts';
@@ -126,8 +125,8 @@ export default async function invite(
return; return;
} }
const inviteUserPrefix = rightPad('Invite User', 14); const inviteUserPrefix = 'Invite User'.padEnd(14);
const sentEmailPrefix = rightPad('Sent Email', 14); const sentEmailPrefix = 'Sent Email'.padEnd(14);
const emails = []; const emails = [];
let hasError = false; let hasError = false;
let email; let email;

View File

@@ -430,12 +430,16 @@ const main = async () => {
return result; return result;
} }
if (result.teamId) {
// SSO login, so set the current scope to the appropriate Team
client.config.currentTeam = result.teamId;
} else {
delete client.config.currentTeam;
}
// When `result` is a string it's the user's authentication token. // When `result` is a string it's the user's authentication token.
// It needs to be saved to the configuration file. // It needs to be saved to the configuration file.
client.authConfig.token = result; client.authConfig.token = result.token;
// New user, so we can't keep the team
delete client.config.currentTeam;
configFiles.writeToAuthConfigFile(client.authConfig); configFiles.writeToAuthConfigFile(client.authConfig);
configFiles.writeToConfigFile(client.config); configFiles.writeToConfigFile(client.config);

View File

@@ -16,11 +16,13 @@ export interface JSONObject {
} }
export interface AuthConfig { export interface AuthConfig {
_: string;
token?: string; token?: string;
skipWrite?: boolean; skipWrite?: boolean;
} }
export interface GlobalConfig { export interface GlobalConfig {
_: string;
currentTeam?: string; currentTeam?: string;
includeScheme?: string; includeScheme?: string;
collectMetrics?: boolean; collectMetrics?: boolean;
@@ -306,3 +308,141 @@ export interface Token {
createdAt: number; createdAt: number;
teamId?: string; teamId?: string;
} }
/**
* An object representing a Build on Vercel
*/
export interface Build {
/**
* The unique identifier of the Build
* @example "bld_q5fj68jh7eewfe8"
*/
id: string;
/**
* The unique identifier of the deployment
* @example "dpl_BRGyoU2Jzzwx7myBnqv3xjRDD2GnHTwUWyFybnrUvjDD"
*/
deploymentId: string;
/**
* The entrypoint of the deployment
* @example "api/index.js"
*/
entrypoint: string;
/**
* The state of the deployment depending on the process of deploying,
* or if it is ready or in an error state
* @example "READY"
*/
readyState:
| 'INITIALIZING'
| 'BUILDING'
| 'UPLOADING'
| 'DEPLOYING'
| 'READY'
| 'ARCHIVED'
| 'ERROR'
| 'QUEUED'
| 'CANCELED';
/**
* The time at which the Build state was last modified
* @example 1567024758130
*/
readyStateAt?: number;
/**
* The time at which the Build was scheduled to be built
* @example 1567024756543
*/
scheduledAt?: number | null;
/**
* The time at which the Build was created
* @example 1567071524208
*/
createdAt?: number;
/**
* The time at which the Build was deployed
* @example 1567071598563
*/
deployedAt?: number;
/**
* The region where the Build was first created
* @example "sfo1"
*/
createdIn?: string;
/**
* The Runtime the Build used to generate the output
* @example "@vercel/node"
*/
use?: string;
/**
* An object that contains the Build's configuration
* @example {"zeroConfig": true}
*/
config?: {
distDir?: string | undefined;
forceBuildIn?: string | undefined;
reuseWorkPathFrom?: string | undefined;
zeroConfig?: boolean | undefined;
};
/**
* A list of outputs for the Build that can be either Serverless Functions or static files
*/
output: BuildOutput[];
/**
* If the Build uses the `@vercel/static` Runtime, it contains a hashed string of all outputs
* @example null
*/
fingerprint?: string | null;
copiedFrom?: string;
}
export interface BuildOutput {
/**
* The type of the output
*/
type?: 'lambda' | 'file';
/**
* The absolute path of the file or Serverless Function
*/
path: string;
/**
* The SHA1 of the file
*/
digest: string;
/**
* The POSIX file permissions
*/
mode: number;
/**
* The size of the file in bytes
*/
size?: number;
/**
* If the output is a Serverless Function, an object
* containing the name, location and memory size of the function
*/
lambda?: {
functionName: string;
deployedTo: string[];
memorySize?: number;
timeout?: number;
layers?: string[];
} | null;
}

View File

@@ -1,5 +0,0 @@
export const isReady = ({ readyState }) => readyState === 'READY';
export const isFailed = ({ readyState }) =>
readyState.endsWith('_ERROR') || readyState === 'ERROR';
export const isDone = ({ readyState }) =>
isReady({ readyState }) || isFailed({ readyState });

View File

@@ -0,0 +1,7 @@
import { Build } from '../types';
export const isReady = ({ readyState }: Pick<Build, 'readyState'>) =>
readyState === 'READY';
export const isFailed = ({ readyState }: Pick<Build, 'readyState'>) =>
readyState.endsWith('_ERROR') || readyState === 'ERROR';

View File

@@ -1,9 +1,10 @@
export const getDefaultConfig = async existingCopy => { import { AuthConfig, GlobalConfig } from '../../types';
export const getDefaultConfig = async (existingCopy: GlobalConfig) => {
let migrated = false; let migrated = false;
const config = { const config: GlobalConfig = {
_: _: 'This is your Vercel config file. For more information see the global configuration documentation: https://vercel.com/docs/configuration#global',
'This is your Vercel config file. For more information see the global configuration documentation: https://vercel.com/docs/configuration#global',
collectMetrics: true, collectMetrics: true,
}; };
@@ -16,44 +17,33 @@ export const getDefaultConfig = async existingCopy => {
'collectMetrics', 'collectMetrics',
'api', 'api',
// This is deleted later in the code // This is deleted later in the code
'shownTips',
]; ];
try { try {
const existing = Object.assign({}, existingCopy); const existing = Object.assign({}, existingCopy);
// @ts-ignore
const sh = Object.assign({}, existing.sh || {}); const sh = Object.assign({}, existing.sh || {});
Object.assign(config, existing, sh); Object.assign(config, existing, sh);
for (const key of Object.keys(config)) { for (const key of Object.keys(config)) {
if (!keep.includes(key)) { if (!keep.includes(key)) {
// @ts-ignore
delete config[key]; delete config[key];
} }
} }
if (typeof config.currentTeam === 'object') { if (typeof config.currentTeam === 'object') {
// @ts-ignore
config.currentTeam = config.currentTeam.id; config.currentTeam = config.currentTeam.id;
} }
// @ts-ignore
if (typeof config.user === 'object') { if (typeof config.user === 'object') {
// @ts-ignore
config.user = config.user.uid || config.user.id; config.user = config.user.uid || config.user.id;
} }
// Make sure Now Desktop users don't see any tips
// again that they already dismissed
if (config.shownTips) {
if (config.desktop) {
config.desktop.shownTips = config.shownTips;
} else {
config.desktop = {
shownTips: config.shownTips,
};
}
// Clean up the old property
delete config.shownTips;
}
migrated = true; migrated = true;
} catch (err) {} } catch (err) {}
} }
@@ -61,16 +51,16 @@ export const getDefaultConfig = async existingCopy => {
return { config, migrated }; return { config, migrated };
}; };
export const getDefaultAuthConfig = async existing => { export const getDefaultAuthConfig = async (existing?: AuthConfig) => {
let migrated = false; let migrated = false;
const config = { const config: AuthConfig = {
_: _: 'This is your Vercel credentials file. DO NOT SHARE! More: https://vercel.com/docs/configuration#global',
'This is your Vercel credentials file. DO NOT SHARE! More: https://vercel.com/docs/configuration#global',
}; };
if (existing) { if (existing) {
try { try {
// @ts-ignore
const sh = existing.credentials.find(item => item.provider === 'sh'); const sh = existing.credentials.find(item => item.provider === 'sh');
if (sh) { if (sh) {

View File

@@ -82,14 +82,14 @@ function getNpmVersion(use = ''): string {
return ''; return '';
} }
export function getBuildUtils(packages: string[], org: string): string { export function getBuildUtils(packages: string[]): string {
const version = packages const version = packages
.map(getNpmVersion) .map(getNpmVersion)
.some(ver => ver.includes('canary')) .some(ver => ver.includes('canary'))
? 'canary' ? 'canary'
: 'latest'; : 'latest';
return `@${org}/build-utils@${version}`; return `@vercel/build-utils@${version}`;
} }
function parseVersionSafe(rawSpec: string) { function parseVersionSafe(rawSpec: string) {
@@ -191,10 +191,7 @@ export async function installBuilders(
return; return;
} }
packagesToInstall.push( packagesToInstall.push(getBuildUtils(packages));
getBuildUtils(packages, 'vercel'),
getBuildUtils(packages, 'now')
);
await npmInstall(builderDir, output, packagesToInstall, false); await npmInstall(builderDir, output, packagesToInstall, false);
@@ -307,10 +304,7 @@ export async function updateBuilders(
}); });
if (packagesToUpdate.length > 0) { if (packagesToUpdate.length > 0) {
packagesToUpdate.push( packagesToUpdate.push(getBuildUtils(packages));
getBuildUtils(packages, 'vercel'),
getBuildUtils(packages, 'now')
);
await npmInstall(builderDir, output, packagesToUpdate, true); await npmInstall(builderDir, output, packagesToUpdate, true);

View File

@@ -1,10 +1,9 @@
//@ts-ignore Missing types for 'title'
import title from 'title'; import title from 'title';
import { ProjectEnvVariable } from '../../types'; import { ProjectEnvVariable } from '../../types';
export default function formatEnvTarget(env: ProjectEnvVariable): string { export default function formatEnvTarget(env: ProjectEnvVariable): string {
const target = (Array.isArray(env.target) ? env.target : [env.target || '']) const target = (Array.isArray(env.target) ? env.target : [env.target || ''])
.map(title) .map(t => title(t))
.join(', '); .join(', ');
return env.gitBranch ? `${target} (${env.gitBranch})` : target; return env.gitBranch ? `${target} (${env.gitBranch})` : target;

View File

@@ -1,20 +0,0 @@
import { basename } from 'path';
export default function getProjectName({ argv, nowConfig, isFile, paths }) {
const nameCli = argv['--name'] || argv.name;
if (nameCli) {
return nameCli;
}
if (nowConfig.name) {
return nowConfig.name;
}
if (isFile || paths.length > 1) {
return 'files';
}
// Otherwise let's send the name of the directory
return basename(paths[0]);
}

View File

@@ -0,0 +1,34 @@
import arg from 'arg';
import { basename } from 'path';
import { VercelConfig } from '@vercel/client';
export interface GetProjectNameOptions {
argv: arg.Result<{ '--name': StringConstructor }>;
nowConfig?: VercelConfig;
isFile: boolean;
paths: string[];
}
export default function getProjectName({
argv,
nowConfig = {},
isFile,
paths = [],
}: GetProjectNameOptions) {
const nameCli = argv['--name'];
if (nameCli) {
return nameCli;
}
if (nowConfig.name) {
return nowConfig.name;
}
if (isFile || paths.length > 1) {
return 'files';
}
// Otherwise, use the name of the directory
return basename(paths[0] || '');
}

View File

@@ -1,8 +0,0 @@
function indent(text, n) {
return text
.split('\n')
.map(l => ' '.repeat(n) + l)
.join('\n');
}
export default indent;

View File

@@ -217,7 +217,10 @@ export default class Now extends EventEmitter {
}); });
} }
if (error.errorCode && error.errorCode === 'BUILD_FAILED') { if (
error.errorCode === 'BUILD_FAILED' ||
error.errorCode === 'UNEXPECTED_ERROR'
) {
return new BuildError({ return new BuildError({
message: error.errorMessage, message: error.errorMessage,
meta: {}, meta: {},

View File

@@ -4,17 +4,16 @@ import confirm from './confirm';
import getProjectByIdOrName from '../projects/get-project-by-id-or-name'; import getProjectByIdOrName from '../projects/get-project-by-id-or-name';
import chalk from 'chalk'; import chalk from 'chalk';
import { ProjectNotFound } from '../../util/errors-ts'; import { ProjectNotFound } from '../../util/errors-ts';
import { Output } from '../output';
import { Project, Org } from '../../types'; import { Project, Org } from '../../types';
import slugify from '@sindresorhus/slugify'; import slugify from '@sindresorhus/slugify';
export default async function inputProject( export default async function inputProject(
output: Output,
client: Client, client: Client,
org: Org, org: Org,
detectedProjectName: string, detectedProjectName: string,
autoConfirm: boolean autoConfirm: boolean
): Promise<Project | string> { ): Promise<Project | string> {
const { output } = client;
const slugifiedName = slugify(detectedProjectName); const slugifiedName = slugify(detectedProjectName);
// attempt to auto-detect a project to link // attempt to auto-detect a project to link

View File

@@ -25,13 +25,24 @@ import { EmojiLabel } from '../emoji';
import createDeploy from '../deploy/create-deploy'; import createDeploy from '../deploy/create-deploy';
import Now from '../index'; import Now from '../index';
export interface SetupAndLinkOptions {
forceDelete?: boolean;
autoConfirm?: boolean;
successEmoji: EmojiLabel;
setupMsg: string;
projectName?: string;
}
export default async function setupAndLink( export default async function setupAndLink(
client: Client, client: Client,
path: string, path: string,
forceDelete: boolean, {
autoConfirm: boolean, forceDelete = false,
successEmoji: EmojiLabel, autoConfirm = false,
setupMsg: string successEmoji,
setupMsg,
projectName,
}: SetupAndLinkOptions
): Promise<ProjectLinkResult> { ): Promise<ProjectLinkResult> {
const { const {
authConfig: { token }, authConfig: { token },
@@ -90,10 +101,9 @@ export default async function setupAndLink(
throw err; throw err;
} }
const detectedProjectName = basename(path); const detectedProjectName = projectName || basename(path);
const projectOrNewProjectName = await inputProject( const projectOrNewProjectName = await inputProject(
output,
client, client,
org, org,
detectedProjectName, detectedProjectName,

View File

@@ -2,6 +2,11 @@ import chalk from 'chalk';
import title from 'title'; import title from 'title';
import bytes from 'bytes'; import bytes from 'bytes';
import { isReady, isFailed } from '../build-state'; import { isReady, isFailed } from '../build-state';
import { Build, BuildOutput } from '../../types';
export interface Times {
[id: string]: string | null;
}
// That's how long the word "Initializing" is // That's how long the word "Initializing" is
const longestState = 12; const longestState = 12;
@@ -13,21 +18,24 @@ const padding = 8;
const MAX_BUILD_GROUPS = 5; const MAX_BUILD_GROUPS = 5;
const MAX_OUTPUTS_PER_GROUP = 5; const MAX_OUTPUTS_PER_GROUP = 5;
const prepareState = state => title(state.replace('_', ' ')); const prepareState = (state: string) => title(state.replace('_', ' '));
const hasOutput = (b: Build) => Array.isArray(b.output) && b.output.length > 0;
// Get the common path out of multiple builds // Get the common path out of multiple builds
const getCommonPath = (buildGroup) => { const getCommonPath = (buildGroup: Build[]) => {
const commonPath = []; const commonPath = [];
const splits = buildGroup.map((build) => getDirPath(build.entrypoint).split('/')); const splits = buildGroup.map(build =>
const shortest = splits.reduce((prevValue, currentValue) => getDirPath(build.entrypoint).split('/')
prevValue.length < currentValue.length );
? prevValue.length const shortest = splits.reduce(
: currentValue.length (prevValue, currentValue) => Math.min(prevValue, currentValue.length),
Infinity
); );
for (let i = 0; i <= shortest; i++) { for (let i = 0; i <= shortest; i++) {
const first = splits[0][i]; const first = splits[0][i];
if (splits.every((pathParts) => pathParts[i] === first)) { if (splits.every(pathParts => pathParts[i] === first)) {
commonPath.push(first); commonPath.push(first);
continue; continue;
} }
@@ -38,58 +46,63 @@ const getCommonPath = (buildGroup) => {
return commonPath.join('/') || '/'; return commonPath.join('/') || '/';
}; };
const styleBuild = (build, times, longestSource) => { const styleBuild = (build: Build, times: Times, longestSource: number) => {
const { entrypoint, readyState, id, hasOutput } = build; const { entrypoint, readyState, id } = build;
const state = prepareState(readyState).padEnd(longestState + padding); const state = prepareState(readyState).padEnd(longestState + padding);
const time = typeof times[id] === 'string' ? times[id] : ''; const time = typeof times[id] === 'string' ? times[id] : '';
let stateColor = chalk.grey; let stateColor = chalk.grey;
let pathColor = chalk.cyan; let pathColor = chalk.cyan;
if (isReady({ readyState })) { if (isReady(build)) {
stateColor = item => item; stateColor = chalk;
} else if (isFailed({ readyState })) { } else if (isFailed(build)) {
stateColor = chalk.red; stateColor = chalk.red;
pathColor = chalk.red; pathColor = chalk.red;
} }
const entry = entrypoint.padEnd(longestSource + padding); const entry = entrypoint.padEnd(longestSource + padding);
const prefix = hasOutput ? '┌' : '╶'; const prefix = hasOutput(build) ? '┌' : '╶';
return `${chalk.grey(prefix)} ${pathColor(entry)}${stateColor(state)}${time}`; return `${chalk.grey(prefix)} ${pathColor(entry)}${stateColor(state)}${time}`;
}; };
const styleHiddenBuilds = (commonPath, buildGroup, times, longestSource, isHidden = false) => { const styleHiddenBuilds = (
commonPath: string,
buildGroup: Build[],
times: Times,
longestSource: number,
isHidden = false
) => {
const { id } = buildGroup[0]; const { id } = buildGroup[0];
const entry = commonPath.padEnd(longestSource + padding); const entry = commonPath.padEnd(longestSource + padding);
const time = typeof times[id] === 'string' ? times[id] : ''; const time = typeof times[id] === 'string' ? times[id] : '';
const prefix = isHidden === false && buildGroup.some((build) => build.hasOutput) ? '┌' : '╶'; const prefix = isHidden === false && buildGroup.some(hasOutput) ? '┌' : '╶';
// Set the defaults so that they will be sorted // Set the defaults so that they will be sorted
const stateMap = { const stateMap: { [readyState: string]: number } = {
READY: 0, READY: 0,
ERROR: 0, ERROR: 0,
BUILDING: 0 BUILDING: 0,
}; };
buildGroup.map(({ readyState }) => { for (const { readyState } of buildGroup) {
stateMap[readyState] = stateMap[readyState] stateMap[readyState]++;
? stateMap[readyState] + 1 }
: 1;
return readyState; let state = Object.keys(stateMap)
}); .map(readyState => {
const counter = stateMap[readyState];
const name = prepareState(readyState);
let state = Object.keys(stateMap).map((readyState) => { if (!counter) {
const counter = stateMap[readyState]; return null;
const name = prepareState(readyState); }
if (!counter) { return `${counter > 9 ? '9+' : counter} ${name}`;
return null; })
} .filter(s => s)
.join(', ');
return `${counter > 9 ? '9+' : counter} ${name}`;
}).filter(s => s).join(', ')
// Since the longestState might still be shorter // Since the longestState might still be shorter
// than multiple states we still want to ensure // than multiple states we still want to ensure
@@ -100,7 +113,7 @@ const styleHiddenBuilds = (commonPath, buildGroup, times, longestSource, isHidd
let stateColor = chalk.grey; let stateColor = chalk.grey;
if (buildGroup.every(isReady)) { if (buildGroup.every(isReady)) {
stateColor = item => item; stateColor = chalk;
} else if (buildGroup.every(isFailed)) { } else if (buildGroup.every(isFailed)) {
stateColor = chalk.red; stateColor = chalk.red;
pathColor = chalk.red; pathColor = chalk.red;
@@ -113,8 +126,12 @@ const styleHiddenBuilds = (commonPath, buildGroup, times, longestSource, isHidd
return `${chalk.grey(prefix)} ${pathColor(entry)}${stateColor(state)}${time}`; return `${chalk.grey(prefix)} ${pathColor(entry)}${stateColor(state)}${time}`;
}; };
const styleOutput = (output) => { const styleOutput = (
const { type, path, readyState, size, isLast, lambda } = output; output: BuildOutput,
readyState: Build['readyState'],
isLast: boolean
) => {
const { type, path, size, lambda } = output;
const prefix = type === 'lambda' ? 'λ ' : ''; const prefix = type === 'lambda' ? 'λ ' : '';
const finalSize = size ? ` ${chalk.grey(`(${bytes(size)})`)}` : ''; const finalSize = size ? ` ${chalk.grey(`(${bytes(size)})`)}` : '';
@@ -122,7 +139,7 @@ const styleOutput = (output) => {
let finalRegion = ''; let finalRegion = '';
if (isReady({ readyState })) { if (isReady({ readyState })) {
color = item => item; color = chalk;
} else if (isFailed({ readyState })) { } else if (isFailed({ readyState })) {
color = chalk.red; color = chalk.red;
} }
@@ -141,7 +158,11 @@ const styleOutput = (output) => {
return `${chalk.grey(corner)} ${color(main)}`; return `${chalk.grey(corner)} ${color(main)}`;
}; };
const getDirPath = (path, level = 0, highestLevel = null) => { const getDirPath = (
path: string,
level = 0,
highestLevel: number | null = null
) => {
const parts = path.split('/').slice(0, -1); const parts = path.split('/').slice(0, -1);
if (highestLevel === null || level === 0) { if (highestLevel === null || level === 0) {
@@ -152,7 +173,7 @@ const getDirPath = (path, level = 0, highestLevel = null) => {
return parts.slice(0, reverseLevel).join('/'); return parts.slice(0, reverseLevel).join('/');
}; };
const sortByEntrypoint = (a, b) => { const sortByEntrypoint = (a: Build, b: Build) => {
const aPath = getDirPath(a.entrypoint); const aPath = getDirPath(a.entrypoint);
const bPath = getDirPath(b.entrypoint); const bPath = getDirPath(b.entrypoint);
@@ -175,22 +196,30 @@ const sortByEntrypoint = (a, b) => {
return 0; return 0;
}; };
const groupBuilds = (buildList, highestLevel, counter) => { const groupBuilds = (
const currentIndex = counter % (buildList.length); buildList: Build[][],
highestLevel: number,
counter: number
) => {
const currentIndex = counter % buildList.length;
const __level = Math.ceil(counter / buildList.length); const __level = Math.ceil(counter / buildList.length);
const _level = (__level === 0 ? 1 : __level) - 1; const _level = (__level === 0 ? 1 : __level) - 1;
const level = _level > highestLevel ? highestLevel : _level; const level = _level > highestLevel ? highestLevel : _level;
const currentPath = getDirPath(buildList[currentIndex][0].entrypoint, level, highestLevel); const currentPath = getDirPath(
buildList[currentIndex][0].entrypoint,
level,
highestLevel
);
const nextList = []; const nextList = [];
let currentGroup = []; const currentGroup = [];
for (let i = 0; i < buildList.length; i++) { for (let i = 0; i < buildList.length; i++) {
const group = buildList[i]; const group = buildList[i];
const path = getDirPath(group[0].entrypoint, level, highestLevel); const path = getDirPath(group[0].entrypoint, level, highestLevel);
if (path === currentPath) { if (path === currentPath) {
currentGroup = currentGroup.concat(group); currentGroup.push(...group);
} else { } else {
nextList.push(group); nextList.push(group);
} }
@@ -205,26 +234,10 @@ const groupBuilds = (buildList, highestLevel, counter) => {
return nextList; return nextList;
}; };
const prepareBuild = (build) => { export default (builds: Build[], times: Times) => {
build.hasOutput = Array.isArray(build.output) && build.output.length > 0;
if (build.hasOutput) {
build.output = build.output.map((item) => {
item.readyState = build.readyState;
return item;
});
}
return build;
};
export default (builds, times) => {
// Sort the builds by path // Sort the builds by path
// so that the grouping will be easier // so that the grouping will be easier
let path = builds let path = builds.sort(sortByEntrypoint).map(build => [build]);
.map(prepareBuild)
.sort(sortByEntrypoint)
.map(build => [build]);
const highestLevel = builds.reduce((prev, curr) => { const highestLevel = builds.reduce((prev, curr) => {
const partCounter = curr.entrypoint.split('/').length - 1; const partCounter = curr.entrypoint.split('/').length - 1;
@@ -251,28 +264,28 @@ export default (builds, times) => {
const final = []; const final = [];
let finalBuildsLength = path.length; let finalBuildsLength = path.length;
let lengthWithoutRootPaths = path.length; let lengthWithoutRootPaths = path.length;
let hiddenBuildGroup = []; let hiddenBuildGroup: Build[] = [];
// Ungroup the root files // Ungroup the root files
path = (() => { path = (() => {
const nextList = []; const nextList = [];
const rootList = []; const rootList: Build[][] = [];
for (const group of path) { for (const group of path) {
if (getCommonPath(group) === '/') { if (getCommonPath(group) === '/') {
group.map((item) => rootList.push([item])); group.map(item => rootList.push([item]));
} else { } else {
nextList.push(group); nextList.push(group);
} }
} }
lengthWithoutRootPaths = nextList.length; lengthWithoutRootPaths = nextList.length;
rootList.map((group) => nextList.push(group)); rootList.map(group => nextList.push(group));
return nextList; return nextList;
})(); })();
path.map((buildGroup) => { for (const buildGroup of path) {
const commonPath = getCommonPath(buildGroup); const commonPath = getCommonPath(buildGroup);
// All items with the common path / are a single group // All items with the common path / are a single group
@@ -286,51 +299,63 @@ export default (builds, times) => {
finalBuildsLength++; finalBuildsLength++;
} else { } else {
hiddenBuildGroup.push(buildGroup[0]); hiddenBuildGroup.push(buildGroup[0]);
return buildGroup; continue;
} }
} else if (buildGroup.length === 1) { } else if (buildGroup.length === 1) {
const item = buildGroup[0]; const item = buildGroup[0];
final.push(`${styleBuild(item, times, longestSource)}\n`); final.push(`${styleBuild(item, times, longestSource)}\n`);
finalBuildsLength++; finalBuildsLength++;
} else { } else {
final.push(`${styleHiddenBuilds(`${commonPath}/*`, buildGroup, times, longestSource)}\n`); final.push(
`${styleHiddenBuilds(
`${commonPath}/*`,
buildGroup,
times,
longestSource
)}\n`
);
finalBuildsLength++; finalBuildsLength++;
} }
// Get the first five outputs when the deployment is ready // Get the first five outputs when the deployment is ready
const outputs = buildGroup.reduce((prevValue, currentValue) => ( const outputs: BuildOutput[] = [];
prevValue.concat(Array.isArray(currentValue.output) for (const build of buildGroup) {
? currentValue.output if (Array.isArray(build.output)) {
: [] outputs.push(...build.output);
) }
), []);
outputs.slice(0, MAX_OUTPUTS_PER_GROUP).map((output, index) => (
final.push(`${styleOutput({
...output,
isLast: outputs.length === (index + 1)
})}\n`)
));
if (outputs.length > MAX_OUTPUTS_PER_GROUP) {
final.push(chalk.grey(`└── ${outputs.length - MAX_OUTPUTS_PER_GROUP} output items hidden\n`));
} }
return buildGroup; outputs
}); .slice(0, MAX_OUTPUTS_PER_GROUP)
.forEach((output, index) =>
final.push(
`${styleOutput(output, 'READY', outputs.length === index + 1)}\n`
)
);
if (outputs.length > MAX_OUTPUTS_PER_GROUP) {
final.push(
chalk.grey(
`└── ${outputs.length - MAX_OUTPUTS_PER_GROUP} output items hidden\n`
)
);
}
}
if (hiddenBuildGroup.length) { if (hiddenBuildGroup.length) {
final.push(`${styleHiddenBuilds( final.push(
`${hiddenBuildGroup.length} builds hidden`, `${styleHiddenBuilds(
hiddenBuildGroup, `${hiddenBuildGroup.length} builds hidden`,
times, hiddenBuildGroup,
longestSource, times,
true longestSource,
)}\n`); true
)}\n`
);
} }
return { return {
lines: final.length + 1, lines: final.length + 1,
toPrint: `${final.join('')}` toPrint: `${final.join('')}`,
}; };
}; };

View File

@@ -1,5 +0,0 @@
import { gray } from 'chalk';
const effect = msg => `${gray(`+ ${msg}`)}`;
export default effect;

View File

@@ -1,6 +0,0 @@
//
import formatLogText from './format-log-text';
export default function formatLogCmd(text) {
return `${formatLogText(text)}`;
}

View File

@@ -1,8 +0,0 @@
//
import formatLogText from './format-log-text';
export default function formatLogOutput(text, prefix = '') {
return formatLogText(text)
.split('\n')
.map(textItem => `${prefix}${textItem.replace(/^> /, '')}`);
}

View File

@@ -1,4 +0,0 @@
//
export default function formatLogText(text) {
return text.replace(/\n$/, '').replace(/^\n/, '');
}

View File

@@ -1,4 +1,4 @@
export default (input, level) => { export default (input: string, level: number) => {
const fill = ' '.repeat(level); const fill = ' '.repeat(level);
return `${fill}${input.replace(/\n/g, `\n${fill}`)}`; return `${fill}${input.replace(/\n/g, `\n${fill}`)}`;
}; };

View File

@@ -1,5 +0,0 @@
import { yellow } from 'chalk';
const note = msg => `${yellow('> NOTE:')} ${msg}`;
export default note;

View File

@@ -0,0 +1,3 @@
import { yellow } from 'chalk';
export default (msg: string) => `${yellow('> NOTE:')} ${msg}`;

View File

@@ -1,5 +0,0 @@
import { cyan } from 'chalk';
const ready = msg => `${cyan('> Ready!')} ${msg}`;
export default ready;

View File

@@ -0,0 +1,3 @@
import { cyan } from 'chalk';
export default (msg: string) => `${cyan('> Ready!')} ${msg}`;

View File

@@ -1,4 +0,0 @@
export default (string, n = 0) => {
n -= string.length;
return string + ' '.repeat(n > -1 ? n : 0);
};

View File

@@ -1,21 +1,21 @@
import chalk from 'chalk'; import chalk from 'chalk';
import { Route } from '@vercel/routing-utils';
const longestProperty = (routes, name) => { const longestProperty = (routes: Route[], name: keyof Route): number => {
const longestItem = routes.sort((a, b) => { const longestItem = routes.sort((a, b) => {
const firstItem = a[name] ? a[name].length : 0; const aName = a[name];
const secondItem = b[name] ? b[name].length : 0; const bName = b[name];
const firstItem = typeof aName === 'string' ? aName.length : 0;
const secondItem = typeof bName === 'string' ? bName.length : 0;
return secondItem - firstItem return secondItem - firstItem;
})[0]; })[0];
if (!longestItem[name]) { const val = longestItem[name];
return null; return typeof val === 'string' ? val.length : 0;
}
return longestItem[name].length;
}; };
export default routes => { export default function routes(routes: Route[]) {
let toPrint = ''; let toPrint = '';
const longestSrc = longestProperty(routes, 'src'); const longestSrc = longestProperty(routes, 'src');
@@ -24,20 +24,22 @@ export default routes => {
const padding = 6; const padding = 6;
const space = ' '.repeat(padding); const space = ' '.repeat(padding);
const destSpace = ' '.repeat(longestDest || 10); const destSpace = ' '.repeat(longestDest || 10);
const arrow = chalk.grey('->') const arrow = chalk.grey('->');
for (const item of routes) { for (const item of routes) {
if (item.handle) { if ('handle' in item) {
toPrint += `${chalk.grey('╶')} ${chalk.cyan(item.handle)}`; toPrint += `${chalk.grey('╶')} ${chalk.cyan(item.handle)}`;
continue; continue;
} }
const { src, dest, status, headers } = item; const { src, dest, status, headers } = item;
const last = routes.indexOf(item) === (routes.length - 1); const last = routes.indexOf(item) === routes.length - 1;
const suffix = last ? '' : `\n`; const suffix = last ? '' : `\n`;
const finalSrc = chalk.cyan(src.padEnd(longestSrc + padding)); const finalSrc = chalk.cyan(src.padEnd(longestSrc + padding));
const finalDest = dest ? `${arrow}${space}${dest}` : ` ${space}${destSpace}`; const finalDest = dest
? `${arrow}${space}${dest}`
: ` ${space}${destSpace}`;
const finalStatus = status ? chalk.grey(`[${status}]`) : ''; const finalStatus = status ? chalk.grey(`[${status}]`) : '';
let finalHeaders = null; let finalHeaders = null;
@@ -49,7 +51,7 @@ export default routes => {
for (const header of headerKeys) { for (const header of headerKeys) {
const value = headers[header]; const value = headers[header];
const last = headerKeys.indexOf(header) === (headerKeys.length - 1); const last = headerKeys.indexOf(header) === headerKeys.length - 1;
const suffix = last ? '' : `\n`; const suffix = last ? '' : `\n`;
const prefix = chalk.grey(last ? '└──' : '├──'); const prefix = chalk.grey(last ? '└──' : '├──');
@@ -64,4 +66,4 @@ export default routes => {
} }
return toPrint; return toPrint;
}; }

View File

@@ -1,10 +1,16 @@
import chalk from 'chalk'; import chalk from 'chalk';
const printLine = (data, sizes) => const printLine = (data: string[], sizes: number[]) =>
data.reduce((line, col, i) => line + col.padEnd(sizes[i]), ''); data.reduce((line, col, i) => line + col.padEnd(sizes[i]), '');
// Print a table /**
export default (fieldNames = [], data = [], margins = []) => { * Print a table.
*/
export default function table(
fieldNames: string[] = [],
data: string[][] = [],
margins: number[] = []
) {
// Compute size of each column // Compute size of each column
const sizes = data const sizes = data
.reduce( .reduce(
@@ -21,6 +27,9 @@ export default (fieldNames = [], data = [], margins = []) => {
// Print header // Print header
console.log(chalk.grey(printLine(fieldNames, sizes))); console.log(chalk.grey(printLine(fieldNames, sizes)));
// Print content // Print content
data.forEach(row => console.log(printLine(row, sizes))); for (const row of data) {
}; console.log(printLine(row, sizes));
}
}

View File

@@ -2,4 +2,4 @@ import chalk from 'chalk';
// Used for including uids in the output // Used for including uids in the output
// example: `(dom_ji13dj2fih4fi2hf)` // example: `(dom_ji13dj2fih4fi2hf)`
export default id => chalk.gray(`(${id})`); export default (id: string) => chalk.gray(`(${id})`);

View File

@@ -1,4 +1,4 @@
export default function parseMeta(meta) { export default function parseMeta(meta?: string | string[]) {
if (!meta) { if (!meta) {
return {}; return {};
} }
@@ -7,12 +7,12 @@ export default function parseMeta(meta) {
meta = [meta]; meta = [meta];
} }
const parsed = {}; const parsed: { [k: string]: string } = {};
meta.forEach(item => { for (const item of meta) {
const [key, ...rest] = item.split('='); const [key, ...rest] = item.split('=');
parsed[key] = rest.join('='); parsed[key] = rest.join('=');
}); }
return parsed; return parsed;
} }

View File

@@ -1,6 +1,4 @@
// @ts-ignore
import title from 'title'; import title from 'title';
import pkg from './pkg'; import pkg from './pkg';
import cmd from './output/cmd'; import cmd from './output/cmd';
@@ -8,9 +6,6 @@ import cmd from './output/cmd';
* Returns the package name such as `vercel` or `now`. * Returns the package name such as `vercel` or `now`.
*/ */
export function getPkgName(): string { export function getPkgName(): string {
if (!pkg.name) {
throw new Error('Expected `package.json` to have a `name` property.');
}
return pkg.name; return pkg.name;
} }

View File

@@ -1,50 +0,0 @@
import chalk from 'chalk';
export default promptOptions;
function promptOptions(opts) {
return new Promise((resolve, reject) => {
opts.forEach(([, text], i) => {
console.log(`${chalk.gray('>')} [${chalk.bold(i + 1)}] ${text}`);
});
const ondata = v => {
const s = v.toString();
const cleanup = () => {
if (process.stdin) {
if (process.stdin.setRawMode) {
process.stdin.setRawMode(false);
}
process.stdin.removeListener('data', ondata);
process.stdin.pause();
}
};
// Ctrl + C
if (s === '\u0003') {
cleanup();
const err = new Error('Aborted');
err.code = 'USER_ABORT';
return reject(err);
}
const n = Number(s);
if (opts[n - 1]) {
cleanup();
resolve(opts[n - 1][0]);
}
};
if (process.stdin) {
if (process.stdin.setRawMode) {
process.stdin.setRawMode(true);
}
process.stdin.resume();
process.stdin.on('data', ondata);
}
});
}

View File

@@ -1,8 +1,8 @@
export const maybeURL = id => export const maybeURL = (id: string): boolean =>
// E.g, "appname-asdf" // E.g, "appname-asdf"
id.includes('-'); id.includes('-');
export const normalizeURL = u => { export const normalizeURL = (u: string): string => {
// Normalize URL by removing slash from the end // Normalize URL by removing slash from the end
if (u.slice(-1) === '/') { if (u.slice(-1) === '/') {
u = u.slice(0, -1); u = u.slice(0, -1);

View File

@@ -1,13 +0,0 @@
import wait from './output/wait';
export default async function withSpinner<T>(msg: string, f: () => Promise<T>) {
const stopSpinner = wait(msg);
try {
const res = await f();
stopSpinner();
return res;
} catch (error) {
stopSpinner();
throw error;
}
}

View File

@@ -3,7 +3,7 @@
"builds": [ "builds": [
{ {
"src": "index.html", "src": "index.html",
"use": "https://files-1bsmqkkol.now.sh" "use": "https://files-17f5npcdc-testuser.vercel.app"
} }
] ]
} }

View File

@@ -258,7 +258,7 @@ function testFixtureStdio(
const projectJsonPath = join(cwd, '.vercel', 'project.json'); const projectJsonPath = join(cwd, '.vercel', 'project.json');
await fs.remove(projectJsonPath); await fs.remove(projectJsonPath);
const gitignore = join(cwd, '.gitignore'); const gitignore = join(cwd, '.gitignore');
const hasGitignore = await fs.exists(gitignore); const hasGitignore = await fs.pathExists(gitignore);
try { try {
// Run `vc link` // Run `vc link`
@@ -469,7 +469,7 @@ test('[vercel dev] `vercel.json` should be invalidated if deleted', async t => {
t.is(body.FOO, undefined); t.is(body.FOO, undefined);
} }
} finally { } finally {
await dev.kill('SIGTERM'); dev.kill('SIGTERM');
await fs.writeJSON(configPath, originalConfig); await fs.writeJSON(configPath, originalConfig);
} }
}); });
@@ -569,7 +569,7 @@ test('[vercel dev] reflects changes to config and env without restart', async t
t.is(body.query.foo, 'boo'); t.is(body.query.foo, 'boo');
} }
} finally { } finally {
await dev.kill('SIGTERM'); dev.kill('SIGTERM');
await fs.writeJSON(configPath, originalConfig); await fs.writeJSON(configPath, originalConfig);
} }
}); });
@@ -598,7 +598,7 @@ test('[vercel dev] `@vercel/node` TypeScript should be resolved by default', asy
const body = await res.text(); const body = await res.text();
t.is(body, 'world'); t.is(body, 'world');
} finally { } finally {
await dev.kill('SIGTERM'); dev.kill('SIGTERM');
await fs.remove(dir); await fs.remove(dir);
} }
}); });
@@ -848,7 +848,7 @@ test('[vercel dev] validate env var names', async t => {
t.pass(); t.pass();
} finally { } finally {
await dev.kill('SIGTERM'); dev.kill('SIGTERM');
} }
t.pass(); t.pass();
@@ -1431,7 +1431,7 @@ test(
test('[vercel dev] 24-ember', async t => { test('[vercel dev] 24-ember', async t => {
if (shouldSkip(t, '24-ember', '>^6.14.0 || ^8.10.0 || >=9.10.0')) return; if (shouldSkip(t, '24-ember', '>^6.14.0 || ^8.10.0 || >=9.10.0')) return;
const tester = await testFixtureStdio( const tester = testFixtureStdio(
'24-ember', '24-ember',
async testPath => { async testPath => {
await testPath(200, '/', /HelloWorld/m); await testPath(200, '/', /HelloWorld/m);
@@ -1539,7 +1539,7 @@ test('[vercel dev] no build matches warning', async t => {
t.pass(); t.pass();
} finally { } finally {
await dev.kill('SIGTERM'); dev.kill('SIGTERM');
} }
}); });
@@ -1577,7 +1577,7 @@ test('[vercel dev] render warning for empty cwd dir', async t => {
validateResponseHeaders(t, response); validateResponseHeaders(t, response);
t.is(response.status, 404); t.is(response.status, 404);
} finally { } finally {
await dev.kill('SIGTERM'); dev.kill('SIGTERM');
} }
}); });
@@ -1617,7 +1617,7 @@ test('[vercel dev] do not rebuild for changes in the output directory', async t
const text2 = await resp2.text(); const text2 = await resp2.text();
t.is(text2.trim(), 'hello second', stderr.join('')); t.is(text2.trim(), 'hello second', stderr.join(''));
} finally { } finally {
await dev.kill('SIGTERM'); dev.kill('SIGTERM');
} }
}); });

View File

@@ -2030,12 +2030,8 @@ test('initialize example "angular"', async t => {
cwd, cwd,
}); });
console.log(stderr);
console.log(stdout);
console.log(exitCode);
t.is(exitCode, 0, formatOutput({ stdout, stderr })); t.is(exitCode, 0, formatOutput({ stdout, stderr }));
t.true(stdout.includes(goal), formatOutput({ stdout, stderr })); t.true(stderr.includes(goal), formatOutput({ stdout, stderr }));
t.true( t.true(
verifyExampleAngular(cwd, 'angular'), verifyExampleAngular(cwd, 'angular'),
formatOutput({ stdout, stderr }) formatOutput({ stdout, stderr })
@@ -2054,13 +2050,9 @@ test('initialize example ("angular") to specified directory', async t => {
} }
); );
console.log(stderr); t.is(exitCode, 0, formatOutput({ stdout, stderr }));
console.log(stdout); t.true(stderr.includes(goal), formatOutput({ stdout, stderr }));
console.log(exitCode); t.true(verifyExampleAngular(cwd, 'ang'), formatOutput({ stdout, stderr }));
t.is(exitCode, 0);
t.true(stdout.includes(goal));
t.true(verifyExampleAngular(cwd, 'ang'));
}); });
test('initialize selected example ("amp")', async t => { test('initialize selected example ("amp")', async t => {
@@ -2073,12 +2065,8 @@ test('initialize selected example ("amp")', async t => {
input: '\n', input: '\n',
}); });
console.log(stderr);
console.log(stdout);
console.log(exitCode);
t.is(exitCode, 0, formatOutput({ stdout, stderr })); t.is(exitCode, 0, formatOutput({ stdout, stderr }));
t.true(stdout.includes(goal), formatOutput({ stdout, stderr })); t.true(stderr.includes(goal), formatOutput({ stdout, stderr }));
t.true(verifyExampleAmp(cwd, 'amp'), formatOutput({ stdout, stderr })); t.true(verifyExampleAmp(cwd, 'amp'), formatOutput({ stdout, stderr }));
}); });
@@ -2096,13 +2084,12 @@ test('initialize example to existing directory with "-f"', async t => {
} }
); );
console.log(stderr); t.is(exitCode, 0, formatOutput({ stdout, stderr }));
console.log(stdout); t.true(stderr.includes(goal), formatOutput({ stdout, stderr }));
console.log(exitCode); t.true(
verifyExampleAngular(cwd, 'angular'),
t.is(exitCode, 0); formatOutput({ stdout, stderr })
t.true(stdout.includes(goal), formatOutput({ stdout, stderr })); );
t.true(verifyExampleAngular(cwd, 'angular'));
}); });
test('try to initialize example to existing directory', async t => { test('try to initialize example to existing directory', async t => {
@@ -2118,12 +2105,8 @@ test('try to initialize example to existing directory', async t => {
input: '\n', input: '\n',
}); });
console.log(stderr); t.is(exitCode, 1, formatOutput({ stdout, stderr }));
console.log(stdout); t.true(stderr.includes(goal), formatOutput({ stdout, stderr }));
console.log(exitCode);
t.is(exitCode, 1);
t.true(stdout.includes(goal), formatOutput({ stdout, stderr }));
}); });
test('try to initialize misspelled example (noce) in non-tty', async t => { test('try to initialize misspelled example (noce) in non-tty', async t => {
@@ -2138,8 +2121,8 @@ test('try to initialize misspelled example (noce) in non-tty', async t => {
console.log(stdout); console.log(stdout);
console.log(exitCode); console.log(exitCode);
t.is(exitCode, 1); t.is(exitCode, 1, formatOutput({ stdout, stderr }));
t.true(stdout.includes(goal), formatOutput({ stdout, stderr })); t.true(stderr.includes(goal), formatOutput({ stdout, stderr }));
}); });
test('try to initialize example "example-404"', async t => { test('try to initialize example "example-404"', async t => {
@@ -2152,12 +2135,8 @@ test('try to initialize example "example-404"', async t => {
cwd, cwd,
}); });
console.log(stderr); t.is(exitCode, 1, formatOutput({ stdout, stderr }));
console.log(stdout); t.true(stderr.includes(goal), formatOutput({ stdout, stderr }));
console.log(exitCode);
t.is(exitCode, 1);
t.true(stdout.includes(goal), formatOutput({ stdout, stderr }));
}); });
test('try to revert a deployment and assign the automatic aliases', async t => { test('try to revert a deployment and assign the automatic aliases', async t => {
@@ -2170,10 +2149,11 @@ test('try to revert a deployment and assign the automatic aliases', async t => {
const url = `https://${name}.user.vercel.app`; const url = `https://${name}.user.vercel.app`;
{ {
const { stdout: deploymentUrl, stderr, exitCode } = await execute([ const {
firstDeployment, stdout: deploymentUrl,
'--confirm', stderr,
]); exitCode,
} = await execute([firstDeployment, '--confirm']);
t.is(exitCode, 0, formatOutput({ stderr, stdout: deploymentUrl })); t.is(exitCode, 0, formatOutput({ stderr, stdout: deploymentUrl }));
@@ -2190,10 +2170,11 @@ test('try to revert a deployment and assign the automatic aliases', async t => {
} }
{ {
const { stdout: deploymentUrl, stderr, exitCode } = await execute([ const {
secondDeployment, stdout: deploymentUrl,
'--confirm', stderr,
]); exitCode,
} = await execute([secondDeployment, '--confirm']);
t.is(exitCode, 0, formatOutput({ stderr, stdout: deploymentUrl })); t.is(exitCode, 0, formatOutput({ stderr, stdout: deploymentUrl }));
@@ -2212,10 +2193,11 @@ test('try to revert a deployment and assign the automatic aliases', async t => {
} }
{ {
const { stdout: deploymentUrl, stderr, exitCode } = await execute([ const {
firstDeployment, stdout: deploymentUrl,
'--confirm', stderr,
]); exitCode,
} = await execute([firstDeployment, '--confirm']);
t.is(exitCode, 0, formatOutput({ stderr, stdout: deploymentUrl })); t.is(exitCode, 0, formatOutput({ stderr, stdout: deploymentUrl }));
@@ -2559,15 +2541,25 @@ test('deploy a Lambda with 3 seconds of maxDuration', async t => {
t.is(output.exitCode, 0, formatOutput(output)); t.is(output.exitCode, 0, formatOutput(output));
const { host: url } = new URL(output.stdout); const url = new URL(output.stdout);
const [response1, response2] = await Promise.all([ // Should time out
fetch('https://' + url + '/api/wait-for/2'), url.pathname = '/api/wait-for/4';
fetch('https://' + url + '/api/wait-for/4'), const response1 = await fetch(url.href);
]); t.is(
response1.status,
504,
`Expected 504 status, got ${response1.status}: ${url}`
);
t.is(response1.status, 200, url); // Should not time out
t.is(response2.status, 504, url); url.pathname = '/api/wait-for/2';
const response2 = await fetch(url.href);
t.is(
response2.status,
200,
`Expected 200 status, got ${response1.status}: ${url}`
);
}); });
test('fail to deploy a Lambda with an incorrect value for maxDuration', async t => { test('fail to deploy a Lambda with an incorrect value for maxDuration', async t => {
@@ -3651,3 +3643,19 @@ test('[vc dev] should send the platform proxy request headers to frontend dev se
process.kill(dev.pid, 'SIGTERM'); process.kill(dev.pid, 'SIGTERM');
} }
}); });
test('[vc link] should support the `--project` flag', async t => {
const projectName = 'link-project-flag';
const directory = fixture('static-deployment');
const [user, output] = await Promise.all([
fetchTokenInformation(token),
execute(['link', '--confirm', '--project', projectName, directory]),
]);
t.is(output.exitCode, 0, formatOutput(output));
t.true(
output.stderr.includes(`Linked to ${user.username}/${projectName}`),
formatOutput(output)
);
});

View File

@@ -245,16 +245,7 @@ test('5xx response error with random JSON', async t => {
t.is(formatted.message, 'Failed to process data (500)'); t.is(formatted.message, 'Failed to process data (500)');
}); });
test('getProjectName with argv - option 1', t => { test('getProjectName with argv', t => {
const project = getProjectName({
argv: {
name: 'abc',
},
});
t.is(project, 'abc');
});
test('getProjectName with argv - option 2', t => {
const project = getProjectName({ const project = getProjectName({
argv: { argv: {
'--name': 'abc', '--name': 'abc',

View File

@@ -11,6 +11,7 @@
"lib": ["esnext"], "lib": ["esnext"],
"resolveJsonModule": true, "resolveJsonModule": true,
"sourceMap": true, "sourceMap": true,
"outDir": "./dist",
"typeRoots": ["./@types", "./node_modules/@types"] "typeRoots": ["./@types", "./node_modules/@types"]
}, },
"include": ["src/**/*"] "include": ["src/**/*"]

View File

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

View File

@@ -1,4 +1,4 @@
import { readdir as readRootFolder, lstatSync } from 'fs-extra'; import { lstatSync } from 'fs-extra';
import { relative, isAbsolute } from 'path'; import { relative, isAbsolute } from 'path';
import hashes, { mapToObject } from './utils/hashes'; import hashes, { mapToObject } from './utils/hashes';
@@ -50,8 +50,6 @@ export default function buildCreateDeployment() {
clientOptions.isDirectory = clientOptions.isDirectory =
!Array.isArray(path) && lstatSync(path).isDirectory(); !Array.isArray(path) && lstatSync(path).isDirectory();
let rootFiles: string[];
if (Array.isArray(path)) { if (Array.isArray(path)) {
for (const filePath of path) { for (const filePath of path) {
if (!isAbsolute(filePath)) { if (!isAbsolute(filePath)) {
@@ -69,15 +67,11 @@ export default function buildCreateDeployment() {
} }
if (clientOptions.isDirectory && !Array.isArray(path)) { if (clientOptions.isDirectory && !Array.isArray(path)) {
debug(`Provided 'path' is a directory. Reading subpaths... `); debug(`Provided 'path' is a directory.`);
rootFiles = await readRootFolder(path);
debug(`Read ${rootFiles.length} subpaths`);
} else if (Array.isArray(path)) { } else if (Array.isArray(path)) {
debug(`Provided 'path' is an array of file paths`); debug(`Provided 'path' is an array of file paths`);
rootFiles = path;
} else { } else {
debug(`Provided 'path' is a single file`); debug(`Provided 'path' is a single file`);
rootFiles = [path];
} }
let { fileList } = await buildFileTree( let { fileList } = await buildFileTree(

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/frameworks", "name": "@vercel/frameworks",
"version": "0.5.0", "version": "0.5.1-canary.1",
"main": "./dist/frameworks.js", "main": "./dist/frameworks.js",
"types": "./dist/frameworks.d.ts", "types": "./dist/frameworks.d.ts",
"files": [ "files": [
@@ -22,8 +22,6 @@
"@types/node-fetch": "2.5.8", "@types/node-fetch": "2.5.8",
"@vercel/routing-utils": "1.11.3", "@vercel/routing-utils": "1.11.3",
"ajv": "6.12.2", "ajv": "6.12.2",
"jest": "24.9.0",
"ts-jest": "24.1.0",
"typescript": "4.3.4" "typescript": "4.3.4"
} }
} }

View File

@@ -6,9 +6,7 @@ import { readConfigFile } from './read-config-file';
export * from './types'; export * from './types';
const { readdir, stat, readFile, unlink } = promises; const { readdir, readFile, unlink } = promises;
const isDir = async (file: string): Promise<boolean> =>
(await stat(file)).isDirectory();
/** /**
* Please note that is extremely important that the `dependency` property needs * Please note that is extremely important that the `dependency` property needs
@@ -313,11 +311,11 @@ export const frameworks = [
const base = 'build'; const base = 'build';
try { try {
const location = join(dirPrefix, base); const location = join(dirPrefix, base);
const content = await readdir(location); const content = await readdir(location, { withFileTypes: true });
// If there is only one file in it that is a dir we'll use it as dist dir // If there is only one file in it that is a dir we'll use it as dist dir
if (content.length === 1 && (await isDir(join(location, content[0])))) { if (content.length === 1 && content[0].isDirectory()) {
return join(base, content[0]); return join(base, content[0].name);
} }
} catch (error) { } catch (error) {
console.error(`Error detecting output directory: `, error); console.error(`Error detecting output directory: `, error);
@@ -402,11 +400,11 @@ export const frameworks = [
const base = 'build'; const base = 'build';
try { try {
const location = join(dirPrefix, base); const location = join(dirPrefix, base);
const content = await readdir(location); const content = await readdir(location, { withFileTypes: true });
// If there is only one file in it that is a dir we'll use it as dist dir // If there is only one file in it that is a dir we'll use it as dist dir
if (content.length === 1 && (await isDir(join(location, content[0])))) { if (content.length === 1 && content[0].isDirectory()) {
return join(base, content[0]); return join(base, content[0].name);
} }
} catch (error) { } catch (error) {
console.error(`Error detecting output directory: `, error); console.error(`Error detecting output directory: `, error);
@@ -744,11 +742,11 @@ export const frameworks = [
const base = 'dist'; const base = 'dist';
try { try {
const location = join(dirPrefix, base); const location = join(dirPrefix, base);
const content = await readdir(location); const content = await readdir(location, { withFileTypes: true });
// If there is only one file in it that is a dir we'll use it as dist dir // If there is only one file in it that is a dir we'll use it as dist dir
if (content.length === 1 && (await isDir(join(location, content[0])))) { if (content.length === 1 && content[0].isDirectory()) {
return join(base, content[0]); return join(base, content[0].name);
} }
} catch (error) { } catch (error) {
console.error(`Error detecting output directory: `, error); console.error(`Error detecting output directory: `, error);
@@ -830,8 +828,7 @@ export const frameworks = [
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/svelte.svg', logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/svelte.svg',
tagline: tagline:
'Svelte lets you write high performance reactive apps with significantly less boilerplate.', 'Svelte lets you write high performance reactive apps with significantly less boilerplate.',
description: description: 'A basic Svelte app using the default template.',
'A basic Svelte app using the default template.',
website: 'https://svelte.dev', website: 'https://svelte.dev',
detectors: { detectors: {
every: [ every: [
@@ -882,8 +879,7 @@ export const frameworks = [
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/svelte.svg', logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/svelte.svg',
tagline: tagline:
'SvelteKit is a framework for building web applications of all sizes.', 'SvelteKit is a framework for building web applications of all sizes.',
description: description: 'A SvelteKit app optimized to work for serverless.',
'A SvelteKit app optimized to work for serverless.',
website: 'https://kit.svelte.dev', website: 'https://kit.svelte.dev',
detectors: { detectors: {
every: [ every: [

View File

@@ -1,10 +0,0 @@
let buildUtils: typeof import('@vercel/build-utils');
try {
buildUtils = require('@vercel/build-utils');
} catch (e) {
// Fallback for older CLI versions
buildUtils = require('@now/build-utils');
}
export default buildUtils;

View File

@@ -12,16 +12,7 @@ async function main() {
// Build with `ncc` // Build with `ncc`
await execa( await execa(
'ncc', 'ncc',
[ ['build', 'index.ts', '-e', '@vercel/build-utils', '-o', outDir],
'build',
'index.ts',
'-e',
'@vercel/build-utils',
'-e',
'@now/build-utils',
'-o',
outDir,
],
{ {
stdio: 'inherit', stdio: 'inherit',
} }

View File

@@ -3,9 +3,8 @@ import execa from 'execa';
import fetch from 'node-fetch'; import fetch from 'node-fetch';
import { mkdirp, pathExists, readFile } from 'fs-extra'; import { mkdirp, pathExists, readFile } from 'fs-extra';
import { join } from 'path'; import { join } from 'path';
import buildUtils from './build-utils';
import stringArgv from 'string-argv'; import stringArgv from 'string-argv';
const { debug } = buildUtils; import { debug } from '@vercel/build-utils';
const versionMap = new Map([ const versionMap = new Map([
['1.16', '1.16'], ['1.16', '1.16'],
['1.15', '1.15.8'], ['1.15', '1.15.8'],

View File

@@ -20,17 +20,13 @@ import {
PrepareCacheOptions, PrepareCacheOptions,
StartDevServerOptions, StartDevServerOptions,
StartDevServerResult, StartDevServerResult,
} from '@vercel/build-utils';
import buildUtils from './build-utils';
const {
glob, glob,
download, download,
createLambda, createLambda,
getWriteableDirectory, getWriteableDirectory,
shouldServe, shouldServe,
debug, debug,
} = buildUtils; } from '@vercel/build-utils';
const TMP = tmpdir(); const TMP = tmpdir();

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/go", "name": "@vercel/go",
"version": "1.2.3", "version": "1.2.4-canary.0",
"license": "MIT", "license": "MIT",
"main": "./dist/index", "main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go", "homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",

View File

@@ -155,7 +155,8 @@ class Bridge {
const { port } = await this.listening; const { port } = await this.listening;
const normalizedEvent = normalizeEvent(event); const normalizedEvent = normalizeEvent(event);
const { isApiGateway, method, path, headers, body } = normalizedEvent; const { isApiGateway, method, headers, body } = normalizedEvent;
let { path } = normalizedEvent;
if (this.shouldStoreEvents) { if (this.shouldStoreEvents) {
const reqId = `${this.reqIdSeed++}`; const reqId = `${this.reqIdSeed++}`;
@@ -165,6 +166,12 @@ class Bridge {
// eslint-disable-next-line consistent-return // eslint-disable-next-line consistent-return
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// if the path is improperly encoded we need to encode it or
// http.request will throw an error (related check: https://github.com/nodejs/node/blob/4ece669c6205ec78abfdadfe78869bbb8411463e/lib/_http_client.js#L84)
if (path && /[^\u0021-\u00ff]/.test(path)) {
path = encodeURI(path);
}
const opts = { hostname: '127.0.0.1', port, path, method }; const opts = { hostname: '127.0.0.1', port, path, method };
const req = request(opts, res => { const req = request(opts, res => {
const response = res; const response = res;

View File

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

View File

@@ -122,6 +122,44 @@ test('consumeEvent', async () => {
server.close(); server.close();
}); });
test('consumeEvent and handle decoded path', async () => {
const mockListener = jest.fn((req, res) => {
res.end('hello');
});
const server = new Server(mockListener);
const bridge = new Bridge(server, true);
bridge.listen();
const context = { callbackWaitsForEmptyEventLoop: true };
await bridge.launcher(
{
Action: 'Invoke',
body: JSON.stringify({
method: 'POST',
headers: { foo: 'baz' },
path: '/now proxy',
body: 'body=1',
}),
},
context
);
const headers = mockListener.mock.calls[0][0].headers;
const reqId = headers['x-now-bridge-request-id'];
expect(reqId).toBeTruthy();
const event = bridge.consumeEvent(reqId);
expect(event.body.toString()).toBe('body=1');
// an event can't be consumed multiple times
// to avoid memory leaks
expect(bridge.consumeEvent(reqId)).toBeUndefined();
server.close();
});
test('invalid request headers', async () => { test('invalid request headers', async () => {
const server = new Server((req, res) => const server = new Server((req, res) =>
res.end( res.end(

View File

@@ -53,8 +53,6 @@ async function main() {
'@vercel/node-bridge', '@vercel/node-bridge',
'-e', '-e',
'@vercel/build-utils', '@vercel/build-utils',
'-e',
'@now/build-utils',
'-o', '-o',
helpersDir, helpersDir,
], ],
@@ -74,8 +72,6 @@ async function main() {
'@vercel/node-bridge', '@vercel/node-bridge',
'-e', '-e',
'@vercel/build-utils', '@vercel/build-utils',
'-e',
'@now/build-utils',
'-o', '-o',
sourceMapSupportDir, sourceMapSupportDir,
], ],
@@ -98,8 +94,6 @@ async function main() {
'-e', '-e',
'@vercel/build-utils', '@vercel/build-utils',
'-e', '-e',
'@now/build-utils',
'-e',
'typescript', 'typescript',
'-o', '-o',
mainDir, mainDir,

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/node", "name": "@vercel/node",
"version": "1.12.0", "version": "1.12.2-canary.0",
"license": "MIT", "license": "MIT",
"main": "./dist/index", "main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js", "homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
@@ -34,7 +34,7 @@
"@types/test-listen": "1.1.0", "@types/test-listen": "1.1.0",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"@vercel/nft": "0.13.1", "@vercel/nft": "0.13.1",
"@vercel/node-bridge": "2.1.0", "@vercel/node-bridge": "2.1.1-canary.0",
"content-type": "1.0.4", "content-type": "1.0.4",
"cookie": "0.4.0", "cookie": "0.4.0",
"etag": "1.8.1", "etag": "1.8.1",

View File

@@ -1,10 +0,0 @@
let buildUtils: typeof import('@vercel/build-utils');
try {
buildUtils = require('@vercel/build-utils');
} catch (e) {
// Fallback for older CLI versions
buildUtils = require('@now/build-utils');
}
export default buildUtils;

View File

@@ -20,7 +20,6 @@ import {
import mkdirp from 'mkdirp-promise'; import mkdirp from 'mkdirp-promise';
import once from '@tootallnate/once'; import once from '@tootallnate/once';
import { nodeFileTrace } from '@vercel/nft'; import { nodeFileTrace } from '@vercel/nft';
import buildUtils from './build-utils';
import { import {
File, File,
Files, Files,
@@ -30,8 +29,6 @@ import {
Config, Config,
StartDevServerOptions, StartDevServerOptions,
StartDevServerResult, StartDevServerResult,
} from '@vercel/build-utils';
const {
glob, glob,
download, download,
FileBlob, FileBlob,
@@ -45,7 +42,7 @@ const {
debug, debug,
isSymbolicLink, isSymbolicLink,
walkParentDirs, walkParentDirs,
} = buildUtils; } from '@vercel/build-utils';
// @ts-ignore - copied to the `dist` output as-is // @ts-ignore - copied to the `dist` output as-is
import { makeVercelLauncher, makeAwsLauncher } from './launcher.js'; import { makeVercelLauncher, makeAwsLauncher } from './launcher.js';
@@ -112,7 +109,7 @@ async function downloadInstallAndBundle({
} else { } else {
const installTime = Date.now(); const installTime = Date.now();
console.log('Installing dependencies...'); console.log('Installing dependencies...');
await runNpmInstall(entrypointFsDirname, [], spawnOpts, meta); await runNpmInstall(entrypointFsDirname, [], spawnOpts, meta, nodeVersion);
debug(`Install complete [${Date.now() - installTime}ms]`); debug(`Install complete [${Date.now() - installTime}ms]`);
} }
@@ -360,18 +357,14 @@ export async function build({
const baseDir = repoRootPath || workPath; const baseDir = repoRootPath || workPath;
const awsLambdaHandler = getAWSLambdaHandler(entrypoint, config); const awsLambdaHandler = getAWSLambdaHandler(entrypoint, config);
const { const { entrypointPath, entrypointFsDirname, nodeVersion, spawnOpts } =
entrypointPath, await downloadInstallAndBundle({
entrypointFsDirname, files,
nodeVersion, entrypoint,
spawnOpts, workPath,
} = await downloadInstallAndBundle({ config,
files, meta,
entrypoint, });
workPath,
config,
meta,
});
await runPackageJsonScript( await runPackageJsonScript(
entrypointFsDirname, entrypointFsDirname,

View File

@@ -28,7 +28,7 @@ export type NowRequestCookies = VercelRequestCookies;
/** @deprecated Use VercelRequestQuery instead. */ /** @deprecated Use VercelRequestQuery instead. */
export type NowRequestQuery = VercelRequestQuery; export type NowRequestQuery = VercelRequestQuery;
/** @deprecated Use `any` instead. */ /** @deprecated Use VercelRequestBody instead. */
export type NowRequestBody = any; export type NowRequestBody = any;
/** @deprecated Use VercelRequest instead. */ /** @deprecated Use VercelRequest instead. */

View File

@@ -1,7 +1,6 @@
import { relative, basename, resolve, dirname } from 'path';
import _ts from 'typescript'; import _ts from 'typescript';
import buildUtils from './build-utils'; import { NowBuildError } from '@vercel/build-utils';
const { NowBuildError } = buildUtils; import { relative, basename, resolve, dirname } from 'path';
/* /*
* Fork of TS-Node - https://github.com/TypeStrong/ts-node * Fork of TS-Node - https://github.com/TypeStrong/ts-node

View File

@@ -16,8 +16,6 @@ async function main() {
join(__dirname, 'src/index.ts'), join(__dirname, 'src/index.ts'),
'-e', '-e',
'@vercel/build-utils', '@vercel/build-utils',
'-e',
'@now/build-utils',
'-o', '-o',
outDir, outDir,
], ],

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/python", "name": "@vercel/python",
"version": "2.0.5", "version": "2.0.6-canary.0",
"main": "./dist/index.js", "main": "./dist/index.js",
"license": "MIT", "license": "MIT",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python", "homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",

View File

@@ -1,10 +0,0 @@
let buildUtils: typeof import('@vercel/build-utils');
try {
buildUtils = require('@vercel/build-utils');
} catch (e) {
// Fallback for older CLI versions
buildUtils = require('@now/build-utils');
}
export default buildUtils;

View File

@@ -4,16 +4,16 @@ import fs from 'fs';
import { promisify } from 'util'; import { promisify } from 'util';
const readFile = promisify(fs.readFile); const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile); const writeFile = promisify(fs.writeFile);
import buildUtils from './build-utils'; import {
import { GlobOptions, BuildOptions } from '@vercel/build-utils'; GlobOptions,
const { BuildOptions,
getWriteableDirectory, getWriteableDirectory,
download, download,
glob, glob,
createLambda, createLambda,
shouldServe, shouldServe,
debug, debug,
} = buildUtils; } from '@vercel/build-utils';
import { installRequirement, installRequirementsFile } from './install'; import { installRequirement, installRequirementsFile } from './install';
async function pipenvConvert(cmd: string, srcDir: string) { async function pipenvConvert(cmd: string, srcDir: string) {

View File

@@ -1,7 +1,5 @@
import execa from 'execa'; import execa from 'execa';
import { Meta } from '@vercel/build-utils'; import { Meta, debug } from '@vercel/build-utils';
import buildUtils from './build-utils';
const { debug } = buildUtils;
const pipPath = 'pip3'; const pipPath = 'pip3';
const makeDependencyCheckCode = (dependency: string) => ` const makeDependencyCheckCode = (dependency: string) => `

View File

@@ -1,10 +0,0 @@
let buildUtils: typeof import('@vercel/build-utils');
try {
buildUtils = require('@vercel/build-utils');
} catch (e) {
// Fallback for older CLI versions
buildUtils = require('@now/build-utils');
}
export default buildUtils;

Some files were not shown because too many files have changed in this diff Show More