mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-11 21:07:47 +00:00
Compare commits
57 Commits
@vercel/py
...
@vercel/py
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e1d708e3f | ||
|
|
38503103c3 | ||
|
|
e8fec4b69c | ||
|
|
b3ffcdf80d | ||
|
|
43c1a93c1d | ||
|
|
5b118fd4e6 | ||
|
|
8916b674af | ||
|
|
1807f83c69 | ||
|
|
74e8ec7c64 | ||
|
|
2644e3127b | ||
|
|
d77ac04b0c | ||
|
|
0ef9c8df4d | ||
|
|
dfc4c98820 | ||
|
|
0e51884725 | ||
|
|
1b264fe60e | ||
|
|
f18bca9718 | ||
|
|
c23dc73f41 | ||
|
|
273718e0b7 | ||
|
|
230b88bf9b | ||
|
|
676a3d2568 | ||
|
|
f221f041d0 | ||
|
|
aca42b2aac | ||
|
|
cf11a8efb5 | ||
|
|
be09349daf | ||
|
|
a01372bcbb | ||
|
|
b941715d7b | ||
|
|
ee9a8a0415 | ||
|
|
2ad27eefb0 | ||
|
|
578fe8a930 | ||
|
|
04ea3bb88d | ||
|
|
46116022b7 | ||
|
|
f80539df82 | ||
|
|
daf06307b4 | ||
|
|
0bd028cd84 | ||
|
|
1c48030e1e | ||
|
|
1dc05428d7 | ||
|
|
288dca045c | ||
|
|
8c5bc04fde | ||
|
|
a07e6fc103 | ||
|
|
9af3054d41 | ||
|
|
9fb254e14a | ||
|
|
3616bdf17a | ||
|
|
52a89fd4b7 | ||
|
|
d4db6635f1 | ||
|
|
f1009a80cd | ||
|
|
2756d1e323 | ||
|
|
5b61b16bd1 | ||
|
|
41c61f8f8b | ||
|
|
6c52e1fad7 | ||
|
|
d2e82fdc3a | ||
|
|
a60b1b225b | ||
|
|
18bec983ae | ||
|
|
e6fb2ffe05 | ||
|
|
0533cfd566 | ||
|
|
3db8618885 | ||
|
|
4722ea5ad6 | ||
|
|
c7bd6f3266 |
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,4 +1,4 @@
|
||||
blank_issues_enabled: false
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Bug Report
|
||||
url: https://vercel.com/support/request
|
||||
|
||||
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
@@ -29,6 +29,6 @@ jobs:
|
||||
- name: Publish
|
||||
run: yarn publish-from-github
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }}
|
||||
GA_TRACKING_ID: ${{ secrets.GA_TRACKING_ID }}
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
|
||||
## 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).
|
||||
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
* Get example list from extracted folder
|
||||
*/
|
||||
|
||||
import { join } from 'path';
|
||||
import { lstatSync, existsSync, readdirSync } from 'fs';
|
||||
|
||||
const exists = (path: string) => existsSync(path);
|
||||
const isDotFile = (name: string) => name.startsWith('.');
|
||||
const isDirectory = (path: string) => lstatSync(path).isDirectory();
|
||||
|
||||
export function summary(source: string) {
|
||||
export function summary(source: string): string[] {
|
||||
if (!exists(source) || !isDirectory(source)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return readdirSync(source)
|
||||
.filter(name => !isDotFile(name))
|
||||
.filter(name => isDirectory(join(source, name)));
|
||||
return readdirSync(source, { withFileTypes: true })
|
||||
.filter(d => !isDotFile(d.name))
|
||||
.filter(d => d.isDirectory())
|
||||
.map(d => d.name);
|
||||
}
|
||||
|
||||
@@ -6,3 +6,5 @@ coverage:
|
||||
project: off
|
||||
patch: off
|
||||
|
||||
fixes:
|
||||
- "::packages/cli/" # move root e.g., "path/" => "after/path/"
|
||||
|
||||
13218
examples/angular/package-lock.json
generated
Normal file
13218
examples/angular/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -12,37 +12,38 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~8.1.0",
|
||||
"@angular/common": "~8.1.0",
|
||||
"@angular/compiler": "~8.1.0",
|
||||
"@angular/core": "~8.1.0",
|
||||
"@angular/forms": "~8.1.0",
|
||||
"@angular/platform-browser": "~8.1.0",
|
||||
"@angular/platform-browser-dynamic": "~8.1.0",
|
||||
"@angular/router": "~8.1.0",
|
||||
"@angular/animations": "^8.1.0",
|
||||
"@angular/common": "^8.1.0",
|
||||
"@angular/core": "^8.1.0",
|
||||
"@angular/forms": "^8.1.0",
|
||||
"@angular/platform-browser": "^8.1.0",
|
||||
"@angular/platform-browser-dynamic": "^8.1.0",
|
||||
"@angular/router": "^8.1.0",
|
||||
"rxjs": "~6.4.0",
|
||||
"tslib": "^1.9.0",
|
||||
"zone.js": "~0.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.801.0",
|
||||
"@angular/cli": "~8.1.0",
|
||||
"@angular/compiler-cli": "~8.1.0",
|
||||
"@angular-devkit/build-angular": "^12.2.2",
|
||||
"@angular/cli": "^12.2.2",
|
||||
"@angular/compiler": "^12.2.2",
|
||||
"@angular/compiler-cli": "^12.2.2",
|
||||
"@angular/language-service": "~8.1.0",
|
||||
"@types/node": "~8.9.4",
|
||||
"@types/jasmine": "~3.3.8",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "~8.9.4",
|
||||
"codelyzer": "^5.0.0",
|
||||
"jasmine-core": "~3.4.0",
|
||||
"glob-parent": "^5.1.2",
|
||||
"jasmine-core": "^3.4.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "~4.1.0",
|
||||
"karma": "^6.3.4",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.0.1",
|
||||
"karma-jasmine": "~2.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.0",
|
||||
"protractor": "~5.4.0",
|
||||
"protractor": "^7.0.0",
|
||||
"ts-node": "~7.0.0",
|
||||
"tslint": "~5.15.0",
|
||||
"typescript": "~3.4.3"
|
||||
"typescript": "^4.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
||||
"dependencies": {
|
||||
"react": "^16.6.1",
|
||||
"react-dom": "^16.6.1",
|
||||
"react-scripts": "2.1.1"
|
||||
"react-scripts": "^4.0.3"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "BROWSER=none react-scripts start",
|
||||
|
||||
3
examples/nextjs/.eslintrc.json
Normal file
3
examples/nextjs/.eslintrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
@@ -29,6 +29,6 @@ You can check out [the Next.js GitHub repository](https://github.com/vercel/next
|
||||
|
||||
## 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.
|
||||
|
||||
3
examples/nextjs/next.config.js
Normal file
3
examples/nextjs/next.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
reactStrictMode: true,
|
||||
}
|
||||
@@ -1,15 +1,20 @@
|
||||
{
|
||||
"name": "nextjs",
|
||||
"version": "0.1.0",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "10.x",
|
||||
"react": "17.x",
|
||||
"react-dom": "17.x"
|
||||
"next": "11.1.2",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "7.32.0",
|
||||
"eslint-config-next": "11.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
|
||||
export default (req, res) => {
|
||||
res.statusCode = 200
|
||||
res.json({ name: 'John Doe' })
|
||||
export default function handler(req, res) {
|
||||
res.status(200).json({ name: 'John Doe' })
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Head from 'next/head'
|
||||
import Image from 'next/image'
|
||||
import styles from '../styles/Home.module.css'
|
||||
|
||||
export default function Home() {
|
||||
@@ -6,6 +7,7 @@ export default function Home() {
|
||||
<div className={styles.container}>
|
||||
<Head>
|
||||
<title>Create Next App</title>
|
||||
<meta name="description" content="Generated by create next app" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
@@ -21,12 +23,12 @@ export default function Home() {
|
||||
|
||||
<div className={styles.grid}>
|
||||
<a href="https://nextjs.org/docs" className={styles.card}>
|
||||
<h3>Documentation →</h3>
|
||||
<h2>Documentation →</h2>
|
||||
<p>Find in-depth information about Next.js features and API.</p>
|
||||
</a>
|
||||
|
||||
<a href="https://nextjs.org/learn" className={styles.card}>
|
||||
<h3>Learn →</h3>
|
||||
<h2>Learn →</h2>
|
||||
<p>Learn about Next.js in an interactive course with quizzes!</p>
|
||||
</a>
|
||||
|
||||
@@ -34,15 +36,15 @@ export default function Home() {
|
||||
href="https://github.com/vercel/next.js/tree/master/examples"
|
||||
className={styles.card}
|
||||
>
|
||||
<h3>Examples →</h3>
|
||||
<h2>Examples →</h2>
|
||||
<p>Discover and deploy boilerplate example Next.js projects.</p>
|
||||
</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}
|
||||
>
|
||||
<h3>Deploy →</h3>
|
||||
<h2>Deploy →</h2>
|
||||
<p>
|
||||
Instantly deploy your Next.js site to a public URL with Vercel.
|
||||
</p>
|
||||
@@ -57,7 +59,9 @@ export default function Home() {
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
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>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.main {
|
||||
@@ -25,14 +26,11 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer img {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.title a {
|
||||
@@ -82,7 +80,6 @@
|
||||
|
||||
.card {
|
||||
margin: 1rem;
|
||||
flex-basis: 45%;
|
||||
padding: 1.5rem;
|
||||
text-align: left;
|
||||
color: inherit;
|
||||
@@ -90,6 +87,7 @@
|
||||
border: 1px solid #eaeaea;
|
||||
border-radius: 10px;
|
||||
transition: color 0.15s ease, border-color 0.15s ease;
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
.card:hover,
|
||||
@@ -99,7 +97,7 @@
|
||||
border-color: #0070f3;
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
.card h2 {
|
||||
margin: 0 0 1rem 0;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
@@ -112,6 +110,7 @@
|
||||
|
||||
.logo {
|
||||
height: 1em;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,6 @@
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "4.28.0",
|
||||
"@typescript-eslint/parser": "4.28.0",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"async-retry": "1.2.3",
|
||||
"buffer-replace": "1.0.0",
|
||||
"cheerio": "1.0.0-rc.3",
|
||||
@@ -25,17 +24,19 @@
|
||||
"eslint-config-prettier": "8.3.0",
|
||||
"eslint-plugin-jest": "24.3.6",
|
||||
"husky": "6.0.0",
|
||||
"jest": "27.0.6",
|
||||
"json5": "2.1.1",
|
||||
"lint-staged": "9.2.5",
|
||||
"node-fetch": "2.6.1",
|
||||
"npm-package-arg": "6.1.0",
|
||||
"prettier": "2.3.1"
|
||||
"prettier": "2.3.1",
|
||||
"ts-jest": "27.0.4"
|
||||
},
|
||||
"scripts": {
|
||||
"lerna": "lerna",
|
||||
"bootstrap": "lerna bootstrap",
|
||||
"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",
|
||||
"changelog": "node utils/changelog.js",
|
||||
"build": "node utils/run.js build all",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "2.12.0",
|
||||
"version": "2.12.3-canary.3",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
@@ -23,15 +23,15 @@
|
||||
"@types/end-of-stream": "^1.4.0",
|
||||
"@types/fs-extra": "^5.0.5",
|
||||
"@types/glob": "^7.1.1",
|
||||
"@types/jest": "26.0.24",
|
||||
"@types/jest": "27.0.1",
|
||||
"@types/js-yaml": "3.12.1",
|
||||
"@types/ms": "0.7.31",
|
||||
"@types/multistream": "2.1.1",
|
||||
"@types/node-fetch": "^2.1.6",
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/yazl": "^2.4.1",
|
||||
"@vercel/frameworks": "0.5.0",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/frameworks": "0.5.1-canary.3",
|
||||
"@vercel/ncc": "0.29.2",
|
||||
"aggregate-error": "3.0.1",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "2.1.4",
|
||||
@@ -41,13 +41,11 @@
|
||||
"fs-extra": "7.0.0",
|
||||
"glob": "7.1.3",
|
||||
"into-stream": "5.0.0",
|
||||
"jest": "27.0.6",
|
||||
"js-yaml": "3.13.1",
|
||||
"minimatch": "3.0.4",
|
||||
"multistream": "2.1.1",
|
||||
"node-fetch": "2.6.1",
|
||||
"semver": "6.1.1",
|
||||
"ts-jest": "27.0.4",
|
||||
"typescript": "4.3.4",
|
||||
"yazl": "2.4.3"
|
||||
}
|
||||
|
||||
@@ -1030,7 +1030,7 @@ function getRouteResult(
|
||||
// https://nextjs.org/docs/advanced-features/custom-error-page
|
||||
errorRoutes.push({
|
||||
status: 404,
|
||||
src: '^/(?!.*api).*$',
|
||||
src: '^(?!/api).*$',
|
||||
dest: options.cleanUrls ? '/404' : '/404.html',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -100,4 +100,4 @@ class FileFsRef implements File {
|
||||
}
|
||||
}
|
||||
|
||||
export = FileFsRef;
|
||||
export default FileFsRef;
|
||||
|
||||
@@ -244,10 +244,13 @@ export async function scanParentDirs(
|
||||
const packageJsonPath = path.join(currentDestPath, 'package.json');
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
if (await fs.pathExists(packageJsonPath)) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
if (readPackageJson) {
|
||||
// Only read the contents of the *first* `package.json` file found,
|
||||
// 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'));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const [packageLockJson, hasYarnLock] = await Promise.all([
|
||||
fs
|
||||
@@ -256,9 +259,8 @@ export async function scanParentDirs(
|
||||
// If the file doesn't exist, fail gracefully otherwise error
|
||||
if (error.code === 'ENOENT') {
|
||||
return null;
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
throw error;
|
||||
}),
|
||||
fs.pathExists(path.join(currentDestPath, 'yarn.lock')),
|
||||
]);
|
||||
@@ -267,7 +269,13 @@ export async function scanParentDirs(
|
||||
cliType = 'npm';
|
||||
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);
|
||||
@@ -305,7 +313,8 @@ export async function runNpmInstall(
|
||||
destPath: string,
|
||||
args: string[] = [],
|
||||
spawnOpts?: SpawnOptions,
|
||||
meta?: Meta
|
||||
meta?: Meta,
|
||||
nodeVersion?: NodeVersion
|
||||
) {
|
||||
if (meta?.isDev) {
|
||||
debug('Skipping dependency installation because dev mode is enabled');
|
||||
@@ -329,7 +338,12 @@ export async function runNpmInstall(
|
||||
.filter(a => a !== '--prefer-offline')
|
||||
.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`
|
||||
env.PATH = `/node16/bin-npm7:${env.PATH}`;
|
||||
console.log('Detected `package-lock.json` generated by npm 7...');
|
||||
|
||||
@@ -19,6 +19,7 @@ interface LambdaOptions {
|
||||
memory?: number;
|
||||
maxDuration?: number;
|
||||
environment: Environment;
|
||||
allowQuery?: string[];
|
||||
}
|
||||
|
||||
interface CreateLambdaOptions {
|
||||
@@ -28,6 +29,7 @@ interface CreateLambdaOptions {
|
||||
memory?: number;
|
||||
maxDuration?: number;
|
||||
environment?: Environment;
|
||||
allowQuery?: string[];
|
||||
}
|
||||
|
||||
interface GetLambdaOptionsFromFunctionOptions {
|
||||
@@ -43,6 +45,7 @@ export class Lambda {
|
||||
public memory?: number;
|
||||
public maxDuration?: number;
|
||||
public environment: Environment;
|
||||
public allowQuery?: string[];
|
||||
|
||||
constructor({
|
||||
zipBuffer,
|
||||
@@ -51,6 +54,7 @@ export class Lambda {
|
||||
maxDuration,
|
||||
memory,
|
||||
environment,
|
||||
allowQuery,
|
||||
}: LambdaOptions) {
|
||||
this.type = 'Lambda';
|
||||
this.zipBuffer = zipBuffer;
|
||||
@@ -59,6 +63,7 @@ export class Lambda {
|
||||
this.memory = memory;
|
||||
this.maxDuration = maxDuration;
|
||||
this.environment = environment;
|
||||
this.allowQuery = allowQuery;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +77,7 @@ export async function createLambda({
|
||||
memory,
|
||||
maxDuration,
|
||||
environment = {},
|
||||
allowQuery,
|
||||
}: CreateLambdaOptions): Promise<Lambda> {
|
||||
assert(typeof files === 'object', '"files" must be an object');
|
||||
assert(typeof handler === 'string', '"handler" is not a string');
|
||||
@@ -86,6 +92,14 @@ export async function createLambda({
|
||||
assert(typeof maxDuration === 'number', '"maxDuration" is not a number');
|
||||
}
|
||||
|
||||
if (allowQuery !== undefined) {
|
||||
assert(Array.isArray(allowQuery), '"allowQuery" is not an Array');
|
||||
assert(
|
||||
allowQuery.every(q => typeof q === 'string'),
|
||||
'"allowQuery" is not a string Array'
|
||||
);
|
||||
}
|
||||
|
||||
await sema.acquire();
|
||||
|
||||
try {
|
||||
@@ -131,9 +145,7 @@ export async function createZip(files: Files): Promise<Buffer> {
|
||||
}
|
||||
|
||||
zipFile.end();
|
||||
streamToBuffer(zipFile.outputStream)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
streamToBuffer(zipFile.outputStream).then(resolve).catch(reject);
|
||||
});
|
||||
|
||||
return zipBuffer;
|
||||
|
||||
15
packages/build-utils/test/fixtures/21-npm-workspaces/a/package.json
vendored
Normal file
15
packages/build-utils/test/fixtures/21-npm-workspaces/a/package.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
||||
15
packages/build-utils/test/fixtures/21-npm-workspaces/b/package.json
vendored
Normal file
15
packages/build-utils/test/fixtures/21-npm-workspaces/b/package.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
||||
782
packages/build-utils/test/fixtures/21-npm-workspaces/package-lock.json
generated
vendored
Normal file
782
packages/build-utils/test/fixtures/21-npm-workspaces/package-lock.json
generated
vendored
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
packages/build-utils/test/fixtures/21-npm-workspaces/package.json
vendored
Normal file
8
packages/build-utils/test/fixtures/21-npm-workspaces/package.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "21-npm-workspaces",
|
||||
"version": "1.0.0",
|
||||
"workspaces": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
@@ -30,6 +30,7 @@ const skipFixtures: string[] = [
|
||||
'06-zero-config-hugo',
|
||||
'07-zero-config-jekyll',
|
||||
'08-zero-config-middleman',
|
||||
'21-npm-workspaces',
|
||||
];
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
|
||||
@@ -2013,15 +2013,11 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
framework: 'redwoodjs',
|
||||
};
|
||||
|
||||
const {
|
||||
builders,
|
||||
defaultRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, null, {
|
||||
projectSettings,
|
||||
featHandleMiss,
|
||||
});
|
||||
const { builders, defaultRoutes, rewriteRoutes, errorRoutes } =
|
||||
await detectBuilders(files, null, {
|
||||
projectSettings,
|
||||
featHandleMiss,
|
||||
});
|
||||
|
||||
expect(builders).toStrictEqual([
|
||||
{
|
||||
@@ -2038,7 +2034,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(errorRoutes).toStrictEqual([
|
||||
{
|
||||
status: 404,
|
||||
src: '^/(?!.*api).*$',
|
||||
src: '^(?!/api).*$',
|
||||
dest: '/404.html',
|
||||
},
|
||||
]);
|
||||
@@ -2050,15 +2046,11 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
framework: 'redwoodjs',
|
||||
};
|
||||
|
||||
const {
|
||||
builders,
|
||||
defaultRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, null, {
|
||||
projectSettings,
|
||||
featHandleMiss,
|
||||
});
|
||||
const { builders, defaultRoutes, rewriteRoutes, errorRoutes } =
|
||||
await detectBuilders(files, null, {
|
||||
projectSettings,
|
||||
featHandleMiss,
|
||||
});
|
||||
|
||||
expect(builders).toStrictEqual([
|
||||
{
|
||||
@@ -2096,7 +2088,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(errorRoutes).toStrictEqual([
|
||||
{
|
||||
status: 404,
|
||||
src: '^/(?!.*api).*$',
|
||||
src: '^(?!/api).*$',
|
||||
dest: '/404.html',
|
||||
},
|
||||
]);
|
||||
@@ -2417,7 +2409,7 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
||||
expect(errorRoutes).toStrictEqual([
|
||||
{
|
||||
status: 404,
|
||||
src: '^/(?!.*api).*$',
|
||||
src: '^(?!/api).*$',
|
||||
dest: '/404.html',
|
||||
},
|
||||
]);
|
||||
@@ -2435,6 +2427,11 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
||||
'/another/sub/index.html',
|
||||
'/another/sub/page.html',
|
||||
'/another/sub/page',
|
||||
'/another/api',
|
||||
'/another/api/page.html',
|
||||
'/rapid',
|
||||
'/rapid/page.html',
|
||||
'/health-api.html',
|
||||
].forEach(file => {
|
||||
expect(file).toMatch(pattern);
|
||||
});
|
||||
@@ -2443,12 +2440,12 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
||||
'/api',
|
||||
'/api/',
|
||||
'/api/index.html',
|
||||
'/api/page.html',
|
||||
'/api/page',
|
||||
'/api/users.js',
|
||||
'/api/users',
|
||||
'/api/sub',
|
||||
'/api/sub/index.html',
|
||||
'/api/sub/page.html',
|
||||
'/api/sub/page',
|
||||
'/api/sub/users.js',
|
||||
'/api/sub/users',
|
||||
].forEach(file => {
|
||||
expect(file).not.toMatch(pattern);
|
||||
});
|
||||
@@ -2819,12 +2816,8 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
{
|
||||
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
|
||||
|
||||
const {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
errorRoutes,
|
||||
} = await detectBuilders(files, null, options);
|
||||
const { defaultRoutes, redirectRoutes, rewriteRoutes, errorRoutes } =
|
||||
await detectBuilders(files, null, options);
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
@@ -2836,7 +2829,7 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
expect(errorRoutes).toStrictEqual([
|
||||
{
|
||||
status: 404,
|
||||
src: '^/(?!.*api).*$',
|
||||
src: '^(?!/api).*$',
|
||||
dest: '/404',
|
||||
},
|
||||
]);
|
||||
@@ -2904,11 +2897,8 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
{
|
||||
const files = ['api/[endpoint].js', 'api/[endpoint]/[id].js'];
|
||||
|
||||
const {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
} = await detectBuilders(files, null, options);
|
||||
const { defaultRoutes, redirectRoutes, rewriteRoutes } =
|
||||
await detectBuilders(files, null, options);
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
@@ -2936,11 +2926,8 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
'api/[endpoint]/[id].js',
|
||||
];
|
||||
|
||||
const {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
} = await detectBuilders(files, null, options);
|
||||
const { defaultRoutes, redirectRoutes, rewriteRoutes } =
|
||||
await detectBuilders(files, null, options);
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
@@ -2974,11 +2961,8 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
|
||||
const files = ['public/index.html', 'api/[endpoint].js'];
|
||||
|
||||
const {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
} = await detectBuilders(files, pkg, options);
|
||||
const { defaultRoutes, redirectRoutes, rewriteRoutes } =
|
||||
await detectBuilders(files, pkg, options);
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
@@ -3004,11 +2988,8 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
{
|
||||
const files = ['api/date/index.js', 'api/date.js'];
|
||||
|
||||
const {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
} = await detectBuilders(files, null, options);
|
||||
const { defaultRoutes, redirectRoutes, rewriteRoutes } =
|
||||
await detectBuilders(files, null, options);
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
@@ -3022,11 +3003,8 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
{
|
||||
const files = ['api/date.js', 'api/[date]/index.js'];
|
||||
|
||||
const {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
} = await detectBuilders(files, null, options);
|
||||
const { defaultRoutes, redirectRoutes, rewriteRoutes } =
|
||||
await detectBuilders(files, null, options);
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
@@ -3051,11 +3029,8 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
'api/food.ts',
|
||||
'api/ts/gold.ts',
|
||||
];
|
||||
const {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
} = await detectBuilders(files, null, options);
|
||||
const { defaultRoutes, redirectRoutes, rewriteRoutes } =
|
||||
await detectBuilders(files, null, options);
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
@@ -3071,11 +3046,8 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
const functions = { 'api/user.php': { runtime: 'vercel-php@0.1.0' } };
|
||||
const files = ['api/user.php'];
|
||||
|
||||
const {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
} = await detectBuilders(files, null, { functions, ...options });
|
||||
const { defaultRoutes, redirectRoutes, rewriteRoutes } =
|
||||
await detectBuilders(files, null, { functions, ...options });
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
@@ -3105,11 +3077,8 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
||||
{
|
||||
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
|
||||
|
||||
const {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
} = await detectBuilders(files, null, options);
|
||||
const { defaultRoutes, redirectRoutes, rewriteRoutes } =
|
||||
await detectBuilders(files, null, options);
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
@@ -3152,11 +3121,8 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
||||
{
|
||||
const files = ['api/[endpoint].js', 'api/[endpoint]/[id].js'];
|
||||
|
||||
const {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
} = await detectBuilders(files, null, options);
|
||||
const { defaultRoutes, redirectRoutes, rewriteRoutes } =
|
||||
await detectBuilders(files, null, options);
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
@@ -3184,11 +3150,8 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
||||
'api/[endpoint]/[id].js',
|
||||
];
|
||||
|
||||
const {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
} = await detectBuilders(files, null, options);
|
||||
const { defaultRoutes, redirectRoutes, rewriteRoutes } =
|
||||
await detectBuilders(files, null, options);
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
@@ -3222,11 +3185,8 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
||||
|
||||
const files = ['public/index.html', 'api/[endpoint].js'];
|
||||
|
||||
const {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
} = await detectBuilders(files, pkg, options);
|
||||
const { defaultRoutes, redirectRoutes, rewriteRoutes } =
|
||||
await detectBuilders(files, pkg, options);
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
@@ -3245,11 +3205,8 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
||||
{
|
||||
const files = ['api/date/index.js', 'api/date.js'];
|
||||
|
||||
const {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
} = await detectBuilders(files, null, options);
|
||||
const { defaultRoutes, redirectRoutes, rewriteRoutes } =
|
||||
await detectBuilders(files, null, options);
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
@@ -3263,11 +3220,8 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
||||
{
|
||||
const files = ['api/date.js', 'api/[date]/index.js'];
|
||||
|
||||
const {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
} = await detectBuilders(files, null, options);
|
||||
const { defaultRoutes, redirectRoutes, rewriteRoutes } =
|
||||
await detectBuilders(files, null, options);
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
@@ -3292,11 +3246,8 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
||||
'api/food.ts',
|
||||
'api/ts/gold.ts',
|
||||
];
|
||||
const {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
} = await detectBuilders(files, null, options);
|
||||
const { defaultRoutes, redirectRoutes, rewriteRoutes } =
|
||||
await detectBuilders(files, null, options);
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
@@ -3312,11 +3263,8 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
||||
const functions = { 'api/user.php': { runtime: 'vercel-php@0.1.0' } };
|
||||
const files = ['api/user.php'];
|
||||
|
||||
const {
|
||||
defaultRoutes,
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
} = await detectBuilders(files, null, { functions, ...options });
|
||||
const { defaultRoutes, redirectRoutes, rewriteRoutes } =
|
||||
await detectBuilders(files, null, { functions, ...options });
|
||||
testHeaders(redirectRoutes);
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
|
||||
26
packages/build-utils/test/unit.test.ts
vendored
26
packages/build-utils/test/unit.test.ts
vendored
@@ -298,23 +298,29 @@ it(
|
||||
);
|
||||
|
||||
it('should return lockfileVersion 2 with npm7', async () => {
|
||||
const packageLockJsonPath = path.join(__dirname, 'fixtures', '20-npm-7');
|
||||
const result = await scanParentDirs(packageLockJsonPath);
|
||||
const fixture = path.join(__dirname, 'fixtures', '20-npm-7');
|
||||
const result = await scanParentDirs(fixture);
|
||||
expect(result.cliType).toEqual('npm');
|
||||
expect(result.lockfileVersion).toEqual(2);
|
||||
});
|
||||
|
||||
it('should not return lockfileVersion with yarn', async () => {
|
||||
const packageLockJsonPath = path.join(__dirname, 'fixtures', '19-yarn-v2');
|
||||
const result = await scanParentDirs(packageLockJsonPath);
|
||||
const fixture = path.join(__dirname, 'fixtures', '19-yarn-v2');
|
||||
const result = await scanParentDirs(fixture);
|
||||
expect(result.cliType).toEqual('yarn');
|
||||
expect(result.lockfileVersion).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should return lockfileVersion 1 with older versions of npm', async () => {
|
||||
const packageLockJsonPath = path.join(
|
||||
__dirname,
|
||||
'fixtures',
|
||||
'08-yarn-npm/with-npm'
|
||||
);
|
||||
const result = await scanParentDirs(packageLockJsonPath);
|
||||
const fixture = path.join(__dirname, 'fixtures', '08-yarn-npm/with-npm');
|
||||
const result = await scanParentDirs(fixture);
|
||||
expect(result.cliType).toEqual('npm');
|
||||
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);
|
||||
});
|
||||
|
||||
11
packages/cli/@types/intercept-stdout/index.d.ts
vendored
11
packages/cli/@types/intercept-stdout/index.d.ts
vendored
@@ -1,11 +0,0 @@
|
||||
declare module 'intercept-stdout' {
|
||||
export default function (fn?: InterceptFn): UnhookIntercept
|
||||
}
|
||||
|
||||
interface InterceptFn {
|
||||
(text: string): string | void
|
||||
}
|
||||
|
||||
interface UnhookIntercept {
|
||||
(): void
|
||||
}
|
||||
5
packages/cli/@types/promisepipe/index.d.ts
vendored
5
packages/cli/@types/promisepipe/index.d.ts
vendored
@@ -1,5 +0,0 @@
|
||||
declare module 'promisepipe' {
|
||||
export default function (
|
||||
...streams: Array<NodeJS.ReadableStream | NodeJS.WritableStream | NodeJS.ReadWriteStream>
|
||||
): Promise<void>
|
||||
}
|
||||
@@ -10,9 +10,13 @@
|
||||
|
||||
## 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
|
||||
npm i -g vercel
|
||||
@@ -26,6 +30,8 @@ cd <PROJECT> # Change directory to the new project
|
||||
vercel # Deploy to the cloud
|
||||
```
|
||||
|
||||
Finally, [connect your Git repository to Vercel](https://vercel.com/docs/git) and deploy with `git push`.
|
||||
|
||||
## Documentation
|
||||
|
||||
For details on how to use Vercel CLI, check out our [documentation](https://vercel.com/docs).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "23.1.0",
|
||||
"version": "23.1.3-canary.4",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -12,33 +12,15 @@
|
||||
},
|
||||
"scripts": {
|
||||
"preinstall": "node ./scripts/preinstall.js",
|
||||
"test-unit": "nyc ava test/unit.js test/dev-builder.unit.js test/dev-router.unit.js test/dev-server.unit.js test/dev-validate.unit.js --serial --fail-fast --verbose",
|
||||
"test": "jest",
|
||||
"test-unit": "jest --coverage --verbose",
|
||||
"test-integration-cli": "rimraf test/fixtures/integration && ava test/integration.js --serial --fail-fast --verbose",
|
||||
"test-integration-dev": "ava test/dev/integration.js --serial --fail-fast --verbose",
|
||||
"prepublishOnly": "yarn build",
|
||||
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
|
||||
"coverage": "codecov",
|
||||
"build": "node -r ts-eager/register ./scripts/build.ts",
|
||||
"build-dev": "node -r ts-eager/register ./scripts/build.ts --dev"
|
||||
},
|
||||
"nyc": {
|
||||
"include": [
|
||||
"src/**"
|
||||
],
|
||||
"extension": [
|
||||
".js",
|
||||
".ts"
|
||||
],
|
||||
"require": [
|
||||
"ts-node/register"
|
||||
],
|
||||
"reporter": [
|
||||
"text",
|
||||
"html"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"instrument": true,
|
||||
"all": true
|
||||
},
|
||||
"bin": {
|
||||
"vc": "./dist/index.js",
|
||||
"vercel": "./dist/index.js"
|
||||
@@ -61,11 +43,11 @@
|
||||
"node": ">= 12"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.12.0",
|
||||
"@vercel/go": "1.2.3",
|
||||
"@vercel/node": "1.12.0",
|
||||
"@vercel/python": "2.0.5",
|
||||
"@vercel/ruby": "1.2.7",
|
||||
"@vercel/build-utils": "2.12.3-canary.3",
|
||||
"@vercel/go": "1.2.4-canary.1",
|
||||
"@vercel/node": "1.12.2-canary.1",
|
||||
"@vercel/python": "2.0.6-canary.2",
|
||||
"@vercel/ruby": "1.2.8-canary.1",
|
||||
"update-notifier": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -76,13 +58,16 @@
|
||||
"@types/ansi-regex": "4.0.0",
|
||||
"@types/async-retry": "1.2.1",
|
||||
"@types/bytes": "3.0.0",
|
||||
"@types/chance": "1.1.3",
|
||||
"@types/debug": "0.0.31",
|
||||
"@types/dotenv": "6.1.1",
|
||||
"@types/escape-html": "0.0.20",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "5.0.5",
|
||||
"@types/glob": "7.1.1",
|
||||
"@types/http-proxy": "1.16.2",
|
||||
"@types/inquirer": "7.3.1",
|
||||
"@types/jest": "27.0.1",
|
||||
"@types/load-json-file": "2.0.7",
|
||||
"@types/mime-types": "2.1.0",
|
||||
"@types/minimatch": "3.0.3",
|
||||
@@ -97,11 +82,13 @@
|
||||
"@types/semver": "6.0.1",
|
||||
"@types/tar-fs": "1.16.1",
|
||||
"@types/text-table": "0.2.0",
|
||||
"@types/title": "3.4.1",
|
||||
"@types/universal-analytics": "0.4.2",
|
||||
"@types/update-notifier": "5.1.0",
|
||||
"@types/which": "1.3.2",
|
||||
"@types/write-json-file": "2.2.1",
|
||||
"@vercel/frameworks": "0.5.0",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/frameworks": "0.5.1-canary.3",
|
||||
"@vercel/ncc": "0.29.2",
|
||||
"@zeit/fun": "0.11.2",
|
||||
"@zeit/source-map-support": "0.6.2",
|
||||
"ajv": "6.12.2",
|
||||
@@ -115,6 +102,7 @@
|
||||
"ava": "2.2.0",
|
||||
"bytes": "3.0.0",
|
||||
"chalk": "4.1.0",
|
||||
"chance": "1.1.7",
|
||||
"chokidar": "3.3.1",
|
||||
"clipboardy": "2.1.0",
|
||||
"codecov": "3.8.2",
|
||||
@@ -130,6 +118,7 @@
|
||||
"escape-html": "1.0.3",
|
||||
"esm": "3.1.4",
|
||||
"execa": "3.2.0",
|
||||
"express": "4.17.1",
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"fs-extra": "7.0.1",
|
||||
"get-port": "5.1.1",
|
||||
@@ -148,7 +137,6 @@
|
||||
"ms": "2.1.2",
|
||||
"node-fetch": "2.6.1",
|
||||
"npm-package-arg": "6.1.0",
|
||||
"nyc": "13.2.0",
|
||||
"open": "8.2.0",
|
||||
"ora": "3.4.0",
|
||||
"pcre-to-regexp": "1.0.0",
|
||||
@@ -161,7 +149,6 @@
|
||||
"rimraf": "3.0.2",
|
||||
"semver": "5.5.0",
|
||||
"serve-handler": "6.1.1",
|
||||
"sinon": "4.4.2",
|
||||
"strip-ansi": "5.2.0",
|
||||
"stripe": "5.1.0",
|
||||
"tar-fs": "1.16.3",
|
||||
@@ -178,5 +165,19 @@
|
||||
"which": "2.0.2",
|
||||
"write-json-file": "2.2.0",
|
||||
"xdg-app-paths": "5.1.0"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "ts-jest",
|
||||
"globals": {
|
||||
"ts-jest": {
|
||||
"diagnostics": false,
|
||||
"isolatedModules": true
|
||||
}
|
||||
},
|
||||
"verbose": false,
|
||||
"testEnvironment": "node",
|
||||
"testMatch": [
|
||||
"<rootDir>/test/**/*.test.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { join } from 'path';
|
||||
import { remove, writeFile } from 'fs-extra';
|
||||
|
||||
const dirRoot = join(__dirname, '..');
|
||||
const distRoot = join(dirRoot, 'dist');
|
||||
|
||||
async function createConstants() {
|
||||
console.log('Creating constants.ts');
|
||||
@@ -48,8 +49,14 @@ async function main() {
|
||||
|
||||
// Do the initial `ncc` build
|
||||
console.log();
|
||||
const src = join(dirRoot, 'src');
|
||||
const args = ['ncc', 'build', '--external', 'update-notifier'];
|
||||
const src = join(dirRoot, 'src/index.ts');
|
||||
const args = [
|
||||
'ncc',
|
||||
'build',
|
||||
'--no-asset-builds',
|
||||
'--external',
|
||||
'update-notifier',
|
||||
];
|
||||
if (isDev) {
|
||||
args.push('--source-map');
|
||||
}
|
||||
@@ -72,19 +79,13 @@ async function main() {
|
||||
dirRoot,
|
||||
'../../node_modules/@zeit/fun/dist/src/runtimes'
|
||||
);
|
||||
const dest = join(dirRoot, 'dist/runtimes');
|
||||
await cpy('**/*', dest, { parents: true, cwd: runtimes });
|
||||
await cpy('**/*', join(distRoot, 'runtimes'), {
|
||||
parents: true,
|
||||
cwd: runtimes,
|
||||
});
|
||||
|
||||
// Band-aid to delete stuff that `ncc` bundles, but it shouldn't:
|
||||
|
||||
// TypeScript definition files from `@vercel/build-utils`
|
||||
await remove(join(dirRoot, 'dist', 'dist'));
|
||||
|
||||
// The Readme and `package.json` from "config-chain" module
|
||||
await remove(join(dirRoot, 'dist', 'config-chain'));
|
||||
|
||||
// A bunch of source `.ts` files from CLI's `util` directory
|
||||
await remove(join(dirRoot, 'dist', 'util'));
|
||||
// Band-aid to bundle stuff that `ncc` neglects to bundle
|
||||
await cpy(join(dirRoot, 'src/util/projects/VERCEL_DIR_README.txt'), distRoot);
|
||||
|
||||
console.log('Finished building Vercel CLI');
|
||||
}
|
||||
|
||||
@@ -421,7 +421,7 @@ function handleCreateAliasError<T>(
|
||||
return error;
|
||||
}
|
||||
|
||||
function getTargetsForAlias(args: string[], { alias }: VercelConfig) {
|
||||
function getTargetsForAlias(args: string[], { alias }: VercelConfig = {}) {
|
||||
if (args.length) {
|
||||
return [args[args.length - 1]]
|
||||
.map(target => (target.indexOf('.') !== -1 ? toHost(target) : target))
|
||||
|
||||
@@ -6,12 +6,11 @@ import cardBrands from '../../util/billing/card-brands';
|
||||
import success from '../../util/output/success';
|
||||
import wait from '../../util/output/wait';
|
||||
import chars from '../../util/output/chars';
|
||||
import rightPad from '../../util/output/right-pad';
|
||||
import error from '../../util/output/error';
|
||||
|
||||
const expDateMiddleware = data => data;
|
||||
|
||||
export default async function({ creditCards, clear = false, contextName }) {
|
||||
export default async function ({ creditCards, clear = false, contextName }) {
|
||||
const state = {
|
||||
error: undefined,
|
||||
cardGroupLabel: `> ${chalk.bold(
|
||||
@@ -19,13 +18,13 @@ export default async function({ creditCards, clear = false, contextName }) {
|
||||
)}`,
|
||||
|
||||
name: {
|
||||
label: rightPad('Full Name', 12),
|
||||
label: 'Full Name'.padEnd(12),
|
||||
placeholder: 'John Appleseed',
|
||||
validateValue: data => data.trim().length > 0,
|
||||
},
|
||||
|
||||
cardNumber: {
|
||||
label: rightPad('Number', 12),
|
||||
label: 'Number'.padEnd(12),
|
||||
mask: 'cc',
|
||||
placeholder: '#### #### #### ####',
|
||||
validateKeypress: (data, value) => /\d/.test(data) && value.length < 19,
|
||||
@@ -40,7 +39,7 @@ export default async function({ creditCards, clear = false, contextName }) {
|
||||
},
|
||||
|
||||
ccv: {
|
||||
label: rightPad('CCV', 12),
|
||||
label: 'CCV'.padEnd(12),
|
||||
mask: 'ccv',
|
||||
placeholder: '###',
|
||||
validateValue: data => {
|
||||
@@ -50,7 +49,7 @@ export default async function({ creditCards, clear = false, contextName }) {
|
||||
},
|
||||
|
||||
expDate: {
|
||||
label: rightPad('Exp. Date', 12),
|
||||
label: 'Exp. Date'.padEnd(12),
|
||||
mask: 'expDate',
|
||||
placeholder: 'mm / yyyy',
|
||||
middleware: expDateMiddleware,
|
||||
@@ -147,9 +146,9 @@ export default async function({ creditCards, clear = false, contextName }) {
|
||||
|
||||
console.log(
|
||||
success(
|
||||
`${state.cardNumber.brand ||
|
||||
state.cardNumber.card.brand} ending in ${res.last4 ||
|
||||
res.card.last4} was added to ${chalk.bold(contextName)}`
|
||||
`${state.cardNumber.brand || state.cardNumber.card.brand} ending in ${
|
||||
res.last4 || res.card.last4
|
||||
} was added to ${chalk.bold(contextName)}`
|
||||
)
|
||||
);
|
||||
} catch (err) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import ms from 'ms';
|
||||
import plural from 'pluralize';
|
||||
import { error } from '../../util/error';
|
||||
import NowCreditCards from '../../util/credit-cards';
|
||||
import indent from '../../util/indent';
|
||||
import indent from '../../util/output/indent';
|
||||
import listInput from '../../util/input/list';
|
||||
import success from '../../util/output/success';
|
||||
import promptBool from '../../util/input/prompt-bool';
|
||||
|
||||
@@ -1,17 +1,72 @@
|
||||
import ms from 'ms';
|
||||
import fs from 'fs-extra';
|
||||
import { resolve, basename } from 'path';
|
||||
import { VercelConfig, fileNameSymbol } from '@vercel/client';
|
||||
import bytes from 'bytes';
|
||||
import chalk from 'chalk';
|
||||
import { join, resolve, basename } from 'path';
|
||||
import { Dictionary, fileNameSymbol, VercelConfig } from '@vercel/client';
|
||||
import code from '../../util/output/code';
|
||||
import highlight from '../../util/output/highlight';
|
||||
import { readLocalConfig } from '../../util/config/files';
|
||||
import getArgs from '../../util/get-args';
|
||||
import { handleError } from '../../util/error';
|
||||
import { help } from './args';
|
||||
import deploy from './latest';
|
||||
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) => {
|
||||
const { output } = client;
|
||||
const {
|
||||
apiUrl,
|
||||
output,
|
||||
authConfig: { token },
|
||||
} = client;
|
||||
|
||||
let argv = null;
|
||||
|
||||
@@ -65,10 +120,7 @@ export default async (client: Client) => {
|
||||
paths = [process.cwd()];
|
||||
}
|
||||
|
||||
let localConfig: VercelConfig | null = client.localConfig;
|
||||
if (!localConfig || localConfig instanceof Error) {
|
||||
localConfig = readLocalConfig(paths[0]);
|
||||
}
|
||||
let localConfig = client.localConfig || readLocalConfig(paths[0]);
|
||||
|
||||
for (const path of paths) {
|
||||
try {
|
||||
@@ -105,5 +157,745 @@ 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,
|
||||
output,
|
||||
});
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -13,8 +13,7 @@ import setupAndLink from '../../util/link/setup-and-link';
|
||||
import getSystemEnvValues from '../../util/env/get-system-env-values';
|
||||
|
||||
type Options = {
|
||||
'--debug'?: boolean;
|
||||
'--listen'?: string;
|
||||
'--listen': string;
|
||||
'--confirm': boolean;
|
||||
};
|
||||
|
||||
@@ -27,7 +26,6 @@ export default async function dev(
|
||||
const [dir = '.'] = args;
|
||||
let cwd = resolve(dir);
|
||||
const listen = parseListen(opts['--listen'] || '3000');
|
||||
const debug = opts['--debug'] || false;
|
||||
|
||||
// retrieve dev command
|
||||
let [link, frameworks] = await Promise.all([
|
||||
@@ -36,17 +34,11 @@ export default async function dev(
|
||||
]);
|
||||
|
||||
if (link.status === 'not_linked' && !process.env.__VERCEL_SKIP_DEV_CMD) {
|
||||
const autoConfirm = opts['--confirm'] || false;
|
||||
const forceDelete = false;
|
||||
|
||||
link = await setupAndLink(
|
||||
client,
|
||||
cwd,
|
||||
forceDelete,
|
||||
autoConfirm,
|
||||
'link',
|
||||
'Set up and develop'
|
||||
);
|
||||
link = await setupAndLink(client, cwd, {
|
||||
autoConfirm: opts['--confirm'],
|
||||
successEmoji: 'link',
|
||||
setupMsg: 'Set up and develop',
|
||||
});
|
||||
|
||||
if (link.status === 'not_linked') {
|
||||
// User aborted project linking questions
|
||||
@@ -100,7 +92,6 @@ export default async function dev(
|
||||
|
||||
const devServer = new DevServer(cwd, {
|
||||
output,
|
||||
debug,
|
||||
devCommand,
|
||||
frameworkSlug,
|
||||
projectSettings,
|
||||
|
||||
@@ -5,7 +5,6 @@ import { User, Team } from '../../types';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import Client from '../../util/client';
|
||||
import getScope from '../../util/get-scope';
|
||||
import withSpinner from '../../util/with-spinner';
|
||||
import moveOutDomain from '../../util/domains/move-out-domain';
|
||||
import isRootDomain from '../../util/is-root-domain';
|
||||
import textInput from '../../util/input/text';
|
||||
@@ -13,7 +12,7 @@ import param from '../../util/output/param';
|
||||
import getDomainAliases from '../../util/alias/get-domain-aliases';
|
||||
import getDomainByName from '../../util/domains/get-domain-by-name';
|
||||
import promptBool from '../../util/input/prompt-bool';
|
||||
import getTeams from '../../util/get-teams';
|
||||
import getTeams from '../../util/teams/get-teams';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
@@ -106,9 +105,14 @@ export default async function move(
|
||||
}
|
||||
|
||||
const context = contextName;
|
||||
const moveTokenResult = await withSpinner('Moving', () => {
|
||||
return moveOutDomain(client, context, domainName, matchId || destination);
|
||||
});
|
||||
output.spinner('Moving');
|
||||
const moveTokenResult = await moveOutDomain(
|
||||
client,
|
||||
context,
|
||||
domainName,
|
||||
matchId || destination
|
||||
);
|
||||
|
||||
if (moveTokenResult instanceof ERRORS.DomainMoveConflict) {
|
||||
const { suffix, pendingAsyncPurchase } = moveTokenResult.meta;
|
||||
if (suffix) {
|
||||
|
||||
@@ -7,7 +7,6 @@ import param from '../../util/output/param';
|
||||
import transferInDomain from '../../util/domains/transfer-in-domain';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import getAuthCode from '../../util/domains/get-auth-code';
|
||||
import withSpinner from '../../util/with-spinner';
|
||||
import getDomainPrice from '../../util/domains/get-domain-price';
|
||||
import checkTransfer from '../../util/domains/check-transfer';
|
||||
import promptBool from '../../util/input/prompt-bool';
|
||||
@@ -89,9 +88,13 @@ export default async function transferIn(
|
||||
}
|
||||
|
||||
const transferStamp = stamp();
|
||||
const transferInResult = await withSpinner(
|
||||
`Initiating transfer for domain ${domainName}`,
|
||||
() => transferInDomain(client, domainName, authCode, price)
|
||||
output.spinner(`Initiating transfer for domain ${domainName}`);
|
||||
|
||||
const transferInResult = await transferInDomain(
|
||||
client,
|
||||
domainName,
|
||||
authCode,
|
||||
price
|
||||
);
|
||||
|
||||
if (transferInResult instanceof ERRORS.InvalidDomain) {
|
||||
|
||||
22
packages/cli/src/commands/env/add.ts
vendored
22
packages/cli/src/commands/env/add.ts
vendored
@@ -13,7 +13,6 @@ import {
|
||||
} from '../../util/env/env-target';
|
||||
import readStandardInput from '../../util/input/read-standard-input';
|
||||
import param from '../../util/output/param';
|
||||
import withSpinner from '../../util/with-spinner';
|
||||
import { emoji, prependEmoji } from '../../util/emoji';
|
||||
import { isKnownError } from '../../util/env/known-error';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
@@ -142,17 +141,16 @@ export default async function add(
|
||||
|
||||
const addStamp = stamp();
|
||||
try {
|
||||
await withSpinner('Saving', () =>
|
||||
addEnvRecord(
|
||||
output,
|
||||
client,
|
||||
project.id,
|
||||
ProjectEnvType.Encrypted,
|
||||
envName,
|
||||
envValue,
|
||||
envTargets,
|
||||
envGitBranch
|
||||
)
|
||||
output.spinner('Saving');
|
||||
await addEnvRecord(
|
||||
output,
|
||||
client,
|
||||
project.id,
|
||||
ProjectEnvType.Encrypted,
|
||||
envName,
|
||||
envValue,
|
||||
envTargets,
|
||||
envGitBranch
|
||||
);
|
||||
} catch (error) {
|
||||
if (isKnownError(error) && error.serverMessage) {
|
||||
|
||||
23
packages/cli/src/commands/env/pull.ts
vendored
23
packages/cli/src/commands/env/pull.ts
vendored
@@ -6,7 +6,6 @@ import Client from '../../util/client';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
|
||||
import param from '../../util/output/param';
|
||||
import withSpinner from '../../util/with-spinner';
|
||||
import { join } from 'path';
|
||||
import { promises, openSync, closeSync, readSync } from 'fs';
|
||||
import { emoji, prependEmoji } from '../../util/emoji';
|
||||
@@ -83,19 +82,16 @@ export default async function pull(
|
||||
project.name
|
||||
)}\n`
|
||||
);
|
||||
const pullStamp = stamp();
|
||||
|
||||
const [
|
||||
{ envs: projectEnvs },
|
||||
{ systemEnvValues },
|
||||
] = await withSpinner('Downloading', () =>
|
||||
Promise.all([
|
||||
getDecryptedEnvRecords(output, client, project.id),
|
||||
project.autoExposeSystemEnvs
|
||||
? getSystemEnvValues(output, client, project.id)
|
||||
: { systemEnvValues: [] },
|
||||
])
|
||||
);
|
||||
const pullStamp = stamp();
|
||||
output.spinner('Downloading');
|
||||
|
||||
const [{ envs: projectEnvs }, { systemEnvValues }] = await Promise.all([
|
||||
getDecryptedEnvRecords(output, client, project.id),
|
||||
project.autoExposeSystemEnvs
|
||||
? getSystemEnvValues(output, client, project.id)
|
||||
: { systemEnvValues: [] },
|
||||
]);
|
||||
|
||||
const records = exposeSystemEnvs(
|
||||
projectEnvs,
|
||||
@@ -120,6 +116,7 @@ export default async function pull(
|
||||
emoji('success')
|
||||
)}\n`
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
6
packages/cli/src/commands/env/rm.ts
vendored
6
packages/cli/src/commands/env/rm.ts
vendored
@@ -13,7 +13,6 @@ import {
|
||||
import Client from '../../util/client';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import param from '../../util/output/param';
|
||||
import withSpinner from '../../util/with-spinner';
|
||||
import { emoji, prependEmoji } from '../../util/emoji';
|
||||
import { isKnownError } from '../../util/env/known-error';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
@@ -112,9 +111,8 @@ export default async function rm(
|
||||
const rmStamp = stamp();
|
||||
|
||||
try {
|
||||
await withSpinner('Removing', async () => {
|
||||
await removeEnvRecord(output, client, project.id, env);
|
||||
});
|
||||
output.spinner('Removing');
|
||||
await removeEnvRecord(output, client, project.id, env);
|
||||
} catch (error) {
|
||||
if (isKnownError(error) && error.serverMessage) {
|
||||
output.error(error.serverMessage);
|
||||
|
||||
@@ -10,7 +10,6 @@ export default new Map([
|
||||
['dns', 'dns'],
|
||||
['domain', 'domains'],
|
||||
['domains', 'domains'],
|
||||
['downgrade', 'upgrade'],
|
||||
['env', 'env'],
|
||||
['help', 'help'],
|
||||
['init', 'init'],
|
||||
|
||||
@@ -5,7 +5,6 @@ import getSubcommand from '../../util/get-subcommand';
|
||||
import Client from '../../util/client';
|
||||
import handleError from '../../util/handle-error';
|
||||
import logo from '../../util/output/logo';
|
||||
import error from '../../util/output/error';
|
||||
import init from './init';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
@@ -44,6 +43,7 @@ const help = () => {
|
||||
};
|
||||
|
||||
export default async function main(client: Client) {
|
||||
const { output } = client;
|
||||
let argv;
|
||||
let args;
|
||||
|
||||
@@ -64,15 +64,15 @@ export default async function main(client: Client) {
|
||||
}
|
||||
|
||||
if (argv._.length > 3) {
|
||||
client.output.error('Too much arguments.');
|
||||
output.error('Too much arguments.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
return await init(client, argv, args);
|
||||
} catch (err) {
|
||||
console.log(error(err.message));
|
||||
client.output.debug(err.stack);
|
||||
output.prettyError(err);
|
||||
output.debug(err.stack);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,13 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
import tar from 'tar-fs';
|
||||
import chalk from 'chalk';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
// @ts-ignore
|
||||
import listInput from '../../util/input/list';
|
||||
import listItem from '../../util/output/list-item';
|
||||
import promptBool from '../../util/input/prompt-bool';
|
||||
import toHumanPath from '../../util/humanize-path';
|
||||
import { Output } from '../../util/output';
|
||||
import Client from '../../util/client';
|
||||
import success from '../../util/output/success';
|
||||
import info from '../../util/output/info';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import didYouMean from '../../util/init/did-you-mean';
|
||||
@@ -40,7 +37,7 @@ export default async function init(
|
||||
const [name, dir] = args;
|
||||
const force = opts['-f'] || opts['--force'];
|
||||
|
||||
const examples = await fetchExampleList(output);
|
||||
const examples = await fetchExampleList(client);
|
||||
|
||||
if (!examples) {
|
||||
throw new Error(`Could not fetch example list.`);
|
||||
@@ -56,47 +53,37 @@ export default async function init(
|
||||
return 0;
|
||||
}
|
||||
|
||||
return extractExample(output, chosen, dir, force);
|
||||
return extractExample(client, chosen, dir, force);
|
||||
}
|
||||
|
||||
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);
|
||||
if (oldExample) {
|
||||
return extractExample(output, name, dir, force, 'v1');
|
||||
return extractExample(client, name, dir, force, 'v1');
|
||||
}
|
||||
|
||||
const found = await guess(exampleList, name);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch example list json
|
||||
*/
|
||||
async function fetchExampleList(output: Output) {
|
||||
output.spinner('Fetching examples');
|
||||
async function fetchExampleList(client: Client) {
|
||||
client.output.spinner('Fetching examples');
|
||||
const url = `${EXAMPLE_API}/v2/list.json`;
|
||||
|
||||
try {
|
||||
const resp = await fetch(url);
|
||||
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();
|
||||
}
|
||||
const body = await client.fetch<Example[]>(url);
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,31 +106,33 @@ async function chooseFromDropdown(message: string, exampleList: string[]) {
|
||||
* Extract example to directory
|
||||
*/
|
||||
async function extractExample(
|
||||
output: Output,
|
||||
client: Client,
|
||||
name: string,
|
||||
dir: string,
|
||||
force?: boolean,
|
||||
ver: string = 'v2'
|
||||
) {
|
||||
const { output } = client;
|
||||
const folder = prepareFolder(process.cwd(), dir || name, force);
|
||||
output.spinner(`Fetching ${name}`);
|
||||
|
||||
const url = `${EXAMPLE_API}/${ver}/download/${name}.tar.gz`;
|
||||
|
||||
return fetch(url)
|
||||
.then(async resp => {
|
||||
return client
|
||||
.fetch(url, { json: false })
|
||||
.then(async res => {
|
||||
output.stopSpinner();
|
||||
|
||||
if (resp.status !== 200) {
|
||||
if (res.status !== 200) {
|
||||
throw new Error(`Could not get ${name}.tar.gz`);
|
||||
}
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
const extractor = tar.extract(folder);
|
||||
resp.body.on('error', reject);
|
||||
res.body.on('error', reject);
|
||||
extractor.on('error', reject);
|
||||
extractor.on('finish', resolve);
|
||||
resp.body.pipe(extractor);
|
||||
res.body.pipe(extractor);
|
||||
});
|
||||
|
||||
const successLog = `Initialized "${chalk.bold(
|
||||
@@ -158,7 +147,7 @@ async function extractExample(
|
||||
`cd ${folderRel}`
|
||||
)} and run ${getCommandName()}.`
|
||||
);
|
||||
console.log(success(`${successLog}\n${deployHint}`));
|
||||
output.success(`${successLog}\n${deployHint}`);
|
||||
return 0;
|
||||
})
|
||||
.catch(e => {
|
||||
|
||||
@@ -11,6 +11,7 @@ import { getPkgName, getCommandName } from '../util/pkg-name';
|
||||
import Client from '../util/client';
|
||||
import { getDeployment } from '../util/get-deployment';
|
||||
import { Deployment } from '@vercel/client';
|
||||
import { Build } from '../types';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
@@ -116,7 +117,7 @@ export default async function main(client: Client) {
|
||||
|
||||
const { builds } =
|
||||
deployment.version === 2
|
||||
? await client.fetch(`/v1/now/deployments/${id}/builds`)
|
||||
? await client.fetch<{ builds: Build[] }>(`/v1/deployments/${id}/builds`)
|
||||
: { builds: [] };
|
||||
|
||||
log(
|
||||
@@ -146,7 +147,8 @@ export default async function main(client: Client) {
|
||||
|
||||
for (const build of builds) {
|
||||
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'));
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import chalk from 'chalk';
|
||||
import Client from '../../util/client';
|
||||
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 { getPkgName } from '../../util/pkg-name';
|
||||
import setupAndLink from '../../util/link/setup-and-link';
|
||||
@@ -24,6 +22,9 @@ const help = () => {
|
||||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
-p ${chalk.bold.underline('NAME')}, --project=${chalk.bold.underline(
|
||||
'NAME'
|
||||
)} Project name
|
||||
--confirm Confirm default options and skip questions
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
@@ -44,40 +45,26 @@ const help = () => {
|
||||
`);
|
||||
};
|
||||
|
||||
const COMMAND_CONFIG = {
|
||||
// No subcommands yet
|
||||
};
|
||||
|
||||
export default async function main(client: Client) {
|
||||
let argv;
|
||||
|
||||
try {
|
||||
argv = getArgs(client.argv.slice(2), {
|
||||
'--confirm': Boolean,
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
return 1;
|
||||
}
|
||||
const argv = getArgs(client.argv.slice(2), {
|
||||
'--confirm': Boolean,
|
||||
'--project': String,
|
||||
'-p': '--project',
|
||||
});
|
||||
|
||||
if (argv['--help']) {
|
||||
help();
|
||||
return 2;
|
||||
}
|
||||
|
||||
const { args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
|
||||
const path = args[0] || process.cwd();
|
||||
const autoConfirm = argv['--confirm'] || false;
|
||||
const forceDelete = true;
|
||||
|
||||
const link = await setupAndLink(
|
||||
client,
|
||||
path,
|
||||
forceDelete,
|
||||
autoConfirm,
|
||||
'success',
|
||||
'Set up'
|
||||
);
|
||||
const cwd = argv._[1] || process.cwd();
|
||||
const link = await setupAndLink(client, cwd, {
|
||||
forceDelete: true,
|
||||
autoConfirm: argv['--confirm'],
|
||||
projectName: argv['--project'],
|
||||
successEmoji: 'success',
|
||||
setupMsg: 'Set up',
|
||||
});
|
||||
|
||||
if (link.status === 'error') {
|
||||
return link.exitCode;
|
||||
|
||||
@@ -92,8 +92,8 @@ export default async function main(client: Client) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let app: string | null = argv._[1];
|
||||
let host: string | null = null;
|
||||
let app: string | undefined = argv._[1];
|
||||
let host: string | undefined = undefined;
|
||||
|
||||
if (argv['--help']) {
|
||||
help();
|
||||
@@ -156,7 +156,7 @@ export default async function main(client: Client) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
app = null;
|
||||
app = undefined;
|
||||
host = asHost;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { validate as validateEmail } from 'email-validator';
|
||||
import chalk from 'chalk';
|
||||
import hp from '../util/humanize-path';
|
||||
import getArgs from '../util/get-args';
|
||||
import handleError from '../util/handle-error';
|
||||
import logo from '../util/output/logo';
|
||||
import prompt from '../util/login/prompt';
|
||||
import doSamlLogin from '../util/login/saml';
|
||||
@@ -52,20 +51,14 @@ const help = () => {
|
||||
};
|
||||
|
||||
export default async function login(client: Client): Promise<number> {
|
||||
let argv;
|
||||
const { output } = client;
|
||||
|
||||
try {
|
||||
argv = getArgs(client.argv.slice(2), {
|
||||
'--oob': Boolean,
|
||||
'--github': Boolean,
|
||||
'--gitlab': Boolean,
|
||||
'--bitbucket': Boolean,
|
||||
});
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
return 1;
|
||||
}
|
||||
const argv = getArgs(client.argv.slice(2), {
|
||||
'--oob': Boolean,
|
||||
'--github': Boolean,
|
||||
'--gitlab': Boolean,
|
||||
'--bitbucket': Boolean,
|
||||
});
|
||||
|
||||
if (argv['--help']) {
|
||||
help();
|
||||
@@ -115,8 +108,7 @@ export default async function login(client: Client): Promise<number> {
|
||||
}
|
||||
}
|
||||
|
||||
// When `result` is a string it's the user's authentication token.
|
||||
// It needs to be saved to the configuration file.
|
||||
// Save the user's authentication token to the configuration file.
|
||||
client.authConfig.token = result.token;
|
||||
|
||||
writeToAuthConfigFile(client.authConfig);
|
||||
@@ -124,9 +116,9 @@ export default async function login(client: Client): Promise<number> {
|
||||
|
||||
output.debug(`Saved credentials in "${hp(getGlobalPathConfig())}"`);
|
||||
|
||||
console.log(
|
||||
output.print(
|
||||
`${chalk.cyan('Congratulations!')} ` +
|
||||
`You are now logged in. In order to deploy something, run ${getCommandName()}.`
|
||||
`You are now logged in. In order to deploy something, run ${getCommandName()}.\n`
|
||||
);
|
||||
|
||||
output.print(
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import chalk from 'chalk';
|
||||
import stamp from '../../util/output/stamp.ts';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import info from '../../util/output/info';
|
||||
import rightPad from '../../util/output/right-pad';
|
||||
import eraseLines from '../../util/output/erase-lines';
|
||||
import chars from '../../util/output/chars';
|
||||
import note from '../../util/output/note';
|
||||
import textInput from '../../util/input/text';
|
||||
import invite from './invite';
|
||||
import { writeToConfigFile } from '../../util/config/files';
|
||||
import { getPkgName, getCommandName } from '../../util/pkg-name.ts';
|
||||
import { getPkgName, getCommandName } from '../../util/pkg-name';
|
||||
import Client from '../../util/client';
|
||||
import createTeam from '../../util/teams/create-team';
|
||||
import patchTeam from '../../util/teams/patch-team';
|
||||
|
||||
const validateSlugKeypress = (data, value) =>
|
||||
const validateSlugKeypress = (data: string, value: string) =>
|
||||
// TODO: the `value` here should contain the current value + the keypress
|
||||
// should be fixed on utils/input/text.js
|
||||
/^[a-zA-Z]+[a-zA-Z0-9_-]*$/.test(value + data);
|
||||
|
||||
const validateNameKeypress = (data, value) =>
|
||||
const validateNameKeypress = (data: string, value: string) =>
|
||||
// TODO: the `value` here should contain the current value + the keypress
|
||||
// should be fixed on utils/input/text.js
|
||||
/^[ a-zA-Z0-9_-]+$/.test(value + data);
|
||||
@@ -30,17 +32,17 @@ const gracefulExit = () => {
|
||||
return 0;
|
||||
};
|
||||
|
||||
const teamUrlPrefix = rightPad('Team URL', 14) + chalk.gray('vercel.com/');
|
||||
const teamNamePrefix = rightPad('Team Name', 14);
|
||||
const teamUrlPrefix = 'Team URL'.padEnd(14) + chalk.gray('vercel.com/');
|
||||
const teamNamePrefix = 'Team Name'.padEnd(14);
|
||||
|
||||
export default async function add(client, teams) {
|
||||
export default async function add(client: Client): Promise<number> {
|
||||
let slug;
|
||||
let team;
|
||||
let elapsed;
|
||||
const { output } = client;
|
||||
|
||||
output.log(
|
||||
`Pick a team identifier for its url (e.g.: ${chalk.cyan(
|
||||
`Pick a team identifier for its URL (e.g.: ${chalk.cyan(
|
||||
'`vercel.com/acme`'
|
||||
)})`
|
||||
);
|
||||
@@ -66,14 +68,12 @@ export default async function add(client, teams) {
|
||||
elapsed = stamp();
|
||||
output.spinner(teamUrlPrefix + slug);
|
||||
|
||||
let res;
|
||||
try {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
res = await teams.create({ slug });
|
||||
team = res;
|
||||
team = await createTeam(client, { slug });
|
||||
} catch (err) {
|
||||
output.stopSpinner();
|
||||
process.stdout.write(eraseLines(2));
|
||||
output.print(eraseLines(2));
|
||||
output.error(err.message);
|
||||
}
|
||||
} while (!team);
|
||||
@@ -104,11 +104,12 @@ export default async function add(client, teams) {
|
||||
elapsed = stamp();
|
||||
output.spinner(teamNamePrefix + name);
|
||||
|
||||
const res = await teams.edit({ id: team.id, name });
|
||||
const res = await patchTeam(client, team.id, { name });
|
||||
|
||||
output.stopSpinner();
|
||||
process.stdout.write(eraseLines(2));
|
||||
|
||||
/*
|
||||
if (res.error) {
|
||||
output.error(res.error.message);
|
||||
output.log(`${chalk.red(`✖ ${teamNamePrefix}`)}${name}`);
|
||||
@@ -117,33 +118,25 @@ export default async function add(client, teams) {
|
||||
// TODO: maybe we want to ask the user to retry? not sure if
|
||||
// there's a scenario where that would be wanted
|
||||
}
|
||||
*/
|
||||
|
||||
team = Object.assign(team, res);
|
||||
|
||||
output.success(`Team name saved ${elapsed()}`);
|
||||
output.log(`${chalk.cyan(`${chars.tick} `) + teamNamePrefix + team.name}\n`);
|
||||
|
||||
output.spinner('Saving');
|
||||
|
||||
// Update config file
|
||||
const configCopy = Object.assign({}, client.config);
|
||||
|
||||
if (configCopy.sh) {
|
||||
configCopy.sh.currentTeam = team;
|
||||
} else {
|
||||
configCopy.currentTeam = team.id;
|
||||
}
|
||||
|
||||
writeToConfigFile(configCopy);
|
||||
|
||||
output.spinner('Saving');
|
||||
client.config.currentTeam = team.id;
|
||||
writeToConfigFile(client.config);
|
||||
output.stopSpinner();
|
||||
|
||||
await invite(client, { _: [] }, teams, {
|
||||
await invite(client, [], {
|
||||
introMsg: 'Invite your teammates! When done, press enter on an empty field',
|
||||
noopMsg: `You can invite teammates later by running ${getCommandName(
|
||||
`teams invite`
|
||||
)}`,
|
||||
});
|
||||
|
||||
gracefulExit();
|
||||
return gracefulExit();
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import chalk from 'chalk';
|
||||
import error from '../../util/output/error';
|
||||
import NowTeams from '../../util/teams';
|
||||
import logo from '../../util/output/logo';
|
||||
import list from './list';
|
||||
import add from './add';
|
||||
@@ -8,7 +7,6 @@ import change from './switch';
|
||||
import invite from './invite';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import getArgs from '../../util/get-args';
|
||||
import handleError from '../../util/handle-error';
|
||||
import Client from '../../util/client';
|
||||
|
||||
const help = () => {
|
||||
@@ -61,28 +59,11 @@ const help = () => {
|
||||
`);
|
||||
};
|
||||
|
||||
let argv;
|
||||
let debug;
|
||||
let apiUrl;
|
||||
let subcommand;
|
||||
|
||||
export default async (client: Client) => {
|
||||
try {
|
||||
argv = getArgs(client.argv.slice(2), {
|
||||
'--since': String,
|
||||
'--until': String,
|
||||
'--next': Number,
|
||||
'-N': '--next',
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
return 1;
|
||||
}
|
||||
let subcommand;
|
||||
|
||||
debug = argv['--debug'];
|
||||
apiUrl = client.apiUrl;
|
||||
|
||||
const isSwitch = argv._[0] && argv._[0] === 'switch';
|
||||
const argv = getArgs(client.argv.slice(2), undefined, { permissive: true });
|
||||
const isSwitch = argv._[0] === 'switch';
|
||||
|
||||
argv._ = argv._.slice(1);
|
||||
|
||||
@@ -97,19 +78,11 @@ export default async (client: Client) => {
|
||||
return 2;
|
||||
}
|
||||
|
||||
const {
|
||||
authConfig: { token },
|
||||
config,
|
||||
} = client;
|
||||
|
||||
const { currentTeam } = config;
|
||||
const teams = new NowTeams({ apiUrl, token, debug, currentTeam });
|
||||
|
||||
let exitCode;
|
||||
let exitCode = 0;
|
||||
switch (subcommand) {
|
||||
case 'list':
|
||||
case 'ls': {
|
||||
exitCode = await list(client, argv, teams);
|
||||
exitCode = await list(client);
|
||||
break;
|
||||
}
|
||||
case 'switch':
|
||||
@@ -119,12 +92,12 @@ export default async (client: Client) => {
|
||||
}
|
||||
case 'add':
|
||||
case 'create': {
|
||||
exitCode = await add(client, teams);
|
||||
exitCode = await add(client);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'invite': {
|
||||
exitCode = await invite(client, argv, teams);
|
||||
exitCode = await invite(client, argv._);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -137,6 +110,5 @@ export default async (client: Client) => {
|
||||
help();
|
||||
}
|
||||
}
|
||||
teams.close();
|
||||
return exitCode || 0;
|
||||
return exitCode;
|
||||
};
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import chalk from 'chalk';
|
||||
import { email as regexEmail } from '../../util/input/regexes';
|
||||
import cmd from '../../util/output/cmd.ts';
|
||||
import stamp from '../../util/output/stamp.ts';
|
||||
import param from '../../util/output/param.ts';
|
||||
import Client from '../../util/client';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import param from '../../util/output/param';
|
||||
import chars from '../../util/output/chars';
|
||||
import rightPad from '../../util/output/right-pad';
|
||||
import textInput from '../../util/input/text';
|
||||
import eraseLines from '../../util/output/erase-lines';
|
||||
import getUser from '../../util/get-user.ts';
|
||||
import { getCommandName } from '../../util/pkg-name.ts';
|
||||
import getUser from '../../util/get-user';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import { email as regexEmail } from '../../util/input/regexes';
|
||||
import getTeams from '../../util/teams/get-teams';
|
||||
import inviteUserToTeam from '../../util/teams/invite-user-to-team';
|
||||
|
||||
const validateEmail = data => regexEmail.test(data.trim()) || data.length === 0;
|
||||
const validateEmail = (data: string) =>
|
||||
regexEmail.test(data.trim()) || data.length === 0;
|
||||
|
||||
const domains = Array.from(
|
||||
new Set([
|
||||
@@ -29,12 +32,12 @@ const domains = Array.from(
|
||||
])
|
||||
);
|
||||
|
||||
const emailAutoComplete = (value, teamSlug) => {
|
||||
const emailAutoComplete = (value: string, teamSlug: string) => {
|
||||
const parts = value.split('@');
|
||||
|
||||
if (parts.length === 2 && parts[1].length > 0) {
|
||||
const [, host] = parts;
|
||||
let suggestion = false;
|
||||
let suggestion: string | false = false;
|
||||
|
||||
domains.unshift(teamSlug);
|
||||
for (const domain of domains) {
|
||||
@@ -52,17 +55,16 @@ const emailAutoComplete = (value, teamSlug) => {
|
||||
};
|
||||
|
||||
export default async function invite(
|
||||
client,
|
||||
argv,
|
||||
teams,
|
||||
{ introMsg, noopMsg = 'No changes made' } = {}
|
||||
) {
|
||||
client: Client,
|
||||
emails: string[] = [],
|
||||
{ introMsg = '', noopMsg = 'No changes made' } = {}
|
||||
): Promise<number> {
|
||||
const { config, output } = client;
|
||||
const { currentTeam: currentTeamId } = config;
|
||||
|
||||
output.spinner('Fetching teams');
|
||||
const list = (await teams.ls()).teams;
|
||||
const currentTeam = list.find(team => team.id === currentTeamId);
|
||||
const teams = await getTeams(client);
|
||||
const currentTeam = teams.find(team => team.id === currentTeamId);
|
||||
|
||||
output.spinner('Fetching user information');
|
||||
let user;
|
||||
@@ -94,8 +96,8 @@ export default async function invite(
|
||||
introMsg || `Inviting team members to ${chalk.bold(currentTeam.name)}`
|
||||
);
|
||||
|
||||
if (argv._.length > 0) {
|
||||
for (const email of argv._) {
|
||||
if (emails.length > 0) {
|
||||
for (const email of emails) {
|
||||
if (regexEmail.test(email)) {
|
||||
output.spinner(email);
|
||||
const elapsed = stamp();
|
||||
@@ -103,8 +105,8 @@ export default async function invite(
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const res = await teams.inviteUser({ teamId: currentTeam.id, email });
|
||||
userInfo = res.name || res.username;
|
||||
const res = await inviteUserToTeam(client, currentTeam.id, email);
|
||||
userInfo = res.username;
|
||||
} catch (err) {
|
||||
if (err.code === 'user_not_found') {
|
||||
output.error(`No user exists with the email address "${email}".`);
|
||||
@@ -123,12 +125,11 @@ export default async function invite(
|
||||
output.log(`${chalk.red(`✖ ${email}`)} ${chalk.gray('[invalid]')}`);
|
||||
}
|
||||
}
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const inviteUserPrefix = rightPad('Invite User', 14);
|
||||
const sentEmailPrefix = rightPad('Sent Email', 14);
|
||||
const emails = [];
|
||||
const inviteUserPrefix = 'Invite User'.padEnd(14);
|
||||
const sentEmailPrefix = 'Sent Email'.padEnd(14);
|
||||
let hasError = false;
|
||||
let email;
|
||||
do {
|
||||
@@ -151,12 +152,12 @@ export default async function invite(
|
||||
output.spinner(inviteUserPrefix + email);
|
||||
try {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const { name, username } = await teams.inviteUser({
|
||||
teamId: currentTeam.id,
|
||||
email,
|
||||
});
|
||||
const userInfo = name || username;
|
||||
email = `${email}${userInfo ? ` (${userInfo})` : ''} ${elapsed()}`;
|
||||
const { username } = await inviteUserToTeam(
|
||||
client,
|
||||
currentTeam.id,
|
||||
email
|
||||
);
|
||||
email = `${email}${username ? ` (${username})` : ''} ${elapsed()}`;
|
||||
emails.push(email);
|
||||
output.log(`${chalk.cyan(chars.tick)} ${sentEmailPrefix}${email}`);
|
||||
if (hasError) {
|
||||
@@ -194,4 +195,6 @@ export default async function invite(
|
||||
output.log(`${chalk.cyan(chars.tick)} ${inviteUserPrefix}${email}`);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,22 +1,41 @@
|
||||
import chars from '../../util/output/chars';
|
||||
import table from '../../util/output/table';
|
||||
import getUser from '../../util/get-user.ts';
|
||||
import getUser from '../../util/get-user';
|
||||
import getTeams from '../../util/teams/get-teams';
|
||||
import getPrefixedFlags from '../../util/get-prefixed-flags';
|
||||
import { getPkgName } from '../../util/pkg-name.ts';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import getCommandFlags from '../../util/get-command-flags';
|
||||
import cmd from '../../util/output/cmd.ts';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import Client from '../../util/client';
|
||||
import getArgs from '../../util/get-args';
|
||||
|
||||
export default async function list(client, argv, teams) {
|
||||
export default async function list(client: Client): Promise<number> {
|
||||
const { config, output } = client;
|
||||
const { next } = argv;
|
||||
|
||||
const argv = getArgs(client.argv.slice(2), {
|
||||
'--since': String,
|
||||
'--until': String,
|
||||
'--count': Number,
|
||||
'--next': Number,
|
||||
'-C': '--count',
|
||||
'-N': '--next',
|
||||
});
|
||||
|
||||
const next = argv['--next'];
|
||||
const count = argv['--count'];
|
||||
|
||||
if (typeof next !== 'undefined' && !Number.isInteger(next)) {
|
||||
output.error('Please provide a number for flag --next');
|
||||
output.error('Please provide a number for flag `--next`');
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (typeof count !== 'undefined' && !Number.isInteger(next)) {
|
||||
output.error('Please provide a number for flag `--count`');
|
||||
return 1;
|
||||
}
|
||||
|
||||
output.spinner('Fetching teams');
|
||||
const { teams: list, pagination } = await teams.ls({
|
||||
const { teams, pagination } = await getTeams(client, {
|
||||
next,
|
||||
apiVersion: 2,
|
||||
});
|
||||
@@ -37,40 +56,31 @@ export default async function list(client, argv, teams) {
|
||||
}
|
||||
|
||||
if (accountIsCurrent) {
|
||||
currentTeam = {
|
||||
slug: user.username || user.email,
|
||||
};
|
||||
currentTeam = user.uid;
|
||||
}
|
||||
|
||||
const teamList = list.map(({ slug, name }) => ({
|
||||
const teamList = teams.map(({ id, slug, name }) => ({
|
||||
id,
|
||||
name,
|
||||
value: slug,
|
||||
current: slug === currentTeam.slug ? chars.tick : '',
|
||||
current: id === currentTeam ? chars.tick : '',
|
||||
}));
|
||||
|
||||
teamList.unshift({
|
||||
id: user.uid,
|
||||
name: user.email,
|
||||
value: user.username || user.email,
|
||||
current: (accountIsCurrent && chars.tick) || '',
|
||||
current: accountIsCurrent ? chars.tick : '',
|
||||
});
|
||||
|
||||
// Let's bring the current team to the beginning of the list
|
||||
// Bring the current Team to the beginning of the list
|
||||
if (!accountIsCurrent) {
|
||||
const index = teamList.findIndex(
|
||||
choice => choice.value === currentTeam.slug
|
||||
);
|
||||
const index = teamList.findIndex(choice => choice.id === currentTeam);
|
||||
const choice = teamList.splice(index, 1)[0];
|
||||
teamList.unshift(choice);
|
||||
}
|
||||
|
||||
// Printing
|
||||
const count = teamList.length;
|
||||
if (!count) {
|
||||
// Maybe should not happen
|
||||
output.error(`No teams found`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
output.stopSpinner();
|
||||
console.log(); // empty line
|
||||
|
||||
@@ -80,7 +90,7 @@ export default async function list(client, argv, teams) {
|
||||
[1, 5]
|
||||
);
|
||||
|
||||
if (pagination && pagination.count === 20) {
|
||||
if (pagination?.count === 20) {
|
||||
const prefixedArgs = getPrefixedFlags(argv);
|
||||
const flags = getCommandFlags(prefixedArgs, ['_', '--next', '-N', '-d']);
|
||||
const nextCmd = `${getPkgName()} teams ls${flags} --next ${
|
||||
@@ -89,4 +99,6 @@ export default async function list(client, argv, teams) {
|
||||
console.log(); // empty line
|
||||
output.log(`To display the next page run ${cmd(nextCmd)}`);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import chalk from 'chalk';
|
||||
import Client from '../../util/client';
|
||||
import { emoji } from '../../util/emoji';
|
||||
import getUser from '../../util/get-user';
|
||||
import getTeams from '../../util/get-teams';
|
||||
import getTeams from '../../util/teams/get-teams';
|
||||
import listInput from '../../util/input/list';
|
||||
import { Team, GlobalConfig } from '../../types';
|
||||
import { writeToConfigFile } from '../../util/config/files';
|
||||
|
||||
@@ -3,7 +3,6 @@ import logo from '../util/output/logo';
|
||||
import getScope from '../util/get-scope';
|
||||
import { getPkgName } from '../util/pkg-name';
|
||||
import getArgs from '../util/get-args';
|
||||
import handleError from '../util/handle-error';
|
||||
import Client from '../util/client';
|
||||
|
||||
const help = () => {
|
||||
@@ -32,16 +31,9 @@ const help = () => {
|
||||
`);
|
||||
};
|
||||
|
||||
export default async (client: Client) => {
|
||||
export default async (client: Client): Promise<number> => {
|
||||
const { output } = client;
|
||||
let argv;
|
||||
try {
|
||||
argv = getArgs(client.argv.slice(2), {});
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const argv = getArgs(client.argv.slice(2), {});
|
||||
argv._ = argv._.slice(1);
|
||||
|
||||
if (argv['--help'] || argv._[0] === 'help') {
|
||||
@@ -62,9 +54,13 @@ export default async (client: Client) => {
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (process.stdout.isTTY) {
|
||||
process.stdout.write('> ');
|
||||
if (output.isTTY) {
|
||||
output.log(contextName);
|
||||
} else {
|
||||
// If stdout is not a TTY, then only print the username
|
||||
// to support piping the output to another file / exe
|
||||
output.print(`${contextName}\n`, { w: process.stdout });
|
||||
}
|
||||
|
||||
console.log(contextName);
|
||||
return 0;
|
||||
};
|
||||
|
||||
@@ -20,10 +20,9 @@ import epipebomb from 'epipebomb';
|
||||
import updateNotifier from 'update-notifier';
|
||||
import { URL } from 'url';
|
||||
import * as Sentry from '@sentry/node';
|
||||
import { NowBuildError } from '@vercel/build-utils';
|
||||
import hp from './util/humanize-path';
|
||||
import commands from './commands/index.ts';
|
||||
import pkg from './util/pkg.ts';
|
||||
import commands from './commands';
|
||||
import pkg from './util/pkg';
|
||||
import createOutput from './util/output';
|
||||
import cmd from './util/output/cmd';
|
||||
import info from './util/output/info';
|
||||
@@ -31,9 +30,9 @@ import error from './util/output/error';
|
||||
import param from './util/output/param';
|
||||
import highlight from './util/output/highlight';
|
||||
import getArgs from './util/get-args';
|
||||
import getUser from './util/get-user.ts';
|
||||
import Client from './util/client.ts';
|
||||
import NowTeams from './util/teams';
|
||||
import getUser from './util/get-user';
|
||||
import getTeams from './util/teams/get-teams';
|
||||
import Client from './util/client';
|
||||
import { handleError } from './util/error';
|
||||
import reportError from './util/report-error';
|
||||
import getConfig from './util/get-config';
|
||||
@@ -44,13 +43,14 @@ import {
|
||||
getDefaultAuthConfig,
|
||||
} from './util/config/get-default';
|
||||
import * as ERRORS from './util/errors-ts';
|
||||
import { NowError } from './util/now-error';
|
||||
import { APIError } from './util/errors-ts.ts';
|
||||
import { SENTRY_DSN } from './util/constants.ts';
|
||||
import { APIError } from './util/errors-ts';
|
||||
import { SENTRY_DSN } from './util/constants';
|
||||
import getUpdateCommand from './util/get-update-command';
|
||||
import { metrics, shouldCollectMetrics } from './util/metrics.ts';
|
||||
import { getCommandName, getTitleName } from './util/pkg-name.ts';
|
||||
import doLoginPrompt from './util/login/prompt.ts';
|
||||
import { metrics, shouldCollectMetrics } from './util/metrics';
|
||||
import { getCommandName, getTitleName } from './util/pkg-name';
|
||||
import doLoginPrompt from './util/login/prompt';
|
||||
import { GlobalConfig } from './types';
|
||||
import { VercelConfig } from '@vercel/client';
|
||||
|
||||
const isCanary = pkg.version.includes('canary');
|
||||
|
||||
@@ -77,8 +77,8 @@ Sentry.init({
|
||||
environment: isCanary ? 'canary' : 'stable',
|
||||
});
|
||||
|
||||
let client;
|
||||
let debug = () => {};
|
||||
let client: Client;
|
||||
let debug: (s: string) => void = () => {};
|
||||
let apiUrl = 'https://api.vercel.com';
|
||||
|
||||
const main = async () => {
|
||||
@@ -108,26 +108,30 @@ const main = async () => {
|
||||
debug = output.debug;
|
||||
|
||||
const localConfigPath = argv['--local-config'];
|
||||
const localConfig = await getConfig(output, localConfigPath);
|
||||
|
||||
if (localConfigPath && localConfig instanceof ERRORS.CantFindConfig) {
|
||||
output.error(
|
||||
`Couldn't find a project configuration file at \n ${localConfig.meta.paths.join(
|
||||
' or\n '
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
let localConfig: VercelConfig | Error | undefined = await getConfig(
|
||||
output,
|
||||
localConfigPath
|
||||
);
|
||||
|
||||
if (localConfig instanceof ERRORS.CantParseJSONFile) {
|
||||
output.error(`Couldn't parse JSON file ${localConfig.meta.file}.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (
|
||||
(localConfig instanceof NowError || localConfig instanceof NowBuildError) &&
|
||||
!(localConfig instanceof ERRORS.CantFindConfig)
|
||||
) {
|
||||
if (localConfig instanceof ERRORS.CantFindConfig) {
|
||||
if (localConfigPath) {
|
||||
output.error(
|
||||
`Couldn't find a project configuration file at \n ${localConfig.meta.paths.join(
|
||||
' or\n '
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
} else {
|
||||
localConfig = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (localConfig instanceof Error) {
|
||||
output.prettyError(localConfig);
|
||||
return 1;
|
||||
}
|
||||
@@ -207,7 +211,7 @@ const main = async () => {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let config;
|
||||
let config: GlobalConfig | null = null;
|
||||
|
||||
if (configExists) {
|
||||
try {
|
||||
@@ -229,8 +233,11 @@ const main = async () => {
|
||||
// multiple providers. In that case, we really
|
||||
// need to migrate.
|
||||
if (
|
||||
// @ts-ignore
|
||||
config.sh ||
|
||||
// @ts-ignore
|
||||
config.user ||
|
||||
// @ts-ignore
|
||||
typeof config.user === 'object' ||
|
||||
typeof config.currentTeam === 'object'
|
||||
) {
|
||||
@@ -300,6 +307,7 @@ const main = async () => {
|
||||
// This is from when Vercel CLI supported
|
||||
// multiple providers. In that case, we really
|
||||
// need to migrate.
|
||||
// @ts-ignore
|
||||
if (authConfig.credentials) {
|
||||
authConfigExists = false;
|
||||
}
|
||||
@@ -346,6 +354,11 @@ const main = async () => {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!config) {
|
||||
output.error(`Vercel global config was not loaded.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Shared API `Client` instance for all sub-commands to utilize
|
||||
client = new Client({
|
||||
apiUrl,
|
||||
@@ -397,7 +410,7 @@ const main = async () => {
|
||||
}
|
||||
|
||||
if (subcommandExists) {
|
||||
debug('user supplied known subcommand', targetOrSubcommand);
|
||||
debug(`user supplied known subcommand: "${targetOrSubcommand}"`);
|
||||
subcommand = targetOrSubcommand;
|
||||
} else {
|
||||
debug('user supplied a possible target for deployment');
|
||||
@@ -430,12 +443,16 @@ const main = async () => {
|
||||
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.
|
||||
// It needs to be saved to the configuration file.
|
||||
client.authConfig.token = result;
|
||||
|
||||
// New user, so we can't keep the team
|
||||
delete client.config.currentTeam;
|
||||
client.authConfig.token = result.token;
|
||||
|
||||
configFiles.writeToAuthConfigFile(client.authConfig);
|
||||
configFiles.writeToConfigFile(client.config);
|
||||
@@ -453,14 +470,12 @@ const main = async () => {
|
||||
}
|
||||
|
||||
if (typeof argv['--token'] === 'string' && subcommand === 'switch') {
|
||||
console.error(
|
||||
error({
|
||||
message: `This command doesn't work with ${param(
|
||||
'--token'
|
||||
)}. Please use ${param('--scope')}.`,
|
||||
slug: 'no-token-allowed',
|
||||
})
|
||||
);
|
||||
output.prettyError({
|
||||
message: `This command doesn't work with ${param(
|
||||
'--token'
|
||||
)}. Please use ${param('--scope')}.`,
|
||||
link: 'https://err.sh/vercel/no-token-allowed',
|
||||
});
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -469,12 +484,10 @@ const main = async () => {
|
||||
const token = argv['--token'];
|
||||
|
||||
if (token.length === 0) {
|
||||
console.error(
|
||||
error({
|
||||
message: `You defined ${param('--token')}, but it's missing a value`,
|
||||
slug: 'missing-token-value',
|
||||
})
|
||||
);
|
||||
output.prettyError({
|
||||
message: `You defined ${param('--token')}, but it's missing a value`,
|
||||
link: 'https://err.sh/vercel/missing-token-value',
|
||||
});
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -482,16 +495,14 @@ const main = async () => {
|
||||
const invalid = token.match(/(\W)/g);
|
||||
if (invalid) {
|
||||
const notContain = Array.from(new Set(invalid)).sort();
|
||||
console.error(
|
||||
error({
|
||||
message: `You defined ${param(
|
||||
'--token'
|
||||
)}, but its contents are invalid. Must not contain: ${notContain
|
||||
.map(c => JSON.stringify(c))
|
||||
.join(', ')}`,
|
||||
slug: 'invalid-token-value',
|
||||
})
|
||||
);
|
||||
output.prettyError({
|
||||
message: `You defined ${param(
|
||||
'--token'
|
||||
)}, but its contents are invalid. Must not contain: ${notContain
|
||||
.map(c => JSON.stringify(c))
|
||||
.join(', ')}`,
|
||||
link: 'https://err.sh/vercel/invalid-token-value',
|
||||
});
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -512,13 +523,8 @@ const main = async () => {
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
authConfig: { token },
|
||||
} = client;
|
||||
|
||||
let scope = argv['--scope'] || argv['--team'] || localConfig.scope;
|
||||
|
||||
const targetCommand = commands.get(subcommand);
|
||||
const scope = argv['--scope'] || argv['--team'] || localConfig?.scope;
|
||||
|
||||
if (
|
||||
typeof scope === 'string' &&
|
||||
@@ -532,12 +538,10 @@ const main = async () => {
|
||||
user = await getUser(client);
|
||||
} catch (err) {
|
||||
if (err.code === 'NOT_AUTHORIZED') {
|
||||
console.error(
|
||||
error({
|
||||
message: `You do not have access to the specified account`,
|
||||
slug: 'scope-not-accessible',
|
||||
})
|
||||
);
|
||||
output.prettyError({
|
||||
message: `You do not have access to the specified account`,
|
||||
link: 'https://err.sh/vercel/scope-not-accessible',
|
||||
});
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -549,19 +553,16 @@ const main = async () => {
|
||||
if (user.uid === scope || user.email === scope || user.username === scope) {
|
||||
delete client.config.currentTeam;
|
||||
} else {
|
||||
let list = [];
|
||||
let teams = [];
|
||||
|
||||
try {
|
||||
const teams = new NowTeams({ apiUrl, token, debug: isDebugging });
|
||||
list = (await teams.ls()).teams;
|
||||
teams = await getTeams(client);
|
||||
} catch (err) {
|
||||
if (err.code === 'not_authorized') {
|
||||
console.error(
|
||||
error({
|
||||
message: `You do not have access to the specified team`,
|
||||
slug: 'scope-not-accessible',
|
||||
})
|
||||
);
|
||||
output.prettyError({
|
||||
message: `You do not have access to the specified team`,
|
||||
link: 'https://err.sh/vercel/scope-not-accessible',
|
||||
});
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -571,15 +572,13 @@ const main = async () => {
|
||||
}
|
||||
|
||||
const related =
|
||||
list && list.find(item => item.id === scope || item.slug === scope);
|
||||
teams && teams.find(team => team.id === scope || team.slug === scope);
|
||||
|
||||
if (!related) {
|
||||
console.error(
|
||||
error({
|
||||
message: 'The specified scope does not exist',
|
||||
slug: 'scope-not-existent',
|
||||
})
|
||||
);
|
||||
output.prettyError({
|
||||
message: 'The specified scope does not exist',
|
||||
link: 'https://err.sh/vercel/scope-not-existent',
|
||||
});
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -674,7 +673,7 @@ const main = async () => {
|
||||
return exitCode;
|
||||
};
|
||||
|
||||
const handleRejection = async err => {
|
||||
const handleRejection = async (err: any) => {
|
||||
debug('handling rejection');
|
||||
|
||||
if (err) {
|
||||
@@ -691,7 +690,7 @@ const handleRejection = async err => {
|
||||
process.exit(1);
|
||||
};
|
||||
|
||||
const handleUnexpected = async err => {
|
||||
const handleUnexpected = async (err: Error) => {
|
||||
const { message } = err;
|
||||
|
||||
// We do not want to render errors about Sentry not being reachable
|
||||
@@ -700,9 +699,8 @@ const handleUnexpected = async err => {
|
||||
return;
|
||||
}
|
||||
|
||||
await reportError(Sentry, client, err);
|
||||
|
||||
console.error(error(`An unexpected error occurred!\n${err.stack}`));
|
||||
await reportError(Sentry, client, err);
|
||||
|
||||
process.exit(1);
|
||||
};
|
||||
@@ -713,6 +711,7 @@ process.on('uncaughtException', handleUnexpected);
|
||||
main()
|
||||
.then(exitCode => {
|
||||
process.exitCode = exitCode;
|
||||
// @ts-ignore - "nowExit" is a non-standard event name
|
||||
process.emit('nowExit');
|
||||
})
|
||||
.catch(handleUnexpected);
|
||||
@@ -16,11 +16,13 @@ export interface JSONObject {
|
||||
}
|
||||
|
||||
export interface AuthConfig {
|
||||
_?: string;
|
||||
token?: string;
|
||||
skipWrite?: boolean;
|
||||
}
|
||||
|
||||
export interface GlobalConfig {
|
||||
_?: string;
|
||||
currentTeam?: string;
|
||||
includeScheme?: string;
|
||||
collectMetrics?: boolean;
|
||||
@@ -306,3 +308,141 @@ export interface Token {
|
||||
createdAt: number;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export async function getDeploymentForAlias(
|
||||
localConfigPath: string | undefined,
|
||||
user: User,
|
||||
contextName: string,
|
||||
localConfig: VercelConfig
|
||||
localConfig?: VercelConfig
|
||||
) {
|
||||
output.spinner(`Fetching deployment to alias in ${chalk.bold(contextName)}`);
|
||||
|
||||
@@ -52,7 +52,7 @@ export async function getDeploymentForAlias(
|
||||
}
|
||||
|
||||
const appName =
|
||||
(localConfig && localConfig.name) ||
|
||||
localConfig?.name ||
|
||||
path.basename(path.resolve(process.cwd(), localConfigPath || ''));
|
||||
|
||||
if (!appName) {
|
||||
|
||||
@@ -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 });
|
||||
7
packages/cli/src/util/build-state.ts
Normal file
7
packages/cli/src/util/build-state.ts
Normal 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';
|
||||
@@ -34,10 +34,10 @@ export interface ClientOptions {
|
||||
authConfig: AuthConfig;
|
||||
output: Output;
|
||||
config: GlobalConfig;
|
||||
localConfig: VercelConfig;
|
||||
localConfig?: VercelConfig;
|
||||
}
|
||||
|
||||
const isJSONObject = (v: any): v is JSONObject => {
|
||||
export const isJSONObject = (v: any): v is JSONObject => {
|
||||
return v && typeof v == 'object' && v.constructor === Object;
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ export default class Client extends EventEmitter {
|
||||
authConfig: AuthConfig;
|
||||
output: Output;
|
||||
config: GlobalConfig;
|
||||
localConfig: VercelConfig;
|
||||
localConfig?: VercelConfig;
|
||||
private requestIdCounter: number;
|
||||
|
||||
constructor(opts: ClientOptions) {
|
||||
@@ -69,7 +69,7 @@ export default class Client extends EventEmitter {
|
||||
});
|
||||
}
|
||||
|
||||
_fetch(_url: string, opts: FetchOptions = {}) {
|
||||
private _fetch(_url: string, opts: FetchOptions = {}) {
|
||||
const parsedUrl = parseUrl(_url, true);
|
||||
const apiUrl = parsedUrl.host
|
||||
? `${parsedUrl.protocol}//${parsedUrl.host}`
|
||||
|
||||
@@ -100,8 +100,8 @@ export function getAuthConfigFilePath() {
|
||||
|
||||
export function readLocalConfig(
|
||||
prefix: string = process.cwd()
|
||||
): VercelConfig | null {
|
||||
let config: VercelConfig | null = null;
|
||||
): VercelConfig | undefined {
|
||||
let config: VercelConfig | undefined = undefined;
|
||||
let target = '';
|
||||
|
||||
try {
|
||||
@@ -116,7 +116,7 @@ export function readLocalConfig(
|
||||
}
|
||||
|
||||
if (!target) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -134,7 +134,7 @@ export function readLocalConfig(
|
||||
}
|
||||
|
||||
if (!config) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
config[fileNameSymbol] = basename(target);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
export const getDefaultConfig = async existingCopy => {
|
||||
import { AuthConfig, GlobalConfig } from '../../types';
|
||||
|
||||
export const getDefaultConfig = async (existingCopy?: GlobalConfig | null) => {
|
||||
let migrated = false;
|
||||
|
||||
const config = {
|
||||
_:
|
||||
'This is your Vercel config file. For more information see the global configuration documentation: https://vercel.com/docs/configuration#global',
|
||||
const config: GlobalConfig = {
|
||||
_: 'This is your Vercel config file. For more information see the global configuration documentation: https://vercel.com/docs/configuration#global',
|
||||
collectMetrics: true,
|
||||
};
|
||||
|
||||
@@ -16,44 +17,33 @@ export const getDefaultConfig = async existingCopy => {
|
||||
'collectMetrics',
|
||||
'api',
|
||||
// This is deleted later in the code
|
||||
'shownTips',
|
||||
];
|
||||
|
||||
try {
|
||||
const existing = Object.assign({}, existingCopy);
|
||||
// @ts-ignore
|
||||
const sh = Object.assign({}, existing.sh || {});
|
||||
|
||||
Object.assign(config, existing, sh);
|
||||
|
||||
for (const key of Object.keys(config)) {
|
||||
if (!keep.includes(key)) {
|
||||
// @ts-ignore
|
||||
delete config[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof config.currentTeam === 'object') {
|
||||
// @ts-ignore
|
||||
config.currentTeam = config.currentTeam.id;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
if (typeof config.user === 'object') {
|
||||
// @ts-ignore
|
||||
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;
|
||||
} catch (err) {}
|
||||
}
|
||||
@@ -61,16 +51,16 @@ export const getDefaultConfig = async existingCopy => {
|
||||
return { config, migrated };
|
||||
};
|
||||
|
||||
export const getDefaultAuthConfig = async existing => {
|
||||
export const getDefaultAuthConfig = async (existing?: AuthConfig | null) => {
|
||||
let migrated = false;
|
||||
|
||||
const config = {
|
||||
_:
|
||||
'This is your Vercel credentials file. DO NOT SHARE! More: https://vercel.com/docs/configuration#global',
|
||||
const config: AuthConfig = {
|
||||
_: 'This is your Vercel credentials file. DO NOT SHARE! More: https://vercel.com/docs/configuration#global',
|
||||
};
|
||||
|
||||
if (existing) {
|
||||
try {
|
||||
// @ts-ignore
|
||||
const sh = existing.credentials.find(item => item.provider === 'sh');
|
||||
|
||||
if (sh) {
|
||||
@@ -4,7 +4,7 @@ import * as ERRORS from '../errors';
|
||||
import { NowError } from '../now-error';
|
||||
import mapCertError from '../certs/map-cert-error';
|
||||
import { Org } from '../../types';
|
||||
import Now from '..';
|
||||
import Now, { CreateOptions } from '..';
|
||||
import Client from '../client';
|
||||
import { DeploymentError } from '../../../../client/dist';
|
||||
|
||||
@@ -13,8 +13,8 @@ export default async function createDeploy(
|
||||
now: Now,
|
||||
contextName: string,
|
||||
paths: string[],
|
||||
createArgs: any,
|
||||
org: Org | null,
|
||||
createArgs: CreateOptions,
|
||||
org: Org,
|
||||
isSettingUpProject: boolean,
|
||||
cwd?: string
|
||||
): Promise<any | DeploymentError> {
|
||||
|
||||
@@ -69,10 +69,15 @@ export default async function processDeployment({
|
||||
|
||||
const { env = {} } = requestBody;
|
||||
|
||||
const token = now._token;
|
||||
if (!token) {
|
||||
throw new Error('Missing authentication token');
|
||||
}
|
||||
|
||||
const clientOptions: VercelClientOptions = {
|
||||
teamId: org.type === 'team' ? org.id : undefined,
|
||||
apiUrl: now._apiUrl,
|
||||
token: now._token,
|
||||
token,
|
||||
debug: now._debug,
|
||||
userAgent: ua,
|
||||
path: paths[0],
|
||||
@@ -149,7 +154,6 @@ export default async function processDeployment({
|
||||
org.slug
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
now.url = event.payload.url;
|
||||
|
||||
output.stopSpinner();
|
||||
|
||||
@@ -19,7 +19,7 @@ import { BuilderWithPackage } from './types';
|
||||
|
||||
type CliPackageJson = typeof cliPkg;
|
||||
|
||||
declare const __non_webpack_require__: typeof require;
|
||||
const require_: typeof require = eval('require');
|
||||
|
||||
const registryTypes = new Set(['version', 'tag', 'range']);
|
||||
|
||||
@@ -82,14 +82,14 @@ function getNpmVersion(use = ''): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
export function getBuildUtils(packages: string[], org: string): string {
|
||||
export function getBuildUtils(packages: string[]): string {
|
||||
const version = packages
|
||||
.map(getNpmVersion)
|
||||
.some(ver => ver.includes('canary'))
|
||||
? 'canary'
|
||||
: 'latest';
|
||||
|
||||
return `@${org}/build-utils@${version}`;
|
||||
return `@vercel/build-utils@${version}`;
|
||||
}
|
||||
|
||||
function parseVersionSafe(rawSpec: string) {
|
||||
@@ -104,7 +104,7 @@ export function filterPackage(
|
||||
builderSpec: string,
|
||||
distTag: string,
|
||||
buildersPkg: PackageJson,
|
||||
cliPkg: CliPackageJson
|
||||
cliPkg: Partial<CliPackageJson>
|
||||
) {
|
||||
if (builderSpec in localBuilders) return false;
|
||||
const parsed = npa(builderSpec);
|
||||
@@ -191,10 +191,7 @@ export async function installBuilders(
|
||||
return;
|
||||
}
|
||||
|
||||
packagesToInstall.push(
|
||||
getBuildUtils(packages, 'vercel'),
|
||||
getBuildUtils(packages, 'now')
|
||||
);
|
||||
packagesToInstall.push(getBuildUtils(packages));
|
||||
|
||||
await npmInstall(builderDir, output, packagesToInstall, false);
|
||||
|
||||
@@ -307,10 +304,7 @@ export async function updateBuilders(
|
||||
});
|
||||
|
||||
if (packagesToUpdate.length > 0) {
|
||||
packagesToUpdate.push(
|
||||
getBuildUtils(packages, 'vercel'),
|
||||
getBuildUtils(packages, 'now')
|
||||
);
|
||||
packagesToUpdate.push(getBuildUtils(packages));
|
||||
|
||||
await npmInstall(builderDir, output, packagesToUpdate, true);
|
||||
|
||||
@@ -361,8 +355,8 @@ export async function getBuilder(
|
||||
|
||||
try {
|
||||
output.debug(`Requiring runtime: "${requirePath}"`);
|
||||
const mod = require(requirePath);
|
||||
const pkg = require(join(requirePath, 'package.json'));
|
||||
const mod = require_(requirePath);
|
||||
const pkg = require_(join(requirePath, 'package.json'));
|
||||
builderWithPkg = {
|
||||
requirePath,
|
||||
builder: Object.freeze(mod),
|
||||
@@ -438,18 +432,13 @@ function purgeRequireCache(
|
||||
builderDir: string,
|
||||
output: Output
|
||||
) {
|
||||
const _require =
|
||||
typeof __non_webpack_require__ === 'function'
|
||||
? __non_webpack_require__
|
||||
: require;
|
||||
|
||||
// The `require()` cache for the builder's assets must be purged
|
||||
const packagesPaths = packages.map(b => join(builderDir, 'node_modules', b));
|
||||
for (const id of Object.keys(_require.cache)) {
|
||||
for (const id of Object.keys(require_.cache)) {
|
||||
for (const path of packagesPaths) {
|
||||
if (id.startsWith(path)) {
|
||||
output.debug(`Purging require cache for "${id}"`);
|
||||
delete _require.cache[id];
|
||||
delete require_.cache[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,8 @@ export async function executeBuild(
|
||||
builderWithPkg: { runInProcess, requirePath, builder, package: pkg },
|
||||
} = match;
|
||||
const { entrypoint } = match;
|
||||
const { debug, envConfigs, cwd: workPath, devCacheDir } = devServer;
|
||||
const { envConfigs, cwd: workPath, devCacheDir } = devServer;
|
||||
const debug = devServer.output.isDebugEnabled();
|
||||
|
||||
const startTime = Date.now();
|
||||
const showBuildTimestamp =
|
||||
|
||||
@@ -117,7 +117,6 @@ function sortBuilders(buildA: Builder, buildB: Builder) {
|
||||
|
||||
export default class DevServer {
|
||||
public cwd: string;
|
||||
public debug: boolean;
|
||||
public output: Output;
|
||||
public proxy: httpProxy;
|
||||
public envConfigs: EnvConfigs;
|
||||
@@ -157,7 +156,6 @@ export default class DevServer {
|
||||
|
||||
constructor(cwd: string, options: DevServerOptions) {
|
||||
this.cwd = cwd;
|
||||
this.debug = options.debug;
|
||||
this.output = options.output;
|
||||
this.envConfigs = { buildEnv: {}, runEnv: {}, allEnv: {} };
|
||||
this.systemEnvValues = options.systemEnvValues || [];
|
||||
|
||||
@@ -23,7 +23,6 @@ export { VercelConfig };
|
||||
|
||||
export interface DevServerOptions {
|
||||
output: Output;
|
||||
debug: boolean;
|
||||
devCommand?: string;
|
||||
frameworkSlug?: string;
|
||||
projectSettings?: ProjectSettings;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
//@ts-ignore Missing types for 'title'
|
||||
import title from 'title';
|
||||
import { ProjectEnvVariable } from '../../types';
|
||||
|
||||
export default function formatEnvTarget(env: ProjectEnvVariable): string {
|
||||
const target = (Array.isArray(env.target) ? env.target : [env.target || ''])
|
||||
.map(title)
|
||||
.map(t => title(t))
|
||||
.join(', ');
|
||||
|
||||
return env.gitBranch ? `${target} (${env.gitBranch})` : target;
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import { Response } from 'node-fetch';
|
||||
import errorOutput from './output/error';
|
||||
|
||||
export { default as handleError } from './handle-error';
|
||||
export const error = errorOutput;
|
||||
|
||||
export interface ResponseError extends Error {
|
||||
status: number;
|
||||
serverMessage: string;
|
||||
retryAfter?: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export async function responseError(
|
||||
res,
|
||||
fallbackMessage = null,
|
||||
res: Response,
|
||||
fallbackMessage: string | null = null,
|
||||
parsedBody = {}
|
||||
) {
|
||||
let message;
|
||||
let message = '';
|
||||
let bodyError;
|
||||
|
||||
if (res.status >= 400 && res.status < 500) {
|
||||
@@ -25,11 +33,11 @@ export async function responseError(
|
||||
message = bodyError.message;
|
||||
}
|
||||
|
||||
if (message == null) {
|
||||
if (!message) {
|
||||
message = fallbackMessage === null ? 'Response Error' : fallbackMessage;
|
||||
}
|
||||
|
||||
const err = new Error(`${message} (${res.status})`);
|
||||
const err = new Error(`${message} (${res.status})`) as ResponseError;
|
||||
|
||||
err.status = res.status;
|
||||
err.serverMessage = message;
|
||||
@@ -54,7 +62,10 @@ export async function responseError(
|
||||
return err;
|
||||
}
|
||||
|
||||
export async function responseErrorMessage(res, fallbackMessage = null) {
|
||||
export async function responseErrorMessage(
|
||||
res: Response,
|
||||
fallbackMessage: string | null = null
|
||||
) {
|
||||
let message;
|
||||
|
||||
if (res.status >= 400 && res.status < 500) {
|
||||
@@ -15,6 +15,7 @@ export class APIError extends Error {
|
||||
status: number;
|
||||
serverMessage: string;
|
||||
link?: string;
|
||||
slug?: string;
|
||||
action?: string;
|
||||
retryAfter: number | null | 'never';
|
||||
[key: string]: any;
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
33
packages/cli/src/util/get-project-name.ts
Normal file
33
packages/cli/src/util/get-project-name.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { basename } from 'path';
|
||||
import { VercelConfig } from '@vercel/client';
|
||||
|
||||
export interface GetProjectNameOptions {
|
||||
argv: { '--name'?: string };
|
||||
nowConfig?: VercelConfig;
|
||||
isFile?: boolean;
|
||||
paths?: string[];
|
||||
}
|
||||
|
||||
export default function getProjectName({
|
||||
argv,
|
||||
nowConfig = {},
|
||||
isFile = false,
|
||||
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] || '');
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import Client from './client';
|
||||
import getUser from './get-user';
|
||||
import getTeamById from './get-team-by-id';
|
||||
import getTeamById from './teams/get-team-by-id';
|
||||
import { TeamDeleted } from './errors-ts';
|
||||
import { Team } from '../types';
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import Client from './client';
|
||||
import { Team } from '../types';
|
||||
import { APIError, InvalidToken } from './errors-ts';
|
||||
|
||||
let teams: Team[] | undefined;
|
||||
|
||||
export default async function getTeams(client: Client): Promise<Team[]> {
|
||||
if (teams) return teams;
|
||||
|
||||
try {
|
||||
const body = await client.fetch<{ teams: Team[] }>('/v1/teams', {
|
||||
useCurrentTeam: false,
|
||||
});
|
||||
teams = body.teams || [];
|
||||
return teams;
|
||||
} catch (error) {
|
||||
if (error instanceof APIError && error.status === 403) {
|
||||
throw new InvalidToken();
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,7 @@ import Client from './client';
|
||||
import { User } from '../types';
|
||||
import { APIError, InvalidToken, MissingUser } from './errors-ts';
|
||||
|
||||
let user: User | undefined;
|
||||
|
||||
export default async function getUser(client: Client) {
|
||||
if (user) return user;
|
||||
|
||||
try {
|
||||
const res = await client.fetch<{ user: User }>('/www/user', {
|
||||
useCurrentTeam: false,
|
||||
@@ -16,8 +12,7 @@ export default async function getUser(client: Client) {
|
||||
throw new MissingUser();
|
||||
}
|
||||
|
||||
user = res.user;
|
||||
return user;
|
||||
return res.user;
|
||||
} catch (error) {
|
||||
if (error instanceof APIError && error.status === 403) {
|
||||
throw new InvalidToken();
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
function indent(text, n) {
|
||||
return text
|
||||
.split('\n')
|
||||
.map(l => ' '.repeat(n) + l)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
export default indent;
|
||||
@@ -3,20 +3,79 @@ import qs from 'querystring';
|
||||
import { parse as parseUrl } from 'url';
|
||||
import retry from 'async-retry';
|
||||
import ms from 'ms';
|
||||
import fetch from 'node-fetch';
|
||||
import fetch, { Headers } from 'node-fetch';
|
||||
import { URLSearchParams } from 'url';
|
||||
import bytes from 'bytes';
|
||||
import chalk from 'chalk';
|
||||
import ua from './ua.ts';
|
||||
import processDeployment from './deploy/process-deployment.ts';
|
||||
import ua from './ua';
|
||||
import processDeployment from './deploy/process-deployment';
|
||||
import highlight from './output/highlight';
|
||||
import createOutput from './output';
|
||||
import createOutput, { Output } from './output';
|
||||
import { responseError } from './error';
|
||||
import stamp from './output/stamp';
|
||||
import { BuildError } from './errors-ts';
|
||||
import printIndications from './print-indications.ts';
|
||||
import { APIError, BuildError } from './errors-ts';
|
||||
import printIndications from './print-indications';
|
||||
import { Org } from '../types';
|
||||
import { VercelConfig } from './dev/types';
|
||||
import { FetchOptions, isJSONObject } from './client';
|
||||
import { Dictionary } from '@vercel/client';
|
||||
|
||||
export interface NowOptions {
|
||||
apiUrl: string;
|
||||
token?: string;
|
||||
url?: string | null;
|
||||
currentTeam?: string | null;
|
||||
output: Output;
|
||||
forceNew?: boolean;
|
||||
withCache?: boolean;
|
||||
debug?: boolean;
|
||||
}
|
||||
|
||||
export interface CreateOptions {
|
||||
// Legacy
|
||||
nowConfig?: VercelConfig;
|
||||
isFile?: boolean;
|
||||
|
||||
// Latest
|
||||
name: string;
|
||||
project?: string;
|
||||
wantsPublic: boolean;
|
||||
meta: Dictionary<string>;
|
||||
regions?: string[];
|
||||
quiet?: boolean;
|
||||
env: Dictionary<string>;
|
||||
build: { env: Dictionary<string> };
|
||||
forceNew?: boolean;
|
||||
withCache?: boolean;
|
||||
target?: string | null;
|
||||
deployStamp: () => string;
|
||||
projectSettings?: any;
|
||||
skipAutoDetectionConfirmation?: boolean;
|
||||
}
|
||||
|
||||
export interface RemoveOptions {
|
||||
hard?: boolean;
|
||||
}
|
||||
|
||||
export interface ListOptions {
|
||||
version?: number;
|
||||
meta?: Dictionary<string>;
|
||||
nextTimestamp?: number;
|
||||
}
|
||||
|
||||
export default class Now extends EventEmitter {
|
||||
url: string | null;
|
||||
currentTeam: string | null;
|
||||
_apiUrl: string;
|
||||
_token?: string;
|
||||
_debug: boolean;
|
||||
_forceNew: boolean;
|
||||
_withCache: boolean;
|
||||
_output: Output;
|
||||
_syncAmount?: number;
|
||||
_files?: any[];
|
||||
_missing?: string[];
|
||||
|
||||
constructor({
|
||||
apiUrl,
|
||||
token,
|
||||
@@ -26,7 +85,7 @@ export default class Now extends EventEmitter {
|
||||
withCache = false,
|
||||
debug = false,
|
||||
output = createOutput({ debug }),
|
||||
}) {
|
||||
}: NowOptions) {
|
||||
super();
|
||||
|
||||
this.url = url;
|
||||
@@ -41,10 +100,10 @@ export default class Now extends EventEmitter {
|
||||
}
|
||||
|
||||
async create(
|
||||
paths,
|
||||
paths: string[],
|
||||
{
|
||||
// Legacy
|
||||
nowConfig = {},
|
||||
nowConfig: nowConfig = {},
|
||||
|
||||
// Latest
|
||||
name,
|
||||
@@ -61,12 +120,12 @@ export default class Now extends EventEmitter {
|
||||
deployStamp,
|
||||
projectSettings,
|
||||
skipAutoDetectionConfirmation,
|
||||
},
|
||||
org,
|
||||
isSettingUpProject,
|
||||
cwd
|
||||
}: CreateOptions,
|
||||
org: Org,
|
||||
isSettingUpProject: boolean,
|
||||
cwd?: string
|
||||
) {
|
||||
let hashes = {};
|
||||
let hashes: any = {};
|
||||
const uploadStamp = stamp();
|
||||
|
||||
let requestBody = {
|
||||
@@ -109,7 +168,7 @@ export default class Now extends EventEmitter {
|
||||
let sizeExceeded = 0;
|
||||
const { log, warn } = this._output;
|
||||
|
||||
deployment.warnings.forEach(warning => {
|
||||
deployment.warnings.forEach((warning: any) => {
|
||||
if (warning.reason === 'size_limit_exceeded') {
|
||||
const { sha, limit } = warning;
|
||||
const n = hashes[sha].names.pop();
|
||||
@@ -135,14 +194,14 @@ export default class Now extends EventEmitter {
|
||||
return deployment;
|
||||
}
|
||||
|
||||
async handleDeploymentError(error, { env }) {
|
||||
async handleDeploymentError(error: any, { env }: any) {
|
||||
if (error.status === 429) {
|
||||
if (error.code === 'builds_rate_limited') {
|
||||
const err = new Error(error.message);
|
||||
const err = Object.create(APIError.prototype);
|
||||
err.message = error.message;
|
||||
err.status = error.status;
|
||||
err.retryAfter = 'never';
|
||||
err.code = error.code;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -157,8 +216,8 @@ export default class Now extends EventEmitter {
|
||||
msg += 'Please slow down.';
|
||||
}
|
||||
|
||||
const err = new Error(msg);
|
||||
|
||||
const err = Object.create(APIError.prototype);
|
||||
err.message = msg;
|
||||
err.status = error.status;
|
||||
err.retryAfter = 'never';
|
||||
|
||||
@@ -172,7 +231,6 @@ export default class Now extends EventEmitter {
|
||||
|
||||
if (error.status === 400 && error.code === 'missing_files') {
|
||||
this._missing = error.missing || [];
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -199,7 +257,7 @@ export default class Now extends EventEmitter {
|
||||
'.vercelignore'
|
||||
)}):` +
|
||||
`\n- ${unreferencedBuildSpecs
|
||||
.map(item => JSON.stringify(item))
|
||||
.map((item: any) => JSON.stringify(item))
|
||||
.join('\n- ')}`;
|
||||
} else {
|
||||
Object.assign(err, error);
|
||||
@@ -211,13 +269,17 @@ export default class Now extends EventEmitter {
|
||||
// Handle build errors
|
||||
if (error.id && error.id.startsWith('bld_')) {
|
||||
return new BuildError({
|
||||
message: 'Build failed',
|
||||
meta: {
|
||||
entrypoint: error.entrypoint,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (error.errorCode && error.errorCode === 'BUILD_FAILED') {
|
||||
if (
|
||||
error.errorCode === 'BUILD_FAILED' ||
|
||||
error.errorCode === 'UNEXPECTED_ERROR'
|
||||
) {
|
||||
return new BuildError({
|
||||
message: error.errorMessage,
|
||||
meta: {},
|
||||
@@ -227,7 +289,7 @@ export default class Now extends EventEmitter {
|
||||
return new Error(error.message);
|
||||
}
|
||||
|
||||
async listSecrets(next, testWarningFlag) {
|
||||
async listSecrets(next?: number, testWarningFlag?: boolean) {
|
||||
const payload = await this.retry(async bail => {
|
||||
let secretsUrl = '/v3/now/secrets?limit=20';
|
||||
|
||||
@@ -256,8 +318,11 @@ export default class Now extends EventEmitter {
|
||||
return payload;
|
||||
}
|
||||
|
||||
async list(app, { version = 4, meta = {}, nextTimestamp } = {}) {
|
||||
const fetchRetry = async (url, options = {}) => {
|
||||
async list(
|
||||
app?: string,
|
||||
{ version = 4, meta = {}, nextTimestamp }: ListOptions = {}
|
||||
) {
|
||||
const fetchRetry = async (url: string, options: FetchOptions = {}) => {
|
||||
return this.retry(
|
||||
async bail => {
|
||||
const res = await this._fetch(url, options);
|
||||
@@ -293,8 +358,8 @@ export default class Now extends EventEmitter {
|
||||
);
|
||||
|
||||
const deployments = await Promise.all(
|
||||
projects.map(async ({ id: projectId }) => {
|
||||
const query = new URLSearchParams({ limit: 1, projectId });
|
||||
projects.map(async ({ id: projectId }: any) => {
|
||||
const query = new URLSearchParams({ limit: '1', projectId });
|
||||
const { deployments } = await fetchRetry(
|
||||
`/v${version}/now/deployments?${query}`
|
||||
);
|
||||
@@ -323,7 +388,7 @@ export default class Now extends EventEmitter {
|
||||
return response;
|
||||
}
|
||||
|
||||
async findDeployment(hostOrId) {
|
||||
async findDeployment(hostOrId: string) {
|
||||
const { debug } = this._output;
|
||||
|
||||
let id = hostOrId && !hostOrId.includes('.');
|
||||
@@ -387,7 +452,7 @@ export default class Now extends EventEmitter {
|
||||
);
|
||||
}
|
||||
|
||||
async remove(deploymentId, { hard }) {
|
||||
async remove(deploymentId: string, { hard = false }: RemoveOptions) {
|
||||
const url = `/now/deployments/${deploymentId}?hard=${hard ? 1 : 0}`;
|
||||
|
||||
await this.retry(async bail => {
|
||||
@@ -409,56 +474,51 @@ export default class Now extends EventEmitter {
|
||||
return true;
|
||||
}
|
||||
|
||||
retry(fn, { retries = 3, maxTimeout = Infinity } = {}) {
|
||||
return retry(fn, {
|
||||
retry<T>(
|
||||
fn: retry.RetryFunction<T>,
|
||||
{ retries = 3, maxTimeout = Infinity }: retry.Options = {}
|
||||
) {
|
||||
return retry<T>(fn, {
|
||||
retries,
|
||||
maxTimeout,
|
||||
onRetry: this._onRetry,
|
||||
});
|
||||
}
|
||||
|
||||
_onRetry(err) {
|
||||
_onRetry(err: Error) {
|
||||
this._output.debug(`Retrying: ${err}\n${err.stack}`);
|
||||
}
|
||||
|
||||
close() {}
|
||||
|
||||
get syncAmount() {
|
||||
if (!this._syncAmount) {
|
||||
this._syncAmount = this._missing
|
||||
.map(sha => this._files.get(sha).data.length)
|
||||
.reduce((a, b) => a + b, 0);
|
||||
}
|
||||
|
||||
return this._syncAmount;
|
||||
}
|
||||
|
||||
async _fetch(_url, opts = {}) {
|
||||
async _fetch(_url: string, opts: FetchOptions = {}) {
|
||||
if (opts.useCurrentTeam !== false && this.currentTeam) {
|
||||
const parsedUrl = parseUrl(_url, true);
|
||||
const query = parsedUrl.query;
|
||||
|
||||
query.teamId = this.currentTeam;
|
||||
_url = `${parsedUrl.pathname}?${qs.encode(query)}`;
|
||||
_url = `${parsedUrl.pathname}?${qs.stringify(query)}`;
|
||||
delete opts.useCurrentTeam;
|
||||
}
|
||||
|
||||
opts.headers = opts.headers || {};
|
||||
opts.headers.accept = 'application/json';
|
||||
opts.headers.authorization = `Bearer ${this._token}`;
|
||||
opts.headers['user-agent'] = ua;
|
||||
|
||||
if (
|
||||
opts.body &&
|
||||
typeof opts.body === 'object' &&
|
||||
opts.body.constructor === Object
|
||||
) {
|
||||
opts.body = JSON.stringify(opts.body);
|
||||
opts.headers['content-type'] = 'application/json; charset=utf-8';
|
||||
opts.headers = new Headers(opts.headers);
|
||||
opts.headers.set('accept', 'application/json');
|
||||
if (this._token) {
|
||||
opts.headers.set('authorization', `Bearer ${this._token}`);
|
||||
}
|
||||
opts.headers.set('user-agent', ua);
|
||||
|
||||
let body;
|
||||
if (isJSONObject(opts.body)) {
|
||||
body = JSON.stringify(opts.body);
|
||||
opts.headers.set('content-type', 'application/json; charset=utf8');
|
||||
} else {
|
||||
body = opts.body;
|
||||
}
|
||||
|
||||
const res = await this._output.time(
|
||||
`${opts.method || 'GET'} ${this._apiUrl}${_url} ${opts.body || ''}`,
|
||||
fetch(`${this._apiUrl}${_url}`, opts)
|
||||
fetch(`${this._apiUrl}${_url}`, { ...opts, body })
|
||||
);
|
||||
printIndications(res);
|
||||
return res;
|
||||
@@ -472,7 +532,7 @@ export default class Now extends EventEmitter {
|
||||
// which automatically returns the json response body
|
||||
// if the response is ok and content-type json
|
||||
// it does the same for JSON` body` in opts
|
||||
async fetch(url, opts = {}) {
|
||||
async fetch(url: string, opts: FetchOptions = {}) {
|
||||
return this.retry(async bail => {
|
||||
if (opts.json !== false && opts.body && typeof opts.body === 'object') {
|
||||
opts = Object.assign({}, opts, {
|
||||
@@ -492,7 +552,7 @@ export default class Now extends EventEmitter {
|
||||
return null;
|
||||
}
|
||||
|
||||
return res.headers.get('content-type').includes('application/json')
|
||||
return res.headers.get('content-type')?.includes('application/json')
|
||||
? res.json()
|
||||
: res;
|
||||
}
|
||||
@@ -4,17 +4,16 @@ import confirm from './confirm';
|
||||
import getProjectByIdOrName from '../projects/get-project-by-id-or-name';
|
||||
import chalk from 'chalk';
|
||||
import { ProjectNotFound } from '../../util/errors-ts';
|
||||
import { Output } from '../output';
|
||||
import { Project, Org } from '../../types';
|
||||
import slugify from '@sindresorhus/slugify';
|
||||
|
||||
export default async function inputProject(
|
||||
output: Output,
|
||||
client: Client,
|
||||
org: Org,
|
||||
detectedProjectName: string,
|
||||
autoConfirm: boolean
|
||||
): Promise<Project | string> {
|
||||
const { output } = client;
|
||||
const slugifiedName = slugify(detectedProjectName);
|
||||
|
||||
// attempt to auto-detect a project to link
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import inquirer from 'inquirer';
|
||||
import chalk from 'chalk';
|
||||
import inquirer from 'inquirer';
|
||||
import Prompt from 'inquirer/lib/prompts/base';
|
||||
|
||||
// Here we patch inquirer to use a `>` instead of the ugly green `?`
|
||||
|
||||
/* eslint-disable no-multiple-empty-lines, no-var, no-undef, no-eq-null, eqeqeq, semi */
|
||||
const getQuestion = function() {
|
||||
const getQuestion = function (this: Prompt) {
|
||||
var message = `${chalk.bold(`> ${this.opt.message}`)} `;
|
||||
|
||||
// Append the default if available, and if question isn't answered
|
||||
@@ -1,5 +1,8 @@
|
||||
import inquirer from 'inquirer';
|
||||
import chalk from 'chalk';
|
||||
import inquirer from 'inquirer';
|
||||
import Prompt from 'inquirer/lib/prompts/base';
|
||||
import Choice from 'inquirer/lib/objects/choice';
|
||||
import Separator from 'inquirer/lib/objects/separator';
|
||||
|
||||
/**
|
||||
* Here we patch inquirer with some tweaks:
|
||||
@@ -10,7 +13,7 @@ import chalk from 'chalk';
|
||||
*/
|
||||
|
||||
// adjusted from https://github.com/SBoudrias/Inquirer.js/blob/942908f17319343d1acc7b876f990797c5695918/packages/inquirer/lib/prompts/base.js#L126
|
||||
const getQuestion = function () {
|
||||
const getQuestion = function (this: Prompt) {
|
||||
let message = `${chalk.gray('?')} ${this.opt.message} `;
|
||||
|
||||
if (this.opt.type === 'confirm') {
|
||||
@@ -57,7 +60,7 @@ inquirer.prompt.prompts.list.prototype.render = function () {
|
||||
this.screen.render(message);
|
||||
};
|
||||
|
||||
function listRender(choices, pointer) {
|
||||
function listRender(choices: (Choice | Separator)[], pointer: number) {
|
||||
let output = '';
|
||||
let separatorOffset = 0;
|
||||
|
||||
@@ -89,7 +92,7 @@ function listRender(choices, pointer) {
|
||||
}
|
||||
|
||||
// adjusted from https://github.com/SBoudrias/Inquirer.js/blob/942908f17319343d1acc7b876f990797c5695918/packages/inquirer/lib/prompts/checkbox.js#L84
|
||||
inquirer.prompt.prompts.checkbox.prototype.render = function (error) {
|
||||
inquirer.prompt.prompts.checkbox.prototype.render = function (error?: string) {
|
||||
// Render question
|
||||
let message = this.getQuestion();
|
||||
let bottomContent = '';
|
||||
@@ -125,7 +128,7 @@ inquirer.prompt.prompts.checkbox.prototype.render = function (error) {
|
||||
this.screen.render(message, bottomContent);
|
||||
};
|
||||
|
||||
function renderChoices(choices, pointer) {
|
||||
function renderChoices(choices: (Choice | Separator)[], pointer: number) {
|
||||
let output = '';
|
||||
let separatorOffset = 0;
|
||||
|
||||
@@ -162,7 +165,7 @@ function renderChoices(choices, pointer) {
|
||||
}
|
||||
|
||||
// adjusted from https://github.com/SBoudrias/Inquirer.js/blob/942908f17319343d1acc7b876f990797c5695918/packages/inquirer/lib/prompts/input.js#L44
|
||||
inquirer.prompt.prompts.input.prototype.render = function (error) {
|
||||
inquirer.prompt.prompts.input.prototype.render = function (error?: string) {
|
||||
let bottomContent = '';
|
||||
let appendContent = '';
|
||||
let message = this.getQuestion();
|
||||
@@ -189,7 +192,7 @@ inquirer.prompt.prompts.input.prototype.render = function (error) {
|
||||
};
|
||||
|
||||
// adjusted from https://github.com/SBoudrias/Inquirer.js/blob/942908f17319343d1acc7b876f990797c5695918/packages/inquirer/lib/prompts/confirm.js#L64
|
||||
inquirer.prompt.prompts.confirm.prototype.render = function (answer) {
|
||||
inquirer.prompt.prompts.confirm.prototype.render = function (answer?: boolean) {
|
||||
let message = this.getQuestion();
|
||||
|
||||
if (this.status === 'answered') {
|
||||
@@ -1,7 +1,7 @@
|
||||
import Client from '../client';
|
||||
import inquirer from 'inquirer';
|
||||
import Client from '../client';
|
||||
import getUser from '../get-user';
|
||||
import getTeams from '../get-teams';
|
||||
import getTeams from '../teams/get-teams';
|
||||
import { User, Team, Org } from '../../types';
|
||||
|
||||
type Choice = { name: string; value: Org };
|
||||
|
||||
@@ -2,7 +2,6 @@ import { join, basename } from 'path';
|
||||
import chalk from 'chalk';
|
||||
import { remove } from 'fs-extra';
|
||||
import { ProjectLinkResult, ProjectSettings } from '../../types';
|
||||
import { VercelConfig } from '../dev/types';
|
||||
import {
|
||||
getLinkedProject,
|
||||
linkFolderToProject,
|
||||
@@ -23,18 +22,30 @@ import editProjectSettings from '../input/edit-project-settings';
|
||||
import stamp from '../output/stamp';
|
||||
import { EmojiLabel } from '../emoji';
|
||||
import createDeploy from '../deploy/create-deploy';
|
||||
import Now from '../index';
|
||||
import Now, { CreateOptions } from '../index';
|
||||
|
||||
export interface SetupAndLinkOptions {
|
||||
forceDelete?: boolean;
|
||||
autoConfirm?: boolean;
|
||||
successEmoji: EmojiLabel;
|
||||
setupMsg: string;
|
||||
projectName?: string;
|
||||
}
|
||||
|
||||
export default async function setupAndLink(
|
||||
client: Client,
|
||||
path: string,
|
||||
forceDelete: boolean,
|
||||
autoConfirm: boolean,
|
||||
successEmoji: EmojiLabel,
|
||||
setupMsg: string
|
||||
{
|
||||
forceDelete = false,
|
||||
autoConfirm = false,
|
||||
successEmoji,
|
||||
setupMsg,
|
||||
projectName,
|
||||
}: SetupAndLinkOptions
|
||||
): Promise<ProjectLinkResult> {
|
||||
const {
|
||||
authConfig: { token },
|
||||
localConfig,
|
||||
apiUrl,
|
||||
output,
|
||||
config,
|
||||
@@ -90,10 +101,9 @@ export default async function setupAndLink(
|
||||
throw err;
|
||||
}
|
||||
|
||||
const detectedProjectName = basename(path);
|
||||
const detectedProjectName = projectName || basename(path);
|
||||
|
||||
const projectOrNewProjectName = await inputProject(
|
||||
output,
|
||||
client,
|
||||
org,
|
||||
detectedProjectName,
|
||||
@@ -134,13 +144,9 @@ export default async function setupAndLink(
|
||||
return { status: 'error', exitCode: 1 };
|
||||
}
|
||||
|
||||
let localConfig: VercelConfig = {};
|
||||
if (client.localConfig && !(client.localConfig instanceof Error)) {
|
||||
localConfig = client.localConfig;
|
||||
}
|
||||
|
||||
config.currentTeam = org.type === 'team' ? org.id : undefined;
|
||||
const isZeroConfig = !localConfig.builds || localConfig.builds.length === 0;
|
||||
const isZeroConfig =
|
||||
!localConfig || !localConfig.builds || localConfig.builds.length === 0;
|
||||
|
||||
try {
|
||||
let settings: ProjectSettings = {};
|
||||
@@ -153,16 +159,15 @@ export default async function setupAndLink(
|
||||
output,
|
||||
currentTeam: config.currentTeam,
|
||||
});
|
||||
const createArgs: any = {
|
||||
const createArgs: CreateOptions = {
|
||||
name: newProjectName,
|
||||
env: {},
|
||||
build: { env: {} },
|
||||
forceNew: undefined,
|
||||
withCache: undefined,
|
||||
quiet,
|
||||
wantsPublic: localConfig.public,
|
||||
wantsPublic: localConfig?.public || false,
|
||||
isFile,
|
||||
type: null,
|
||||
nowConfig: localConfig,
|
||||
regions: undefined,
|
||||
meta: {},
|
||||
@@ -171,7 +176,7 @@ export default async function setupAndLink(
|
||||
skipAutoDetectionConfirmation: false,
|
||||
};
|
||||
|
||||
if (!localConfig.builds || localConfig.builds.length === 0) {
|
||||
if (isZeroConfig) {
|
||||
// Only add projectSettings for zero config deployments
|
||||
createArgs.projectSettings = { sourceFilesOutsideRootDirectory };
|
||||
}
|
||||
|
||||
@@ -2,6 +2,11 @@ import chalk from 'chalk';
|
||||
import title from 'title';
|
||||
import bytes from 'bytes';
|
||||
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
|
||||
const longestState = 12;
|
||||
@@ -13,21 +18,24 @@ const padding = 8;
|
||||
const MAX_BUILD_GROUPS = 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
|
||||
const getCommonPath = (buildGroup) => {
|
||||
const getCommonPath = (buildGroup: Build[]) => {
|
||||
const commonPath = [];
|
||||
const splits = buildGroup.map((build) => getDirPath(build.entrypoint).split('/'));
|
||||
const shortest = splits.reduce((prevValue, currentValue) =>
|
||||
prevValue.length < currentValue.length
|
||||
? prevValue.length
|
||||
: currentValue.length
|
||||
const splits = buildGroup.map(build =>
|
||||
getDirPath(build.entrypoint).split('/')
|
||||
);
|
||||
const shortest = splits.reduce(
|
||||
(prevValue, currentValue) => Math.min(prevValue, currentValue.length),
|
||||
Infinity
|
||||
);
|
||||
|
||||
for (let i = 0; i <= shortest; i++) {
|
||||
const first = splits[0][i];
|
||||
if (splits.every((pathParts) => pathParts[i] === first)) {
|
||||
if (splits.every(pathParts => pathParts[i] === first)) {
|
||||
commonPath.push(first);
|
||||
continue;
|
||||
}
|
||||
@@ -38,58 +46,63 @@ const getCommonPath = (buildGroup) => {
|
||||
return commonPath.join('/') || '/';
|
||||
};
|
||||
|
||||
const styleBuild = (build, times, longestSource) => {
|
||||
const { entrypoint, readyState, id, hasOutput } = build;
|
||||
const styleBuild = (build: Build, times: Times, longestSource: number) => {
|
||||
const { entrypoint, readyState, id } = build;
|
||||
const state = prepareState(readyState).padEnd(longestState + padding);
|
||||
const time = typeof times[id] === 'string' ? times[id] : '';
|
||||
|
||||
let stateColor = chalk.grey;
|
||||
let pathColor = chalk.cyan;
|
||||
|
||||
if (isReady({ readyState })) {
|
||||
stateColor = item => item;
|
||||
} else if (isFailed({ readyState })) {
|
||||
if (isReady(build)) {
|
||||
stateColor = chalk;
|
||||
} else if (isFailed(build)) {
|
||||
stateColor = chalk.red;
|
||||
pathColor = chalk.red;
|
||||
}
|
||||
|
||||
const entry = entrypoint.padEnd(longestSource + padding);
|
||||
const prefix = hasOutput ? '┌' : '╶';
|
||||
const prefix = hasOutput(build) ? '┌' : '╶';
|
||||
|
||||
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 entry = commonPath.padEnd(longestSource + padding);
|
||||
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
|
||||
const stateMap = {
|
||||
const stateMap: { [readyState: string]: number } = {
|
||||
READY: 0,
|
||||
ERROR: 0,
|
||||
BUILDING: 0
|
||||
BUILDING: 0,
|
||||
};
|
||||
|
||||
buildGroup.map(({ readyState }) => {
|
||||
stateMap[readyState] = stateMap[readyState]
|
||||
? stateMap[readyState] + 1
|
||||
: 1;
|
||||
for (const { readyState } of buildGroup) {
|
||||
stateMap[readyState]++;
|
||||
}
|
||||
|
||||
return readyState;
|
||||
});
|
||||
let state = Object.keys(stateMap)
|
||||
.map(readyState => {
|
||||
const counter = stateMap[readyState];
|
||||
const name = prepareState(readyState);
|
||||
|
||||
let state = Object.keys(stateMap).map((readyState) => {
|
||||
const counter = stateMap[readyState];
|
||||
const name = prepareState(readyState);
|
||||
if (!counter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!counter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return `${counter > 9 ? '9+' : counter} ${name}`;
|
||||
}).filter(s => s).join(', ')
|
||||
return `${counter > 9 ? '9+' : counter} ${name}`;
|
||||
})
|
||||
.filter(s => s)
|
||||
.join(', ');
|
||||
|
||||
// Since the longestState might still be shorter
|
||||
// than multiple states we still want to ensure
|
||||
@@ -100,7 +113,7 @@ const styleHiddenBuilds = (commonPath, buildGroup, times, longestSource, isHidd
|
||||
let stateColor = chalk.grey;
|
||||
|
||||
if (buildGroup.every(isReady)) {
|
||||
stateColor = item => item;
|
||||
stateColor = chalk;
|
||||
} else if (buildGroup.every(isFailed)) {
|
||||
stateColor = 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}`;
|
||||
};
|
||||
|
||||
const styleOutput = (output) => {
|
||||
const { type, path, readyState, size, isLast, lambda } = output;
|
||||
const styleOutput = (
|
||||
output: BuildOutput,
|
||||
readyState: Build['readyState'],
|
||||
isLast: boolean
|
||||
) => {
|
||||
const { type, path, size, lambda } = output;
|
||||
const prefix = type === 'lambda' ? 'λ ' : '';
|
||||
const finalSize = size ? ` ${chalk.grey(`(${bytes(size)})`)}` : '';
|
||||
|
||||
@@ -122,7 +139,7 @@ const styleOutput = (output) => {
|
||||
let finalRegion = '';
|
||||
|
||||
if (isReady({ readyState })) {
|
||||
color = item => item;
|
||||
color = chalk;
|
||||
} else if (isFailed({ readyState })) {
|
||||
color = chalk.red;
|
||||
}
|
||||
@@ -141,7 +158,11 @@ const styleOutput = (output) => {
|
||||
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);
|
||||
|
||||
if (highestLevel === null || level === 0) {
|
||||
@@ -152,7 +173,7 @@ const getDirPath = (path, level = 0, highestLevel = null) => {
|
||||
return parts.slice(0, reverseLevel).join('/');
|
||||
};
|
||||
|
||||
const sortByEntrypoint = (a, b) => {
|
||||
const sortByEntrypoint = (a: Build, b: Build) => {
|
||||
const aPath = getDirPath(a.entrypoint);
|
||||
const bPath = getDirPath(b.entrypoint);
|
||||
|
||||
@@ -175,22 +196,30 @@ const sortByEntrypoint = (a, b) => {
|
||||
return 0;
|
||||
};
|
||||
|
||||
const groupBuilds = (buildList, highestLevel, counter) => {
|
||||
const currentIndex = counter % (buildList.length);
|
||||
const groupBuilds = (
|
||||
buildList: Build[][],
|
||||
highestLevel: number,
|
||||
counter: number
|
||||
) => {
|
||||
const currentIndex = counter % buildList.length;
|
||||
const __level = Math.ceil(counter / buildList.length);
|
||||
const _level = (__level === 0 ? 1 : __level) - 1;
|
||||
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 = [];
|
||||
let currentGroup = [];
|
||||
const currentGroup = [];
|
||||
|
||||
for (let i = 0; i < buildList.length; i++) {
|
||||
const group = buildList[i];
|
||||
const path = getDirPath(group[0].entrypoint, level, highestLevel);
|
||||
|
||||
if (path === currentPath) {
|
||||
currentGroup = currentGroup.concat(group);
|
||||
currentGroup.push(...group);
|
||||
} else {
|
||||
nextList.push(group);
|
||||
}
|
||||
@@ -205,26 +234,10 @@ const groupBuilds = (buildList, highestLevel, counter) => {
|
||||
return nextList;
|
||||
};
|
||||
|
||||
const prepareBuild = (build) => {
|
||||
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) => {
|
||||
export default (builds: Build[], times: Times) => {
|
||||
// Sort the builds by path
|
||||
// so that the grouping will be easier
|
||||
let path = builds
|
||||
.map(prepareBuild)
|
||||
.sort(sortByEntrypoint)
|
||||
.map(build => [build]);
|
||||
let path = builds.sort(sortByEntrypoint).map(build => [build]);
|
||||
|
||||
const highestLevel = builds.reduce((prev, curr) => {
|
||||
const partCounter = curr.entrypoint.split('/').length - 1;
|
||||
@@ -251,28 +264,28 @@ export default (builds, times) => {
|
||||
const final = [];
|
||||
let finalBuildsLength = path.length;
|
||||
let lengthWithoutRootPaths = path.length;
|
||||
let hiddenBuildGroup = [];
|
||||
let hiddenBuildGroup: Build[] = [];
|
||||
|
||||
// Ungroup the root files
|
||||
path = (() => {
|
||||
const nextList = [];
|
||||
const rootList = [];
|
||||
const rootList: Build[][] = [];
|
||||
|
||||
for (const group of path) {
|
||||
if (getCommonPath(group) === '/') {
|
||||
group.map((item) => rootList.push([item]));
|
||||
group.map(item => rootList.push([item]));
|
||||
} else {
|
||||
nextList.push(group);
|
||||
}
|
||||
}
|
||||
|
||||
lengthWithoutRootPaths = nextList.length;
|
||||
rootList.map((group) => nextList.push(group));
|
||||
rootList.map(group => nextList.push(group));
|
||||
|
||||
return nextList;
|
||||
})();
|
||||
|
||||
path.map((buildGroup) => {
|
||||
for (const buildGroup of path) {
|
||||
const commonPath = getCommonPath(buildGroup);
|
||||
|
||||
// All items with the common path / are a single group
|
||||
@@ -286,51 +299,63 @@ export default (builds, times) => {
|
||||
finalBuildsLength++;
|
||||
} else {
|
||||
hiddenBuildGroup.push(buildGroup[0]);
|
||||
return buildGroup;
|
||||
continue;
|
||||
}
|
||||
} else if (buildGroup.length === 1) {
|
||||
const item = buildGroup[0];
|
||||
final.push(`${styleBuild(item, times, longestSource)}\n`);
|
||||
finalBuildsLength++;
|
||||
} else {
|
||||
final.push(`${styleHiddenBuilds(`${commonPath}/*`, buildGroup, times, longestSource)}\n`);
|
||||
final.push(
|
||||
`${styleHiddenBuilds(
|
||||
`${commonPath}/*`,
|
||||
buildGroup,
|
||||
times,
|
||||
longestSource
|
||||
)}\n`
|
||||
);
|
||||
finalBuildsLength++;
|
||||
}
|
||||
|
||||
// Get the first five outputs when the deployment is ready
|
||||
const outputs = buildGroup.reduce((prevValue, currentValue) => (
|
||||
prevValue.concat(Array.isArray(currentValue.output)
|
||||
? currentValue.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`));
|
||||
const outputs: BuildOutput[] = [];
|
||||
for (const build of buildGroup) {
|
||||
if (Array.isArray(build.output)) {
|
||||
outputs.push(...build.output);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
final.push(`${styleHiddenBuilds(
|
||||
`${hiddenBuildGroup.length} builds hidden`,
|
||||
hiddenBuildGroup,
|
||||
times,
|
||||
longestSource,
|
||||
true
|
||||
)}\n`);
|
||||
final.push(
|
||||
`${styleHiddenBuilds(
|
||||
`${hiddenBuildGroup.length} builds hidden`,
|
||||
hiddenBuildGroup,
|
||||
times,
|
||||
longestSource,
|
||||
true
|
||||
)}\n`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
lines: final.length + 1,
|
||||
toPrint: `${final.join('')}`
|
||||
toPrint: `${final.join('')}`,
|
||||
};
|
||||
};
|
||||
@@ -1,8 +1,6 @@
|
||||
const chars = {
|
||||
// in some setups now.exe crashes if we use
|
||||
// the normal tick unicode character :|
|
||||
tick: process.platform === 'win32' ? '√' : '✔',
|
||||
cross: process.platform === 'win32' ? '☓' : '✘'
|
||||
};
|
||||
cross: process.platform === 'win32' ? '☓' : '✘',
|
||||
} as const;
|
||||
|
||||
export default chars;
|
||||
|
||||
@@ -2,45 +2,54 @@ import chalk from 'chalk';
|
||||
import boxen from 'boxen';
|
||||
import renderLink from './link';
|
||||
import wait, { StopSpinner } from './wait';
|
||||
|
||||
export type Output = ReturnType<typeof _createOutput>;
|
||||
import { Writable } from 'stream';
|
||||
|
||||
export interface OutputOptions {
|
||||
debug?: boolean;
|
||||
}
|
||||
|
||||
// Singleton
|
||||
let instance: Output | null = null;
|
||||
|
||||
export default function createOutput(opts?: OutputOptions) {
|
||||
if (!instance) {
|
||||
instance = _createOutput(opts);
|
||||
}
|
||||
return instance;
|
||||
export interface PrintOptions {
|
||||
w?: Writable;
|
||||
}
|
||||
|
||||
function _createOutput({ debug: debugEnabled = false }: OutputOptions = {}) {
|
||||
let spinnerMessage = '';
|
||||
let spinner: StopSpinner | null = null;
|
||||
export interface LogOptions extends PrintOptions {
|
||||
color?: typeof chalk;
|
||||
}
|
||||
|
||||
function isDebugEnabled() {
|
||||
return debugEnabled;
|
||||
export class Output {
|
||||
private debugEnabled: boolean;
|
||||
private spinnerMessage: string;
|
||||
private _spinner: StopSpinner | null;
|
||||
|
||||
constructor({ debug: debugEnabled = false }: OutputOptions = {}) {
|
||||
this.debugEnabled = debugEnabled;
|
||||
this.spinnerMessage = '';
|
||||
this._spinner = null;
|
||||
}
|
||||
|
||||
function print(str: string) {
|
||||
stopSpinner();
|
||||
process.stderr.write(str);
|
||||
get isTTY() {
|
||||
return process.stdout.isTTY;
|
||||
}
|
||||
|
||||
function log(str: string, color = chalk.grey) {
|
||||
print(`${color('>')} ${str}\n`);
|
||||
}
|
||||
isDebugEnabled = () => {
|
||||
return this.debugEnabled;
|
||||
};
|
||||
|
||||
function dim(str: string, color = chalk.grey) {
|
||||
print(`${color(`> ${str}`)}\n`);
|
||||
}
|
||||
print = (str: string, { w }: PrintOptions = { w: process.stderr }) => {
|
||||
this.stopSpinner();
|
||||
const stream: Writable = w || process.stderr;
|
||||
stream.write(str);
|
||||
};
|
||||
|
||||
function warn(
|
||||
log = (str: string, color = chalk.grey) => {
|
||||
this.print(`${color('>')} ${str}\n`);
|
||||
};
|
||||
|
||||
dim = (str: string, color = chalk.grey) => {
|
||||
this.print(`${color(`> ${str}`)}\n`);
|
||||
};
|
||||
|
||||
warn = (
|
||||
str: string,
|
||||
slug: string | null = null,
|
||||
link: string | null = null,
|
||||
@@ -48,10 +57,10 @@ function _createOutput({ debug: debugEnabled = false }: OutputOptions = {}) {
|
||||
options?: {
|
||||
boxen?: boxen.Options;
|
||||
}
|
||||
) {
|
||||
) => {
|
||||
const details = slug ? `https://err.sh/vercel/${slug}` : link;
|
||||
|
||||
print(
|
||||
this.print(
|
||||
boxen(
|
||||
chalk.bold.yellow('WARN! ') +
|
||||
str +
|
||||
@@ -68,110 +77,99 @@ function _createOutput({ debug: debugEnabled = false }: OutputOptions = {}) {
|
||||
}
|
||||
)
|
||||
);
|
||||
print('\n');
|
||||
}
|
||||
this.print('\n');
|
||||
};
|
||||
|
||||
function note(str: string) {
|
||||
log(chalk`{yellow.bold NOTE:} ${str}`);
|
||||
}
|
||||
note = (str: string) => {
|
||||
this.log(chalk`{yellow.bold NOTE:} ${str}`);
|
||||
};
|
||||
|
||||
function error(
|
||||
error = (
|
||||
str: string,
|
||||
slug?: string,
|
||||
link?: string,
|
||||
action = 'Learn More'
|
||||
) {
|
||||
print(`${chalk.red(`Error!`)} ${str}\n`);
|
||||
) => {
|
||||
this.print(`${chalk.red(`Error!`)} ${str}\n`);
|
||||
const details = slug ? `https://err.sh/vercel/${slug}` : link;
|
||||
if (details) {
|
||||
print(`${chalk.bold(action)}: ${renderLink(details)}\n`);
|
||||
this.print(`${chalk.bold(action)}: ${renderLink(details)}\n`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function prettyError(err: Error & { link?: string; action?: string }) {
|
||||
return error(err.message, undefined, err.link, err.action);
|
||||
}
|
||||
prettyError = (
|
||||
err: Pick<Error, 'message'> & { link?: string; action?: string }
|
||||
) => {
|
||||
return this.error(err.message, undefined, err.link, err.action);
|
||||
};
|
||||
|
||||
function ready(str: string) {
|
||||
print(`${chalk.cyan('> Ready!')} ${str}\n`);
|
||||
}
|
||||
ready = (str: string) => {
|
||||
this.print(`${chalk.cyan('> Ready!')} ${str}\n`);
|
||||
};
|
||||
|
||||
function success(str: string) {
|
||||
print(`${chalk.cyan('> Success!')} ${str}\n`);
|
||||
}
|
||||
success = (str: string) => {
|
||||
this.print(`${chalk.cyan('> Success!')} ${str}\n`);
|
||||
};
|
||||
|
||||
function debug(str: string) {
|
||||
if (debugEnabled) {
|
||||
log(
|
||||
debug = (str: string) => {
|
||||
if (this.debugEnabled) {
|
||||
this.log(
|
||||
`${chalk.bold('[debug]')} ${chalk.gray(
|
||||
`[${new Date().toISOString()}]`
|
||||
)} ${str}`
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function setSpinner(message: string, delay: number = 300): void {
|
||||
spinnerMessage = message;
|
||||
if (debugEnabled) {
|
||||
debug(`Spinner invoked (${message}) with a ${delay}ms delay`);
|
||||
spinner = (message: string, delay: number = 300): void => {
|
||||
this.spinnerMessage = message;
|
||||
if (this.debugEnabled) {
|
||||
this.debug(`Spinner invoked (${message}) with a ${delay}ms delay`);
|
||||
return;
|
||||
}
|
||||
if (spinner) {
|
||||
spinner.text = message;
|
||||
if (this._spinner) {
|
||||
this._spinner.text = message;
|
||||
} else {
|
||||
spinner = wait(message, delay);
|
||||
this._spinner = wait(message, delay);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function stopSpinner() {
|
||||
if (debugEnabled && spinnerMessage) {
|
||||
const msg = `Spinner stopped (${spinnerMessage})`;
|
||||
spinnerMessage = '';
|
||||
debug(msg);
|
||||
stopSpinner = () => {
|
||||
if (this.debugEnabled && this.spinnerMessage) {
|
||||
const msg = `Spinner stopped (${this.spinnerMessage})`;
|
||||
this.spinnerMessage = '';
|
||||
this.debug(msg);
|
||||
}
|
||||
if (spinner) {
|
||||
spinner();
|
||||
spinner = null;
|
||||
spinnerMessage = '';
|
||||
if (this._spinner) {
|
||||
this._spinner();
|
||||
this._spinner = null;
|
||||
this.spinnerMessage = '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
async function time<T>(
|
||||
time = async <T>(
|
||||
label: string | ((r?: T) => string),
|
||||
fn: Promise<T> | (() => Promise<T>)
|
||||
) {
|
||||
) => {
|
||||
const promise = typeof fn === 'function' ? fn() : fn;
|
||||
|
||||
if (debugEnabled) {
|
||||
if (this.debugEnabled) {
|
||||
const startLabel = typeof label === 'function' ? label() : label;
|
||||
debug(startLabel);
|
||||
this.debug(startLabel);
|
||||
const start = Date.now();
|
||||
const r = await promise;
|
||||
const endLabel = typeof label === 'function' ? label(r) : label;
|
||||
const duration = Date.now() - start;
|
||||
const durationPretty =
|
||||
duration < 1000 ? `${duration}ms` : `${(duration / 1000).toFixed(2)}s`;
|
||||
debug(`${endLabel} ${chalk.gray(`[${durationPretty}]`)}`);
|
||||
this.debug(`${endLabel} ${chalk.gray(`[${durationPretty}]`)}`);
|
||||
return r;
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
return {
|
||||
isDebugEnabled,
|
||||
print,
|
||||
log,
|
||||
warn,
|
||||
error,
|
||||
prettyError,
|
||||
ready,
|
||||
success,
|
||||
debug,
|
||||
dim,
|
||||
time,
|
||||
note,
|
||||
spinner: setSpinner,
|
||||
stopSpinner,
|
||||
};
|
||||
}
|
||||
|
||||
export default function createOutput(opts?: OutputOptions) {
|
||||
return new Output(opts);
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { gray } from 'chalk';
|
||||
|
||||
const effect = msg => `${gray(`+ ${msg}`)}`;
|
||||
|
||||
export default effect;
|
||||
@@ -5,7 +5,9 @@ import renderLink from './link';
|
||||
|
||||
const metric = metrics();
|
||||
|
||||
export default function error(...input: string[] | [APIError]) {
|
||||
export default function error(
|
||||
...input: string[] | [Pick<APIError, 'slug' | 'message' | 'link' | 'action'>]
|
||||
) {
|
||||
let messages = input;
|
||||
if (typeof input[0] === 'object') {
|
||||
const { slug, message, link, action = 'Learn More' } = input[0];
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
//
|
||||
import formatLogText from './format-log-text';
|
||||
|
||||
export default function formatLogCmd(text) {
|
||||
return `▲ ${formatLogText(text)}`;
|
||||
}
|
||||
@@ -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(/^> /, '')}`);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
//
|
||||
export default function formatLogText(text) {
|
||||
return text.replace(/\n$/, '').replace(/^\n/, '');
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
export default (input, level) => {
|
||||
export default (input: string, level: number) => {
|
||||
const fill = ' '.repeat(level);
|
||||
return `${fill}${input.replace(/\n/g, `\n${fill}`)}`;
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
import { yellow } from 'chalk';
|
||||
|
||||
const note = msg => `${yellow('> NOTE:')} ${msg}`;
|
||||
|
||||
export default note;
|
||||
3
packages/cli/src/util/output/note.ts
Normal file
3
packages/cli/src/util/output/note.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { yellow } from 'chalk';
|
||||
|
||||
export default (msg: string) => `${yellow('> NOTE:')} ${msg}`;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user