Compare commits

...

30 Commits

Author SHA1 Message Date
Steven
2b62de803f Publish Stable
- @vercel/build-utils@2.9.0
 - vercel@21.2.3
 - @vercel/client@9.0.6
2021-02-04 15:43:26 -05:00
Steven
196433ec9d Publish Canary
- @vercel/build-utils@2.8.1-canary.1
 - vercel@21.2.3-canary.0
 - @vercel/client@9.0.6-canary.4
2021-02-04 14:26:59 -05:00
Steven
0cdd571087 [build-utils] Add support for Node.js 14, warn for Node.js 10 (#5761)
### Related

Related to #4754 

### Upstream

https://aws.amazon.com/blogs/compute/node-js-14-x-runtime-now-available-in-aws-lambda/

### 📋 Checklist


#### Tests

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

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR
2021-02-04 14:25:16 -05:00
Steven
5e2227507d [docs] Fix links to GH Discussion categories (#5777) 2021-02-01 12:08:57 -05:00
Nathan Rajlich
e3d478a471 Publish Stable
- vercel@21.2.2
 - @vercel/go@1.1.8
2021-01-29 16:57:35 -08:00
Steven
187d41434a Publish Canary
- vercel@21.2.2-canary.1
2021-01-29 18:55:54 -05:00
Steven
f6fde2d351 [cli] Update error message when project is not found (#5770)
Both `vc` and `vc dev` already ask to link/setup when the project is not found, so this will update the error message to mention the possible reasons why a project was not found.

### Before

```
$ vc
Vercel CLI 21.2.1
️ Your project was either removed from Vercel or you’re not a member of it anymore.
? Set up and deploy “~/Code/myproject”? [Y/n]
```

### After

```
$ vc
Vercel CLI 21.2.1
️ Your Project was either deleted, transferred to a new Team, or you don’t have access to it anymore.
? Set up and deploy “~/Code/myproject”? [Y/n]
```


### 📋 Checklist



#### Tests

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

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR
2021-01-29 18:54:30 -05:00
Steven
e09ceca95d Publish Canary
- @vercel/build-utils@2.8.1-canary.0
 - @vercel/cgi@1.0.7-canary.0
 - vercel@21.2.2-canary.0
 - @vercel/client@9.0.6-canary.3
 - @vercel/go@1.1.8-canary.0
 - @vercel/node@1.9.1-canary.0
 - @vercel/python@1.2.5-canary.1
 - @vercel/ruby@1.2.6-canary.0
2021-01-29 12:40:38 -05:00
Igor Klopov
d9318af654 [go] support skipDownload flag (#5767) 2021-01-29 12:37:06 -05:00
Nathan Rajlich
679cd6f14e [cli] Use output.stopSpinner() interface consistently (#5762)
There was an issue with `output.spinner()` spinner if the old-style
interface was used (invoking the return value to stop the spinner),
which caused the internal spinner instance in `output` to be defined,
but in a "stopped" state. Then later the `text` gets updated to a
different value but the spinner is still stopped and nothing gets
rendered.

The fix is to ensure that `output.stopSpinner()` is called rather
than directly invoking the cancel function. In fact, the return
value of `output.spinner()` has been removed completely.
2021-01-28 14:16:17 -08:00
Nathan Rajlich
47c7a13324 [all] Rewrite build scripts in JavaScript (#5763)
Fixes these strange Windows failures that randomly started happening:

https://github.com/vercel/vercel/pull/5761/checks?check_run_id=1780742477
2021-01-28 19:20:07 +00:00
Nathan Rajlich
f498d25d8b Publish Stable
- vercel@21.2.1
2021-01-27 14:26:52 -08:00
Nathan Rajlich
726d3924ae Publish Canary
- vercel@21.2.1-canary.0
 - @vercel/python@1.2.5-canary.0
2021-01-26 18:31:12 -08:00
Nathan Rajlich
d3e6c2d335 [python] Fix request URL path encoding for basic, WSGI and ASGI interfaces (#5739)
`@vercel/python` has been incorrectly "unquoting" the incoming request
URL for all Python interfaces (basic, WSGI, ASGI). The proper behavior is
to pass along the request URL to the serverless function handler unmodified.

This is proven by the additional test that is added in this PR, which actually
fires up the corresponding Python server via i.e. `python3 api/asgi.py`,
and executes the test probes defined in the
`test/fixtures/00-request-path/vercel.json` file. Adding this test ensures
that the request URL behavior of `@vercel/python` matches the behavior
as compared to executing via `python` directly.
2021-01-26 12:37:15 -08:00
Nathan Rajlich
f9ed84a5c9 [cli] Pass the output object to vercel switch command (#5756)
Fixes error: `Cannot read property 'spinner' of undefined`
2021-01-26 12:34:08 -08:00
Nathan Rajlich
88a815b5f0 Publish Stable
- vercel@21.2.0
2021-01-25 14:44:41 -08:00
Steven
9f49743ea4 Publish Stable
- @vercel/build-utils@2.8.0
2021-01-22 18:53:38 -05:00
Steven
0ac3ae19c0 Publish Canary
- @vercel/build-utils@2.7.1-canary.2
 - vercel@21.1.1-canary.3
 - @vercel/client@9.0.6-canary.2
2021-01-22 18:48:09 -05:00
Steven
e7920fd783 [build-utils] Fix warning message (#5741) 2021-01-22 18:47:33 -05:00
Steven
b4c13470df Publish Canary
- @vercel/build-utils@2.7.1-canary.1
 - vercel@21.1.1-canary.2
 - @vercel/client@9.0.6-canary.1
2021-01-22 11:59:09 -05:00
Steven
2a797b77b9 [build-utils] Add support for config.nodeVersion (#5729)
### 📋 Checklist

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

#### Tests

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

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR
2021-01-22 11:57:55 -05:00
Nathan Rajlich
85a34126df Publish Canary
- vercel@21.1.1-canary.1
2021-01-21 22:48:10 -08:00
Nathan Rajlich
92889c5376 [cli] Re-use a single Output instance throughout the codebase (#5722)
Previously every command implementation was invoking `createOutput()` individually, causing multiple instances of `Output` to be created, which was unnecessary, and causes issues with the shared `spinner` instance introduced by #5717.

Now, the `Output` instance created in `src/index.js` is passed down to the sub-command being executed and therefore re-used.
2021-01-21 19:57:08 -08:00
Nathan Rajlich
66a7fa30b8 [cli] Show login prompt for vercel dev (#5718)
`vercel dev` requires the user to be logged in ever since the project needs to be queried from the database.

**Before:**

<img width="682" alt="Screen Shot 2021-01-18 at 7 58 56 PM" src="https://user-images.githubusercontent.com/71256/104986545-14aac800-59c8-11eb-8302-836a5481ce5f.png">

**After:**

<img width="497" alt="Screen Shot 2021-01-18 at 7 59 01 PM" src="https://user-images.githubusercontent.com/71256/104986547-15dbf500-59c8-11eb-85f6-d0ad628c8e8d.png">
2021-01-21 19:56:35 -08:00
Nathan Rajlich
a7ba405503 [cli] Make Output class a singleton and have reusable Spinner (#5717)
This is a refactor of the `ora` Spinner usage to make the preferred usage be via the `output.spinner()` and `output.stopSpinner()` functions.

* The `Output` instance has a local spinner instance that can be updated via multiple calls to `output.spinner()`.
* `output.print()` and friends call `output.stopSpinner()` implicitly, so `output.stopSpinner()` doesn't need to be called unless it is desired to immediately remove the spinner before rendering the next text.
* Because there's meant to be a shared `spinner` for the `output` instance, it means that the same `output` instance needs to be used throughout the CLI lifecycle. Therefore, the `createOutput()` function now returns a singleton so that it's guaranteed to be the same instance.

https://user-images.githubusercontent.com/71256/105009979-0670a200-59f0-11eb-9853-232f92eae536.mov

https://user-images.githubusercontent.com/71256/105010034-17211800-59f0-11eb-918d-b15f457e9640.mov

In the "before" video above, note how there's a brief second where the spinner is cleared, but the deployment URL has not yet been printed. In the "after" video, this is fixed and the spinner is only cleared once the CLI is ready to render the deployment URL.
2021-01-19 11:38:29 -08:00
luc
84145245ba Publish Canary
- @vercel/build-utils@2.7.1-canary.0
 - vercel@21.1.1-canary.0
 - @vercel/client@9.0.6-canary.0
2021-01-19 09:29:20 +01:00
Luc Leray
90d2e8b63b [cli] Hide "Development" for secret env variables (#5710) 2021-01-19 09:27:42 +01:00
Nathan Rajlich
0d31fe8018 [cli] Fix cleanUrls: true when using outputDirectory (#5701)
This is essentially a refactor of the `@vercel/static` builder that
simplifies the code a lot and fixes a bug where `cleanUrls: true`
was not working correctly when using an `outputDirectory`.
2021-01-18 12:03:34 -08:00
Mark Glagola
37d747c241 [cli] Add auto renew prompt to domains buy (#5663)
Add auto renew prompt to `domains buy`

```diff
 > The domain "example.com" is available to buy under <username>!
 > Buy now for $20 (1yr)? [y|N] 
+ > Auto renew yearly for $20? [Y|n] 
```

### 📋 Checklist

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2021-01-15 19:13:37 +00:00
Nathan Rajlich
3f052d905f [build-utils] Remove now-next and now-static-build tests (#5695)
These directories are no longer in this repository, so the tests were failing.
2021-01-14 13:50:19 +00:00
123 changed files with 1160 additions and 746 deletions

View File

@@ -4,8 +4,8 @@ contact_links:
url: https://vercel.com/support/request
about: Report a bug using the Vercel support form
- name: Feature Request
url: https://github.com/vercel/vercel/discussions/new?category_id=66161
url: https://github.com/vercel/vercel/discussions/new?category=ideas
about: Share ideas for new features
- name: Ask a Question
url: https://github.com/vercel/vercel/discussions/new?category_id=66160
url: https://github.com/vercel/vercel/discussions/new?category=help
about: Ask the community for help

View File

@@ -11,7 +11,7 @@ on:
jobs:
test:
name: Dev
timeout-minutes: 45
timeout-minutes: 60
strategy:
fail-fast: false
matrix:

View File

@@ -0,0 +1,30 @@
#!/usr/bin/env node
const fs = require('fs-extra');
const execa = require('execa');
const { join } = require('path');
async function main() {
const outDir = join(__dirname, 'dist');
// Start fresh
await fs.remove(outDir);
// Compile TypeScript
await execa('tsc', [], { stdio: 'inherit' });
// Run `ncc`
const mainDir = join(outDir, 'main');
await execa('ncc', ['build', 'src/index.ts', '-o', mainDir], {
stdio: 'inherit',
});
// Move compiled ncc file to out dir
await fs.rename(join(mainDir, 'index.js'), join(outDir, 'index.js'));
// Delete leftover "main" dir
await fs.remove(mainDir);
}
main().catch(err => {
console.error(err);
process.exit(1);
});

View File

@@ -1,13 +0,0 @@
#!/bin/bash
set -euo pipefail
out="dist"
rm -rf "$out"
tsc
rm "$out/index.js"
ncc build "src/index.ts" -o "$out/main"
mv "$out/main/index.js" "$out/index.js"
rm -rf "$out/main"

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "2.7.0",
"version": "2.9.0",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
@@ -11,10 +11,10 @@
"directory": "packages/now-build-utils"
},
"scripts": {
"build": "./build.sh",
"build": "node build",
"test-unit": "jest --env node --verbose --runInBand --bail test/unit.*test.*",
"test-integration-once": "jest --env node --verbose --runInBand --bail test/integration.test.js",
"prepublishOnly": "./build.sh"
"prepublishOnly": "node build"
},
"devDependencies": {
"@iarna/toml": "2.2.3",

View File

@@ -4,28 +4,34 @@ import { NodeVersion } from '../types';
import { NowBuildError } from '../errors';
import debug from '../debug';
const allOptions: NodeVersion[] = [
const allOptions = [
{ major: 14, range: '14.x', runtime: 'nodejs14.x' },
{ major: 12, range: '12.x', runtime: 'nodejs12.x' },
{ major: 10, range: '10.x', runtime: 'nodejs10.x' },
{
major: 10,
range: '10.x',
runtime: 'nodejs10.x',
discontinueDate: new Date('2021-03-30'),
},
{
major: 8,
range: '8.10.x',
runtime: 'nodejs8.10',
discontinueDate: new Date('2020-01-06'),
},
];
] as const;
const pleaseSet =
'Please set "engines": { "node": "' +
'Please change your Project Settings or set "engines": { "node": "' +
getLatestNodeVersion().range +
'" } in your `package.json` file to upgrade to Node.js ' +
'" } in your `package.json` file to use Node.js ' +
getLatestNodeVersion().major +
'.';
const upstreamProvider =
'This change is the result of a decision made by an upstream infrastructure provider (AWS).' +
'\nRead more: https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html';
export function getLatestNodeVersion(): NodeVersion {
export function getLatestNodeVersion() {
return allOptions[0];
}
@@ -37,7 +43,7 @@ export async function getSupportedNodeVersion(
engineRange?: string,
isAuto?: boolean
): Promise<NodeVersion> {
let selection = getLatestNodeVersion();
let selection: NodeVersion = getLatestNodeVersion();
if (engineRange) {
const found =

View File

@@ -165,8 +165,8 @@ export function getSpawnOptions(
export async function getNodeVersion(
destPath: string,
_nodeVersion?: string,
_config?: Config,
meta?: Meta
config: Config = {},
meta: Meta = {}
): Promise<NodeVersion> {
if (meta && meta.isDev) {
// Use the system-installed version of `node` in PATH for `vercel dev`
@@ -174,13 +174,22 @@ export async function getNodeVersion(
return { ...latest, runtime: 'nodejs' };
}
const { packageJson } = await scanParentDirs(destPath, true);
let range: string | undefined;
let { nodeVersion } = config;
let isAuto = true;
if (packageJson && packageJson.engines && packageJson.engines.node) {
range = packageJson.engines.node;
if (
nodeVersion &&
nodeVersion !== packageJson.engines.node &&
!meta.isDev
) {
console.warn(
'Warning: Due to `engines` existing in your `package.json` file, the Node.js Version defined in your Project Settings will not apply. Learn More: http://vercel.link/node-version'
);
}
nodeVersion = packageJson.engines.node;
isAuto = false;
}
return getSupportedNodeVersion(range, isAuto);
return getSupportedNodeVersion(nodeVersion, isAuto);
}
async function scanParentDirs(destPath: string, readPackageJson = false) {

View File

@@ -39,7 +39,7 @@ for (const fixture of fs.readdirSync(fixturesPath)) {
// few foreign tests
const buildersToTestWith = ['now-next', 'now-node', 'now-static-build'];
const buildersToTestWith = ['now-node'];
// eslint-disable-next-line no-restricted-syntax
for (const builder of buildersToTestWith) {

View File

@@ -0,0 +1,6 @@
{
"private": true,
"engines": {
"node": "14.x"
}
}

View File

@@ -24,6 +24,19 @@ async function expectBuilderError(promise, pattern) {
);
}
let warningMessages;
const originalConsoleWarn = console.warn;
beforeEach(() => {
warningMessages = [];
console.warn = m => {
warningMessages.push(m);
};
});
afterEach(() => {
console.warn = originalConsoleWarn;
});
it('should re-create symlinks properly', async () => {
if (process.platform === 'win32') {
console.log('Skipping test on windows');
@@ -81,6 +94,10 @@ it('should only match supported node versions', async () => {
'major',
12
);
expect(await getSupportedNodeVersion('14.x', false)).toHaveProperty(
'major',
14
);
expect(getSupportedNodeVersion('8.11.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('6.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('999.x', false)).rejects.toThrow();
@@ -104,6 +121,10 @@ it('should only match supported node versions', async () => {
'major',
12
);
expect(await getSupportedNodeVersion('14.x', true)).toHaveProperty(
'major',
14
);
const foundMessage = /Found `engines` in `package\.json` with an invalid Node\.js version range/;
await expectBuilderError(
getSupportedNodeVersion('8.11.x', false),
@@ -125,8 +146,8 @@ it('should match all semver ranges', async () => {
// See https://docs.npmjs.com/files/package.json#engines
expect(await getSupportedNodeVersion('10.0.0')).toHaveProperty('major', 10);
expect(await getSupportedNodeVersion('10.x')).toHaveProperty('major', 10);
expect(await getSupportedNodeVersion('>=10')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('>=10.3.0')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('>=10')).toHaveProperty('major', 14);
expect(await getSupportedNodeVersion('>=10.3.0')).toHaveProperty('major', 14);
expect(await getSupportedNodeVersion('8.5.0 - 10.5.0')).toHaveProperty(
'major',
10
@@ -150,20 +171,57 @@ it('should ignore node version in vercel dev getNodeVersion()', async () => {
).toHaveProperty('runtime', 'nodejs');
});
it('should select project setting from config when no package.json is found', async () => {
expect(
await getNodeVersion('/tmp', undefined, { nodeVersion: '14.x' }, {})
).toHaveProperty('range', '14.x');
expect(warningMessages).toStrictEqual([]);
});
it('should prefer package.json engines over project setting from config and warn', async () => {
expect(
await getNodeVersion(
path.join(__dirname, 'pkg-engine-node'),
undefined,
{ nodeVersion: '12.x' },
{}
)
).toHaveProperty('range', '14.x');
expect(warningMessages).toStrictEqual([
'Warning: Due to `engines` existing in your `package.json` file, the Node.js Version defined in your Project Settings will not apply. Learn More: http://vercel.link/node-version',
]);
});
it('should not warn when package.json engines matches project setting from config', async () => {
expect(
await getNodeVersion(
path.join(__dirname, 'pkg-engine-node'),
undefined,
{ nodeVersion: '14.x' },
{}
)
).toHaveProperty('range', '14.x');
expect(warningMessages).toStrictEqual([]);
});
it('should get latest node version', async () => {
expect(await getLatestNodeVersion()).toHaveProperty('major', 12);
expect(await getLatestNodeVersion()).toHaveProperty('major', 14);
});
it('should throw for discontinued versions', async () => {
// Mock a future date so that Node 8 becomes discontinued
const realDateNow = Date.now.bind(global.Date);
global.Date.now = () => new Date('2020-02-14').getTime();
global.Date.now = () => new Date('2021-04-01').getTime();
expect(getSupportedNodeVersion('8.10.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('8.10.x', true)).rejects.toThrow();
expect(getSupportedNodeVersion('10.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('10.x', true)).rejects.toThrow();
expect(getDiscontinuedNodeVersions().length).toBe(1);
expect(getDiscontinuedNodeVersions()[0]).toHaveProperty('range', '8.10.x');
const discontinued = getDiscontinuedNodeVersions();
expect(discontinued.length).toBe(2);
expect(discontinued[0]).toHaveProperty('range', '10.x');
expect(discontinued[1]).toHaveProperty('range', '8.10.x');
global.Date.now = realDateNow;
});

25
packages/now-cgi/build.js Normal file
View File

@@ -0,0 +1,25 @@
#!/usr/bin/env node
const execa = require('execa');
const { join } = require('path');
const { homedir } = require('os');
async function main() {
process.env.GOOS = 'linux';
process.env.GOARCH = 'amd64';
process.env.GOPATH = join(homedir(), 'go');
await execa('go', ['get', 'github.com/aws/aws-lambda-go/events'], {
stdio: 'inherit',
});
await execa('go', ['get', 'github.com/aws/aws-lambda-go/lambda'], {
stdio: 'inherit',
});
await execa('go', ['build', '-o', 'handler', 'main.go'], {
stdio: 'inherit',
});
}
main().catch(err => {
console.error(err);
process.exit(1);
});

View File

@@ -1,7 +0,0 @@
#!/usr/bin/env bash
export GOOS=linux
export GOARCH=amd64
export GOPATH=$HOME/go
go get github.com/aws/aws-lambda-go/events
go get github.com/aws/aws-lambda-go/lambda
go build -o handler main.go

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/cgi",
"version": "1.0.6",
"version": "1.0.7-canary.0",
"license": "MIT",
"repository": {
"type": "git",
@@ -8,7 +8,8 @@
"directory": "packages/now-cgi"
},
"scripts": {
"prepublish": "./build.sh"
"build": "node build",
"prepublishOnly": "node build"
},
"files": [
"index.js",

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "21.1.0",
"version": "21.2.3",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -61,11 +61,11 @@
"node": ">= 10"
},
"dependencies": {
"@vercel/build-utils": "2.7.0",
"@vercel/go": "1.1.7",
"@vercel/node": "1.9.0",
"@vercel/python": "1.2.4",
"@vercel/ruby": "1.2.5",
"@vercel/build-utils": "2.9.0",
"@vercel/go": "1.1.8",
"@vercel/node": "1.9.1-canary.0",
"@vercel/python": "1.2.5-canary.1",
"@vercel/ruby": "1.2.6-canary.0",
"update-notifier": "4.1.0"
},
"devDependencies": {

View File

@@ -2,7 +2,6 @@ import chalk from 'chalk';
import { handleError } from '../../util/error';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import logo from '../../util/output/logo';
@@ -92,15 +91,14 @@ export default async function main(ctx) {
return 2;
}
const output = createOutput({ debug: argv['--debug'] });
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
switch (subcommand) {
case 'ls':
return ls(ctx, argv, args, output);
return ls(ctx, argv, args);
case 'rm':
return rm(ctx, argv, args, output);
return rm(ctx, argv, args);
default:
return set(ctx, argv, args, output);
return set(ctx, argv, args);
}
}

View File

@@ -10,9 +10,10 @@ import strlen from '../../util/strlen.ts';
import getCommandFlags from '../../util/get-command-flags';
import { getCommandName } from '../../util/pkg-name.ts';
export default async function ls(ctx, opts, args, output) {
export default async function ls(ctx, opts, args) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
@@ -23,6 +24,7 @@ export default async function ls(ctx, opts, args, output) {
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;
@@ -47,9 +49,9 @@ export default async function ls(ctx, opts, args, output) {
token,
debug: debugEnabled,
currentTeam,
output,
});
const lsStamp = stamp();
let cancelWait;
if (args.length > 0) {
output.error(
@@ -60,17 +62,13 @@ export default async function ls(ctx, opts, args, output) {
return 1;
}
cancelWait = output.spinner(
`Fetching aliases under ${chalk.bold(contextName)}`
);
output.spinner(`Fetching aliases under ${chalk.bold(contextName)}`);
const { aliases, pagination } = await getAliases(
now,
undefined,
nextTimestamp
);
if (cancelWait) cancelWait();
output.log(`aliases found under ${chalk.bold(contextName)} ${lsStamp()}`);
console.log(printAliasTable(aliases));

View File

@@ -12,9 +12,10 @@ import { isValidName } from '../../util/is-valid-name';
import findAliasByAliasOrId from '../../util/alias/find-alias-by-alias-or-id';
import { getCommandName } from '../../util/pkg-name.ts';
export default async function rm(ctx, opts, args, output) {
export default async function rm(ctx, opts, args) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
@@ -25,6 +26,7 @@ export default async function rm(ctx, opts, args, output) {
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;
@@ -39,8 +41,13 @@ export default async function rm(ctx, opts, args, output) {
throw err;
}
// $FlowFixMe
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
const now = new Now({
apiUrl,
token,
debug: debugEnabled,
currentTeam,
output,
});
const [aliasOrId] = args;
if (args.length !== 1) {

View File

@@ -29,11 +29,11 @@ type Options = {
export default async function set(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
localConfig,
} = ctx;
@@ -49,6 +49,7 @@ export default async function set(
token,
currentTeam,
debug: debugEnabled,
output,
});
let user: User;

View File

@@ -14,7 +14,6 @@ import addBilling from './add';
import exit from '../../util/exit';
import Client from '../../util/client.ts';
import getScope from '../../util/get-scope.ts';
import createOutput from '../../util/output';
import { getPkgName } from '../../util/pkg-name.ts';
const help = () => {
@@ -90,8 +89,9 @@ function buildInquirerChoices(cards) {
const _default =
source.id === cards.defaultSource ? ` ${chalk.bold('(default)')}` : '';
const id = `${chalk.cyan(`ID: ${source.id}`)}${_default}`;
const number = `${chalk.gray('#### ').repeat(3)}${source.last4 ||
source.card.last4}`;
const number = `${chalk.gray('#### ').repeat(3)}${
source.last4 || source.card.last4
}`;
const str = [
id,
indent(source.name || source.owner.name, 2),
@@ -106,11 +106,16 @@ function buildInquirerChoices(cards) {
});
}
async function run({ token, config: { currentTeam } }) {
async function run({ token, output, config: { currentTeam } }) {
const start = new Date();
const creditCards = new NowCreditCards({ apiUrl, token, debug, currentTeam });
const output = createOutput({ debug });
const client = new Client({ apiUrl, token, currentTeam, debug });
const creditCards = new NowCreditCards({
apiUrl,
token,
debug,
currentTeam,
output,
});
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {
@@ -147,8 +152,9 @@ async function run({ token, config: { currentTeam } }) {
const id = `${chalk.gray('-')} ${chalk.cyan(
`ID: ${source.id}`
)}${_default}`;
const number = `${chalk.gray('#### ').repeat(3)}${source.last4 ||
source.card.last4}`;
const number = `${chalk.gray('#### ').repeat(3)}${
source.last4 || source.card.last4
}`;
return [
id,
@@ -231,8 +237,9 @@ async function run({ token, config: { currentTeam } }) {
const elapsed = ms(new Date() - start);
console.log(
success(
`${card.brand || card.card.brand} ending in ${card.last4 ||
card.card.last4} is now the default ${chalk.gray(`[${elapsed}]`)}`
`${card.brand || card.card.brand} ending in ${
card.last4 || card.card.last4
} is now the default ${chalk.gray(`[${elapsed}]`)}`
)
);
} else {
@@ -301,9 +308,9 @@ async function run({ token, config: { currentTeam } }) {
const deletedCard = cards.sources.find(card => card.id === cardId);
const remainingCards = cards.sources.filter(card => card.id !== cardId);
let text = `${deletedCard.brand ||
deletedCard.card.brand} ending in ${deletedCard.last4 ||
deletedCard.card.last4} was deleted`;
let text = `${deletedCard.brand || deletedCard.card.brand} ending in ${
deletedCard.last4 || deletedCard.card.last4
} was deleted`;
// ${chalk.gray(`[${elapsed}]`)}
if (cardId === cards.defaultSource) {
@@ -317,11 +324,11 @@ async function run({ token, config: { currentTeam } }) {
card => card.id === cards.defaultCardId
);
text += `\n${newDefaultCard.brand ||
newDefaultCard.card.brand} ending in ${newDefaultCard.last4 ||
newDefaultCard.card.last4} in now default for ${chalk.bold(
contextName
)}`;
text += `\n${
newDefaultCard.brand || newDefaultCard.card.brand
} ending in ${
newDefaultCard.last4 || newDefaultCard.card.last4
} in now default for ${chalk.bold(contextName)}`;
}
}

View File

@@ -8,7 +8,6 @@ import stamp from '../../util/output/stamp';
import createCertFromFile from '../../util/certs/create-cert-from-file';
import createCertForCns from '../../util/certs/create-cert-for-cns';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import { getCommandName } from '../../util/pkg-name';
interface Options {
@@ -22,11 +21,11 @@ interface Options {
async function add(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
): Promise<number> {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
@@ -49,6 +48,7 @@ async function add(
token,
currentTeam,
debug: debugEnabled,
output,
});
try {
@@ -62,7 +62,13 @@ async function add(
throw err;
}
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
const now = new Now({
apiUrl,
token,
debug: debugEnabled,
currentTeam,
output,
});
if (overwite) {
output.error('Overwrite option is deprecated');
@@ -71,7 +77,7 @@ async function add(
}
if (crtPath || keyPath || caPath) {
if (args.length !== 0 || (!crtPath || !keyPath || !caPath)) {
if (args.length !== 0 || !crtPath || !keyPath || !caPath) {
output.error(
`Invalid number of arguments to create a custom certificate entry. Usage:`
);
@@ -113,12 +119,12 @@ async function add(
(res, item) => res.concat(item.split(',')),
[]
);
const cancelWait = output.spinner(
output.spinner(
`Generating a certificate for ${chalk.bold(cns.join(', '))}`
);
cert = await createCertForCns(now, cns, contextName);
cancelWait();
output.stopSpinner();
}
if (cert instanceof Error) {

View File

@@ -3,7 +3,6 @@ import chalk from 'chalk';
// @ts-ignore
import { handleError } from '../../util/error';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import logo from '../../util/output/logo';
@@ -104,17 +103,17 @@ export default async function main(ctx: NowContext) {
return 0;
}
const output = createOutput({ debug: argv['--debug'] });
const { output } = ctx;
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
switch (subcommand) {
case 'issue':
return issue(ctx, argv, args, output);
return issue(ctx, argv, args);
case 'ls':
return ls(ctx, argv, args, output);
return ls(ctx, argv, args);
case 'rm':
return rm(ctx, argv, args, output);
return rm(ctx, argv, args);
case 'add':
return add(ctx, argv, args, output);
return add(ctx, argv, args);
case 'renew':
output.error('Renewing certificates is deprecated, issue a new one.');
return 1;

View File

@@ -28,11 +28,11 @@ type Options = {
export default async function issue(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
@@ -55,6 +55,7 @@ export default async function issue(
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;

View File

@@ -8,7 +8,6 @@ import getScope from '../../util/get-scope';
import stamp from '../../util/output/stamp';
import getCerts from '../../util/certs/get-certs';
import strlen from '../../util/strlen';
import { Output } from '../../util/output';
import { NowContext, Cert } from '../../types';
import getCommandFlags from '../../util/get-command-flags';
import { getCommandName } from '../../util/pkg-name';
@@ -21,17 +20,17 @@ interface Options {
async function ls(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
): Promise<number> {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const { '--debug': debug, '--next': nextTimestamp } = opts;
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {
@@ -48,7 +47,7 @@ async function ls(
output.error('Please provide a number for flag --next');
return 1;
}
const now = new Now({ apiUrl, token, debug, currentTeam });
const now = new Now({ apiUrl, token, debug, currentTeam, output });
const lsStamp = stamp();
if (args.length !== 0) {

View File

@@ -18,21 +18,17 @@ type Options = {
'--debug': boolean;
};
async function rm(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
) {
async function rm(ctx: NowContext, opts: Options, args: string[]) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const rmStamp = stamp();
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;

View File

@@ -3,7 +3,6 @@ import { resolve, basename } from 'path';
import { fileNameSymbol } from '@vercel/client';
import Client from '../../util/client.ts';
import getScope from '../../util/get-scope.ts';
import createOutput from '../../util/output';
import code from '../../util/output/code';
import highlight from '../../util/output/highlight';
import { readLocalConfig } from '../../util/config/files';
@@ -15,6 +14,7 @@ import deploy from './latest';
export default async ctx => {
const {
authConfig,
output,
config: { currentTeam },
apiUrl,
} = ctx;
@@ -48,7 +48,6 @@ export default async ctx => {
localConfig = readLocalConfig(paths[0]);
}
const debugEnabled = argv['--debug'];
const output = createOutput({ debug: debugEnabled });
const stats = {};
if (argv['--help']) {
@@ -74,6 +73,7 @@ export default async ctx => {
apiUrl,
token: authConfig.token,
currentTeam,
output,
debug: debugEnabled,
});
try {

View File

@@ -233,8 +233,7 @@ export default async function main(
const paths = Object.keys(stats);
const debugEnabled = argv['--debug'];
// $FlowFixMe
const isTTY = process.stdout.isTTY;
const { isTTY } = process.stdout;
const quiet = !isTTY;
// check paths
@@ -263,6 +262,7 @@ export default async function main(
apiUrl: ctx.apiUrl,
token: ctx.authConfig.token,
debug: debugEnabled,
output,
});
// retrieve `project` and `org` from .vercel
@@ -645,6 +645,7 @@ export default async function main(
token: ctx.authConfig.token,
currentTeam: org.id,
debug: debugEnabled,
output,
}),
err.meta.domain,
contextName
@@ -727,6 +728,7 @@ export default async function main(
token: ctx.authConfig.token,
currentTeam: org.type === 'team' ? org.id : null,
debug: debugEnabled,
output,
}),
deployment,
deployStamp,

View File

@@ -2,7 +2,6 @@ import { resolve, join } from 'path';
import DevServer from '../../util/dev/server';
import parseListen from '../../util/dev/parse-listen';
import { Output } from '../../util/output';
import { NowContext, ProjectEnvVariable } from '../../types';
import Client from '../../util/client';
import { getLinkedProject } from '../../util/projects/link';
@@ -22,9 +21,9 @@ type Options = {
export default async function dev(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const { output } = ctx;
const [dir = '.'] = args;
let cwd = resolve(dir);
const listen = parseListen(opts['--listen'] || '3000');
@@ -35,6 +34,7 @@ export default async function dev(
token: ctx.authConfig.token,
currentTeam: ctx.config.currentTeam,
debug,
output,
});
// retrieve dev command
@@ -49,7 +49,6 @@ export default async function dev(
link = await setupAndLink(
ctx,
output,
cwd,
forceDelete,
autoConfirm,

View File

@@ -7,7 +7,6 @@ import getSubcommand from '../../util/get-subcommand';
import { NowContext } from '../../types';
import { NowError } from '../../util/now-error';
import handleError from '../../util/handle-error';
import createOutput from '../../util/output/create-output';
import logo from '../../util/output/logo';
import cmd from '../../util/output/cmd';
import highlight from '../../util/output/highlight';
@@ -51,7 +50,7 @@ const help = () => {
export default async function main(ctx: NowContext) {
let argv;
let args;
let output;
const { output } = ctx;
try {
argv = getArgs(ctx.argv.slice(2), {
@@ -63,9 +62,7 @@ export default async function main(ctx: NowContext) {
'--port': Number,
'-p': '--port',
});
const debug = argv['--debug'];
args = getSubcommand(argv._.slice(1), COMMAND_CONFIG).args;
output = createOutput({ debug });
if ('--port' in argv) {
output.warn('`--port` is deprecated, please use `--listen` instead');
@@ -120,7 +117,7 @@ export default async function main(ctx: NowContext) {
}
try {
return await dev(ctx, argv, args, output);
return await dev(ctx, argv, args);
} catch (err) {
if (err.code === 'ENOTFOUND') {
// Error message will look like the following:

View File

@@ -6,7 +6,6 @@ import {
DNSInvalidType,
} from '../../util/errors-ts';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import addDNSRecord from '../../util/dns/add-dns-record';
import Client from '../../util/client';
import getScope from '../../util/get-scope';
@@ -22,17 +21,17 @@ type Options = {
export default async function add(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
apiUrl,
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {

View File

@@ -1,6 +1,5 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import Client from '../../util/client';
import getScope from '../../util/get-scope';
import { DomainNotFound, InvalidDomain } from '../../util/errors-ts';
@@ -15,17 +14,17 @@ type Options = {
export default async function add(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
apiUrl,
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import handleError from '../../util/handle-error';
@@ -112,16 +111,15 @@ export default async function main(ctx: NowContext) {
return 2;
}
const output = createOutput({ debug: argv['--debug'] });
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
switch (subcommand) {
case 'add':
return add(ctx, argv, args, output);
return add(ctx, argv, args);
case 'import':
return importZone(ctx, argv, args, output);
return importZone(ctx, argv, args);
case 'rm':
return rm(ctx, argv, args, output);
return rm(ctx, argv, args);
default:
return ls(ctx, argv, args, output);
return ls(ctx, argv, args);
}
}

View File

@@ -1,6 +1,5 @@
import chalk from 'chalk';
import ms from 'ms';
import { Output } from '../../util/output';
import { DomainNotFound } from '../../util/errors-ts';
import { DNSRecord, NowContext } from '../../types';
import Client from '../../util/client';
@@ -22,17 +21,17 @@ type Options = {
export default async function ls(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
apiUrl,
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const { '--debug': debug, '--next': nextTimestamp } = opts;
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {

View File

@@ -17,17 +17,17 @@ type Options = {
export default async function rm(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
apiUrl,
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
try {
await getScope(client);
@@ -100,12 +100,7 @@ function readConfirmation(
process.stdin
.on('data', d => {
process.stdin.pause();
resolve(
d
.toString()
.trim()
.toLowerCase() === 'y'
);
resolve(d.toString().trim().toLowerCase() === 'y');
})
.resume();
});

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import * as ERRORS from '../../util/errors-ts';
import Client from '../../util/client';
import formatNSTable from '../../util/format-ns-table';
@@ -24,18 +23,18 @@ type Options = {
export default async function add(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const force = opts['--force'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {

View File

@@ -2,7 +2,6 @@ import chalk from 'chalk';
import psl from 'psl';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import * as ERRORS from '../../util/errors-ts';
import Client from '../../util/client';
import getDomainPrice from '../../util/domains/get-domain-price';
@@ -21,17 +20,17 @@ type Options = {
export default async function buy(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {
@@ -70,7 +69,10 @@ export default async function buy(
}
const availableStamp = stamp();
const domainPrice = await getDomainPrice(client, domainName);
const [domainPrice, renewalPrice] = await Promise.all([
getDomainPrice(client, domainName),
getDomainPrice(client, domainName, 'renewal'),
]);
if (domainPrice instanceof Error) {
output.prettyError(domainPrice);
@@ -102,14 +104,22 @@ export default async function buy(
return 0;
}
const autoRenew = await promptBool(
renewalPrice.period === 1
? `Auto renew yearly for ${chalk.bold(`$${price}`)}?`
: `Auto renew every ${renewalPrice.period} years for ${chalk.bold(
`$${price}`
)}?`,
{ defaultValue: true }
);
let buyResult;
const purchaseStamp = stamp();
const stopPurchaseSpinner = output.spinner('Purchasing');
output.spinner('Purchasing');
try {
buyResult = await purchaseDomain(client, domainName, price);
buyResult = await purchaseDomain(client, domainName, price, autoRenew);
} catch (err) {
stopPurchaseSpinner();
output.error(
'An unexpected error occurred while purchasing your domain. Please try again later.'
);
@@ -117,7 +127,7 @@ export default async function buy(
return 1;
}
stopPurchaseSpinner();
output.stopSpinner();
if (buyResult instanceof ERRORS.SourceNotFound) {
output.error(

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import handleError from '../../util/handle-error';
@@ -106,24 +105,23 @@ export default async function main(ctx: NowContext) {
return 2;
}
const output = createOutput({ debug: argv['--debug'] });
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
switch (subcommand) {
case 'add':
return add(ctx, argv, args, output);
return add(ctx, argv, args);
case 'inspect':
return inspect(ctx, argv, args, output);
return inspect(ctx, argv, args);
case 'move':
return move(ctx, argv, args, output);
return move(ctx, argv, args);
case 'buy':
return buy(ctx, argv, args, output);
return buy(ctx, argv, args);
case 'rm':
return rm(ctx, argv, args, output);
return rm(ctx, argv, args);
case 'transferIn':
return transferIn(ctx, argv, args, output);
return transferIn(ctx, argv, args);
case 'verify':
return verify(ctx, argv, args, output);
return verify(ctx, argv, args);
default:
return ls(ctx, argv, args, output);
return ls(ctx, argv, args);
}
}

View File

@@ -24,17 +24,17 @@ type Options = {
export default async function inspect(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;

View File

@@ -7,7 +7,6 @@ import Client from '../../util/client';
import getDomains from '../../util/domains/get-domains';
import getScope from '../../util/get-scope';
import stamp from '../../util/output/stamp';
import { Output } from '../../util/output';
import formatTable from '../../util/format-table';
import { formatDateWithoutTime } from '../../util/format-date';
import { Domain, NowContext } from '../../types';
@@ -24,17 +23,17 @@ type Options = {
export default async function ls(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const { '--debug': debug, '--next': nextTimestamp } = opts;
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
if (typeof nextTimestamp !== undefined && Number.isNaN(nextTimestamp)) {

View File

@@ -2,7 +2,6 @@ import chalk from 'chalk';
import plural from 'pluralize';
import { NowContext, User, Team } from '../../types';
import { Output } from '../../util/output';
import * as ERRORS from '../../util/errors-ts';
import Client from '../../util/client';
import getScope from '../../util/get-scope';
@@ -25,17 +24,17 @@ type Options = {
export default async function move(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
let user = null;

View File

@@ -26,17 +26,17 @@ type Options = {
export default async function rm(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
const [domainName] = args;
let contextName = null;

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import * as ERRORS from '../../util/errors-ts';
import Client from '../../util/client';
import getScope from '../../util/get-scope';
@@ -24,17 +23,17 @@ type Options = {
export default async function transferIn(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {

View File

@@ -1,13 +1,11 @@
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import { NowBuildError } from '@vercel/build-utils';
import { getCommandName } from '../../util/pkg-name';
export default async function verify(
_ctx: NowContext,
{ output }: NowContext,
_opts: {},
args: string[],
output: Output
args: string[]
) {
const [domainName] = args;

View File

@@ -122,7 +122,17 @@ export default async function add(
const existing = new Set(
envs.filter(r => r.key === envName).map(r => r.target)
);
const choices = getEnvTargetChoices().filter(c => !existing.has(c.value));
const choices = getEnvTargetChoices().filter(c => {
// hide Development if "Secret" is chosen
if (
envType === ProjectEnvType.Secret &&
c.value === ProjectEnvTarget.Development
) {
return false;
}
return !existing.has(c.value);
});
if (choices.length === 0) {
output.error(

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import getInvalidSubcommand from '../../util/get-invalid-subcommand';
@@ -109,15 +108,15 @@ export default async function main(ctx: NowContext) {
}
const debug = argv['--debug'];
const output = createOutput({ debug });
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
const {
authConfig: { token },
apiUrl,
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
const link = await getLinkedProject(output, client);
if (link.status === 'error') {
return link.exitCode;

View File

@@ -4,7 +4,6 @@ import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import { NowContext } from '../../types';
import handleError from '../../util/handle-error';
import createOutput from '../../util/output/create-output';
import logo from '../../util/output/logo';
import error from '../../util/output/error';
import init from './init';
@@ -47,7 +46,6 @@ const help = () => {
export default async function main(ctx: NowContext) {
let argv;
let args;
let output;
try {
argv = getArgs(ctx.argv.slice(2), {
@@ -55,7 +53,6 @@ export default async function main(ctx: NowContext) {
'-f': Boolean,
});
args = getSubcommand(argv._.slice(1), COMMAND_CONFIG).args;
output = createOutput({ debug: argv['--debug'] });
} catch (err) {
handleError(err);
return 1;
@@ -67,15 +64,15 @@ export default async function main(ctx: NowContext) {
}
if (argv._.length > 3) {
output.error('Too much arguments.');
ctx.output.error('Too much arguments.');
return 1;
}
try {
return await init(ctx, argv, args, output);
return await init(ctx, argv, args);
} catch (err) {
console.log(error(err.message));
output.debug(err.stack);
ctx.output.debug(err.stack);
return 1;
}
}

View File

@@ -34,9 +34,9 @@ const EXAMPLE_API = 'https://now-example-files.zeit.sh';
export default async function init(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const { output } = ctx;
const [name, dir] = args;
const force = opts['-f'] || opts['--force'];
@@ -82,12 +82,12 @@ export default async function init(
* Fetch example list json
*/
async function fetchExampleList(output: Output) {
const stopSpinner = output.spinner('Fetching examples');
output.spinner('Fetching examples');
const url = `${EXAMPLE_API}/v2/list.json`;
try {
const resp = await fetch(url);
stopSpinner();
output.stopSpinner();
if (resp.status !== 200) {
throw new Error(`Failed fetching list.json (${resp.statusText}).`);
@@ -95,7 +95,7 @@ async function fetchExampleList(output: Output) {
return (await resp.json()) as Example[];
} catch (e) {
stopSpinner();
output.stopSpinner();
}
}
@@ -127,13 +127,13 @@ async function extractExample(
ver: string = 'v2'
) {
const folder = prepareFolder(process.cwd(), dir || name, force);
const stopSpinner = output.spinner(`Fetching ${name}`);
output.spinner(`Fetching ${name}`);
const url = `${EXAMPLE_API}/${ver}/download/${name}.tar.gz`;
return fetch(url)
.then(async resp => {
stopSpinner();
output.stopSpinner();
if (resp.status !== 200) {
throw new Error(`Could not get ${name}.tar.gz`);
@@ -163,7 +163,7 @@ async function extractExample(
return 0;
})
.catch(e => {
stopSpinner();
output.stopSpinner();
throw e;
});
}

View File

@@ -3,7 +3,6 @@ import getArgs from '../util/get-args';
import buildsList from '../util/output/builds';
import routesList from '../util/output/routes';
import indent from '../util/output/indent';
import createOutput from '../util/output';
import Now from '../util';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed.ts';
@@ -59,9 +58,13 @@ export default async function main(ctx) {
return 2;
}
const apiUrl = ctx.apiUrl;
const {
apiUrl,
output,
authConfig: { token },
config,
} = ctx;
const debugEnabled = argv['--debug'];
const output = createOutput({ debug: debugEnabled });
const { print, log, error } = output;
// extract the first parameter
@@ -73,16 +76,13 @@ export default async function main(ctx) {
return 1;
}
const {
authConfig: { token },
config,
} = ctx;
const { currentTeam } = config;
const client = new Client({
apiUrl,
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;
@@ -90,25 +90,30 @@ export default async function main(ctx) {
({ contextName } = await getScope(client));
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
error(err.message);
return 1;
}
throw err;
}
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
const now = new Now({
apiUrl,
token,
debug: debugEnabled,
currentTeam,
output,
});
// resolve the deployment, since we might have been given an alias
const depFetchStart = Date.now();
const cancelWait = output.spinner(
output.spinner(
`Fetching deployment "${deploymentIdOrHost}" in ${chalk.bold(contextName)}`
);
try {
deployment = await now.findDeployment(deploymentIdOrHost);
} catch (err) {
cancelWait();
if (err.status === 404) {
error(
`Failed to find deployment "${deploymentIdOrHost}" in ${chalk.bold(
@@ -136,7 +141,6 @@ export default async function main(ctx) {
? await now.fetch(`/v1/now/deployments/${id}/builds`)
: { builds: [] };
cancelWait();
log(
`Fetched deployment "${url}" in ${chalk.bold(contextName)} ${elapsed(
Date.now() - depFetchStart

View File

@@ -1,6 +1,5 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import handleError from '../../util/handle-error';
@@ -66,8 +65,6 @@ export default async function main(ctx: NowContext) {
return 2;
}
const debug = argv['--debug'];
const output = createOutput({ debug });
const { args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
const path = args[0] || process.cwd();
const autoConfirm = argv['--confirm'];
@@ -75,7 +72,6 @@ export default async function main(ctx: NowContext) {
const link = await setupAndLink(
ctx,
output,
path,
forceDelete,
autoConfirm,

View File

@@ -3,12 +3,10 @@ import ms from 'ms';
import table from 'text-table';
import Now from '../util';
import getArgs from '../util/get-args';
import createOutput from '../util/output';
import { handleError } from '../util/error';
import cmd from '../util/output/cmd.ts';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed.ts';
import wait from '../util/output/wait';
import strlen from '../util/strlen.ts';
import Client from '../util/client.ts';
import getScope from '../util/get-scope.ts';
@@ -78,11 +76,15 @@ export default async function main(ctx) {
return 1;
}
const debugEnabled = argv['--debug'];
const {
authConfig: { token },
output,
apiUrl,
config,
} = ctx;
const { print, log, error, note, debug } = createOutput({
debug: debugEnabled,
});
const debugEnabled = argv['--debug'];
const { print, log, error, note, debug, spinner } = output;
if (argv._.length > 2) {
error(`${getCommandName('ls [app]')} accepts at most one argument`);
@@ -92,24 +94,19 @@ export default async function main(ctx) {
let app = argv._[1];
let host = null;
const apiUrl = ctx.apiUrl;
if (argv['--help']) {
help();
return 0;
}
const meta = parseMeta(argv['--meta']);
const {
authConfig: { token },
config,
} = ctx;
const { currentTeam, includeScheme } = config;
const client = new Client({
apiUrl,
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;
@@ -131,11 +128,15 @@ export default async function main(ctx) {
return 1;
}
const stopSpinner = wait(
`Fetching deployments in ${chalk.bold(contextName)}`
);
spinner(`Fetching deployments in ${chalk.bold(contextName)}`);
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
const now = new Now({
apiUrl,
token,
debug: debugEnabled,
output,
currentTeam,
});
const start = new Date();
if (app && !isValidName(app)) {
@@ -156,7 +157,6 @@ export default async function main(ctx) {
const hostParts = asHost.split('-');
if (hostParts < 2) {
stopSpinner();
error('Only deployment hostnames are allowed, no aliases');
return 1;
}
@@ -165,19 +165,12 @@ export default async function main(ctx) {
host = asHost;
}
let response;
try {
debug('Fetching deployments');
response = await now.list(app, {
version: 6,
meta,
nextTimestamp,
});
} catch (err) {
stopSpinner();
throw err;
}
debug('Fetching deployments');
const response = await now.list(app, {
version: 6,
meta,
nextTimestamp,
});
let { deployments, pagination } = response;
@@ -193,7 +186,6 @@ export default async function main(ctx) {
if (err.status === 404) {
debug('Ignore findDeployment 404');
} else {
stopSpinner();
throw err;
}
}
@@ -210,7 +202,6 @@ export default async function main(ctx) {
deployments = deployments.filter(deployment => deployment.url === host);
}
stopSpinner();
log(
`Deployments under ${chalk.bold(contextName)} ${elapsed(
Date.now() - start

View File

@@ -19,7 +19,6 @@ import getGlobalPathConfig from '../util/config/global-path';
import hp from '../util/humanize-path';
import logo from '../util/output/logo';
import exit from '../util/exit';
import createOutput from '../util/output';
import executeLogin from '../util/login/login.ts';
import { prependEmoji, emoji } from '../util/emoji';
import { getCommandName, getPkgName } from '../util/pkg-name.ts';
@@ -141,15 +140,12 @@ const login = async ctx => {
await exit(0);
}
const debugEnabled = argv['--debug'];
const output = createOutput({ debug: debugEnabled });
const { apiUrl, output } = ctx;
argv._ = argv._.slice(1);
const apiUrl = ctx.apiUrl;
let email;
let emailIsValid = false;
let stopSpinner;
const possibleAddress = argv._[0];
@@ -190,19 +186,18 @@ const login = async ctx => {
let verificationToken;
let securityCode;
stopSpinner = output.spinner('Sending you an email');
output.spinner('Sending you an email');
try {
const data = await executeLogin(apiUrl, email);
verificationToken = data.token;
securityCode = data.securityCode;
} catch (err) {
stopSpinner();
console.log(error(err.message));
output.error(err.message);
return 1;
}
stopSpinner();
output.stopSpinner();
// Clear up `Sending email` success message
process.stdout.write(eraseLines(possibleAddress ? 1 : 2));
@@ -215,7 +210,7 @@ const login = async ctx => {
)}.\n`
);
stopSpinner = output.spinner('Waiting for your confirmation');
output.spinner('Waiting for your confirmation');
let token;
@@ -228,14 +223,13 @@ const login = async ctx => {
// /now/registraton is currently returning plain text in that case
// we just wait for the user to click on the link
} else {
stopSpinner();
console.log(err.message);
output.error(err.message);
return 1;
}
}
}
stopSpinner();
output.stopSpinner();
console.log(ok('Email confirmed'));
// There's no need to save the user since we always

View File

@@ -11,7 +11,7 @@ import {
} from '../util/config/files';
import getArgs from '../util/get-args';
import { NowContext } from '../types';
import createOutput, { Output } from '../util/output';
import { Output } from '../util/output';
import { getPkgName } from '../util/pkg-name';
const help = () => {
@@ -54,13 +54,11 @@ export default async function main(ctx: NowContext): Promise<number> {
return 2;
}
const debugEnabled = argv['--debug'];
const output = createOutput({ debug: debugEnabled });
return logout(ctx.apiUrl, output);
return logout(ctx.apiUrl, ctx.output);
}
const logout = async (apiUrl: string, output: Output) => {
const spinner = output.spinner('Logging out...', 200);
output.spinner('Logging out', 200);
const configContent = readConfigFile();
const authContent = readAuthConfigFile();
@@ -83,7 +81,6 @@ const logout = async (apiUrl: string, output: Output) => {
writeToAuthConfigFile(authContent);
output.debug('Configuration has been deleted');
} catch (err) {
spinner();
output.error(`Couldn't remove config while logging out`);
return 1;
}
@@ -98,14 +95,12 @@ const logout = async (apiUrl: string, output: Output) => {
if (res.status === 403) {
output.debug('Token is invalid so it cannot be revoked');
} else if (res.status !== 200) {
spinner();
const err = await res.json();
output.error('Failed to revoke token');
output.debug(err ? err.message : '');
return 1;
}
spinner();
output.log('Logged out!');
return 0;
};

View File

@@ -1,7 +1,6 @@
import mri from 'mri';
import chalk from 'chalk';
import Now from '../util';
import createOutput from '../util/output';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed.ts';
import { maybeURL, normalizeURL } from '../util/url';
@@ -59,7 +58,6 @@ export default async function main(ctx) {
let deploymentIdOrURL;
let debug;
let apiUrl;
let head;
let limit;
let follow;
@@ -87,8 +85,12 @@ export default async function main(ctx) {
return 2;
}
const debugEnabled = argv.debug;
const output = createOutput({ debug: debugEnabled });
const {
authConfig: { token },
apiUrl,
output,
config,
} = ctx;
try {
since = argv.since ? toTimestamp(argv.since) : 0;
@@ -117,7 +119,6 @@ export default async function main(ctx) {
}
debug = argv.debug;
apiUrl = ctx.apiUrl;
head = argv.head;
limit = typeof argv.n === 'number' ? argv.n : 100;
@@ -125,17 +126,14 @@ export default async function main(ctx) {
if (follow) until = 0;
outputMode = argv.output in logPrinters ? argv.output : 'short';
const {
authConfig: { token },
config,
} = ctx;
const { currentTeam } = config;
const now = new Now({ apiUrl, token, debug, currentTeam });
const now = new Now({ apiUrl, token, debug, currentTeam, output });
const client = new Client({
apiUrl,
token,
currentTeam,
debug: debugEnabled,
debug,
output,
});
let contextName = null;
@@ -154,14 +152,12 @@ export default async function main(ctx) {
const id = deploymentIdOrURL;
const depFetchStart = Date.now();
const cancelWait = output.spinner(
`Fetching deployment "${id}" in ${chalk.bold(contextName)}`
);
output.spinner(`Fetching deployment "${id}" in ${chalk.bold(contextName)}`);
try {
deployment = await now.findDeployment(id);
} catch (err) {
cancelWait();
output.stopSpinner();
now.close();
if (err.status === 404) {
@@ -182,7 +178,6 @@ export default async function main(ctx) {
throw err;
}
cancelWait();
output.log(
`Fetched deployment "${deployment.url}" in ${chalk.bold(
contextName
@@ -206,6 +201,7 @@ export default async function main(ctx) {
quiet: false,
debug,
findOpts: findOpts1,
output,
});
const printedEventIds = new Set();
@@ -232,6 +228,7 @@ export default async function main(ctx) {
quiet: false,
debug,
findOpts: findOpts2,
output,
});
}

View File

@@ -8,7 +8,6 @@ import exit from '../util/exit';
import Client from '../util/client.ts';
import logo from '../util/output/logo';
import getScope from '../util/get-scope';
import createOutput from '../util/output';
import getCommandFlags from '../util/get-command-flags';
import wait from '../util/output/wait';
import { getPkgName, getCommandName } from '../util/pkg-name.ts';
@@ -76,13 +75,12 @@ const main = async ctx => {
return exit(2);
}
const output = createOutput({ debug });
const {
authConfig: { token },
config: { currentTeam },
output,
} = ctx;
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
@@ -288,12 +286,7 @@ function readConfirmation(projectName) {
process.stdin
.on('data', d => {
process.stdin.pause();
resolve(
d
.toString()
.trim()
.toLowerCase() === 'y'
);
resolve(d.toString().trim().toLowerCase() === 'y');
})
.resume();
});

View File

@@ -5,7 +5,6 @@ import plural from 'pluralize';
import table from 'text-table';
import Now from '../util';
import getAliases from '../util/alias/get-aliases';
import createOutput from '../util/output';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed.ts';
import { normalizeURL } from '../util/url';
@@ -79,12 +78,16 @@ export default async function main(ctx) {
argv._ = argv._.slice(1);
const apiUrl = ctx.apiUrl;
const {
apiUrl,
authConfig: { token },
output,
config,
} = ctx;
const hard = argv.hard || false;
const skipConfirmation = argv.yes || false;
const ids = argv._;
const debugEnabled = argv.debug;
const output = createOutput({ debug: debugEnabled });
const { success, error, log } = output;
if (argv.help || ids[0] === 'help') {
@@ -107,16 +110,13 @@ export default async function main(ctx) {
return 1;
}
const {
authConfig: { token },
config,
} = ctx;
const { currentTeam } = config;
const client = new Client({
apiUrl,
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;
@@ -133,7 +133,7 @@ export default async function main(ctx) {
throw err;
}
const cancelWait = output.spinner(
output.spinner(
`Fetching deployment(s) ${ids
.map(id => `"${id}"`)
.join(' ')} in ${chalk.bold(contextName)}`
@@ -198,7 +198,7 @@ export default async function main(ctx) {
})
);
} finally {
cancelWait();
output.stopSpinner();
}
deployments = deployments.filter((match, i) => {
@@ -235,11 +235,9 @@ export default async function main(ctx) {
}
if (!skipConfirmation) {
const confirmation = (await readConfirmation(
deployments,
projects,
output
)).toLowerCase();
const confirmation = (
await readConfirmation(deployments, projects, output)
).toLowerCase();
if (confirmation !== 'y' && confirmation !== 'yes') {
output.log('Aborted');
@@ -248,7 +246,13 @@ export default async function main(ctx) {
}
}
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
const now = new Now({
apiUrl,
token,
debug: debugEnabled,
currentTeam,
output,
});
const start = new Date();
await Promise.all([

View File

@@ -9,7 +9,6 @@ import exit from '../util/exit';
import logo from '../util/output/logo';
import Client from '../util/client.ts';
import getScope from '../util/get-scope.ts';
import createOutput from '../util/output';
import confirm from '../util/input/confirm';
import getCommandFlags from '../util/get-command-flags';
import getPrefixedFlags from '../util/get-prefixed-flags';
@@ -104,10 +103,10 @@ const main = async ctx => {
const {
authConfig: { token },
output,
config: { currentTeam },
} = ctx;
const output = createOutput({ debug });
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {
@@ -139,7 +138,7 @@ export default async ctx => {
};
async function run({ output, token, contextName, currentTeam, ctx }) {
const secrets = new NowSecrets({ apiUrl, token, debug, currentTeam });
const secrets = new NowSecrets({ apiUrl, token, debug, currentTeam, output });
const args = argv._.slice(1);
const start = Date.now();

View File

@@ -52,7 +52,7 @@ const help = () => {
${chalk.gray('')} Invite new members (interactively)
${chalk.cyan(`$ ${getPkgName()} teams invite`)}
${chalk.gray('')} Paginate results, where ${chalk.dim(
'`1584722256178`'
)} is the time in milliseconds since the UNIX epoch.
@@ -97,10 +97,11 @@ const main = async ctx => {
const {
authConfig: { token },
output,
config,
} = ctx;
return run({ token, config });
return run({ token, config, output });
};
export default async ctx => {
@@ -112,7 +113,7 @@ export default async ctx => {
}
};
async function run({ token, config }) {
async function run({ token, config, output }) {
const { currentTeam } = config;
const teams = new NowTeams({ apiUrl, token, debug, currentTeam });
const args = argv._;
@@ -126,6 +127,7 @@ async function run({ token, config }) {
config,
apiUrl,
token,
output,
argv,
});
break;
@@ -138,6 +140,7 @@ async function run({ token, config }) {
apiUrl,
token,
debug,
output,
});
break;
}
@@ -154,6 +157,7 @@ async function run({ token, config }) {
config,
apiUrl,
token,
output,
});
break;
}

View File

@@ -14,7 +14,6 @@ import success from '../../util/output/success';
import getUser from '../../util/get-user.ts';
import Client from '../../util/client.ts';
import { getCommandName } from '../../util/pkg-name.ts';
import createOutput from '../../util/output';
const validateEmail = data => regexEmail.test(data.trim()) || data.length === 0;
@@ -65,8 +64,8 @@ export default async function ({
noopMsg = 'No changes made',
apiUrl,
token,
output,
} = {}) {
const output = createOutput();
const { currentTeam: currentTeamId } = config;
const stopSpinner = wait('Fetching teams');
@@ -77,7 +76,7 @@ export default async function ({
stopSpinner();
const stopUserSpinner = wait('Fetching user information');
const client = new Client({ apiUrl, token });
const client = new Client({ apiUrl, token, output });
let user;
try {
user = await getUser(client);

View File

@@ -8,7 +8,6 @@ import info from '../../util/output/info';
import error from '../../util/output/error';
import chars from '../../util/output/chars';
import table from '../../util/output/table';
import createOutput from '../../util/output';
import getUser from '../../util/get-user.ts';
import Client from '../../util/client.ts';
import getPrefixedFlags from '../../util/get-prefixed-flags';
@@ -16,9 +15,8 @@ import { getPkgName } from '../../util/pkg-name.ts';
import getCommandFlags from '../../util/get-command-flags';
import cmd from '../../util/output/cmd.ts';
export default async function({ teams, config, apiUrl, token, argv }) {
export default async function ({ teams, config, apiUrl, token, output, argv }) {
const { next } = argv;
const output = createOutput({ debug: argv['--debug'] });
if (typeof next !== 'undefined' && !Number.isInteger(next)) {
output.error('Please provide a number for flag --next');
@@ -36,7 +34,7 @@ export default async function({ teams, config, apiUrl, token, argv }) {
stopSpinner();
const stopUserSpinner = wait('Fetching user information');
const client = new Client({ apiUrl, token, currentTeam });
const client = new Client({ apiUrl, token, currentTeam, output });
let user;
try {
user = await getUser(client);

View File

@@ -2,8 +2,6 @@
import chalk from 'chalk';
// Utilities
import wait from '../../util/output/wait';
import listInput from '../../util/input/list';
import success from '../../util/output/success';
import info from '../../util/output/info';
@@ -24,22 +22,20 @@ const updateCurrentTeam = (config, newTeam) => {
writeToConfigFile(config);
};
export default async function({ apiUrl, token, debug, args, config }) {
let stopSpinner = wait('Fetching teams');
export default async function ({ apiUrl, token, debug, args, config, output }) {
output.spinner('Fetching teams');
// We're loading the teams here without `currentTeam`, so that
// people can use `vercel switch` in the case that their
// current team was deleted.
const teams = new NowTeams({ apiUrl, token, debug });
const teams = new NowTeams({ apiUrl, token, debug, output });
const list = (await teams.ls()).teams;
let { currentTeam } = config;
const accountIsCurrent = !currentTeam;
stopSpinner();
const stopUserSpinner = wait('Fetching user information');
const client = new Client({ apiUrl, token });
output.spinner('Fetching user information');
const client = new Client({ apiUrl, token, output });
let user;
try {
user = await getUser(client);
@@ -52,8 +48,6 @@ export default async function({ apiUrl, token, debug, args, config }) {
throw err;
}
stopUserSpinner();
if (accountIsCurrent) {
currentTeam = {
slug: user.username || user.email,
@@ -84,10 +78,10 @@ export default async function({ apiUrl, token, debug, args, config }) {
}
if (desiredSlug === user.username) {
stopSpinner = wait('Saving');
output.spinner('Saving');
updateCurrentTeam(config);
stopSpinner();
output.stopSpinner();
console.log(
success(`Your account (${chalk.bold(desiredSlug)}) is now active!`)
);
@@ -163,10 +157,10 @@ export default async function({ apiUrl, token, debug, args, config }) {
return 0;
}
stopSpinner = wait('Saving');
output.spinner('Saving');
updateCurrentTeam(config);
stopSpinner();
output.stopSpinner();
console.log(success(`Your account (${chalk.bold(choice)}) is now active!`));
return 0;
}
@@ -176,10 +170,10 @@ export default async function({ apiUrl, token, debug, args, config }) {
return 0;
}
stopSpinner = wait('Saving');
output.spinner('Saving');
updateCurrentTeam(config, newTeam);
stopSpinner();
output.stopSpinner();
console.log(
success(
`The team ${chalk.bold(newTeam.name)} (${newTeam.slug}) is now active!`

View File

@@ -5,7 +5,6 @@ import logo from '../util/output/logo';
import handleError from '../util/handle-error';
import getArgs from '../util/get-args';
import { NowContext } from '../types';
import createOutput from '../util/output';
import getUpdateCommand from '../util/get-update-command';
import { getPkgName, getTitleName } from '../util/pkg-name';
@@ -35,6 +34,7 @@ const help = () => {
export default async function main(ctx: NowContext): Promise<number> {
let argv;
const { output } = ctx;
try {
argv = getArgs(ctx.argv.slice(2), {
@@ -55,8 +55,6 @@ export default async function main(ctx: NowContext): Promise<number> {
return 2;
}
const debugEnabled = argv['--debug'];
const output = createOutput({ debug: debugEnabled });
output.log(
`Please run ${cmd(
await getUpdateCommand()

View File

@@ -4,7 +4,6 @@ import logo from '../util/output/logo';
import { handleError } from '../util/error';
import Client from '../util/client.ts';
import getScope from '../util/get-scope.ts';
import createOutput from '../util/output';
import { getPkgName } from '../util/pkg-name.ts';
const help = () => {
@@ -55,10 +54,10 @@ const main = async ctx => {
const debug = argv['--debug'];
const {
authConfig: { token },
output,
apiUrl,
} = ctx;
const output = createOutput({ debug });
const client = new Client({ apiUrl, token, debug });
const client = new Client({ apiUrl, token, debug, output });
let contextName = null;
try {

View File

@@ -291,7 +291,7 @@ const main = async argv_ => {
let authConfig = null;
const subcommandsWithoutToken = ['login', 'help', 'init', 'dev', 'update'];
const subcommandsWithoutToken = ['login', 'help', 'init', 'update'];
if (authConfigExists) {
try {
@@ -346,6 +346,7 @@ const main = async argv_ => {
// the context object to supply to the providers or the commands
const ctx = {
output,
config,
authConfig,
localConfig,
@@ -536,7 +537,7 @@ const main = async argv_ => {
!(targetCommand === 'teams' && argv._[3] !== 'invite')
) {
let user = null;
const client = new Client({ apiUrl, token });
const client = new Client({ apiUrl, token, output });
try {
user = await getUser(client);

View File

@@ -1,4 +1,5 @@
import { NowConfig } from './util/dev/types';
import { Output } from './util/output';
export type ThenArg<T> = T extends Promise<infer U> ? U : T;
@@ -8,6 +9,7 @@ export interface NowContext {
authConfig: {
token: string;
};
output: Output;
config: {
currentTeam: string;
updateChannel: string;

View File

@@ -19,14 +19,14 @@ export default async function createAlias(
alias: string,
externalDomain: boolean
) {
let cancelMessage = output.spinner(`Creating alias`);
output.spinner(`Creating alias`);
const result = await performCreateAlias(
client,
contextName,
deployment,
alias
);
cancelMessage();
output.stopSpinner();
if (result instanceof ERRORS.CertMissing) {
const cert = await createCertForAlias(
@@ -40,14 +40,14 @@ export default async function createAlias(
return cert;
}
let cancelMessage = output.spinner(`Creating alias`);
output.spinner(`Creating alias`);
const secondTry = await performCreateAlias(
client,
contextName,
deployment,
alias
);
cancelMessage();
output.stopSpinner();
return secondTry;
}

View File

@@ -37,9 +37,7 @@ export async function getDeploymentForAlias(
contextName: string,
localConfig: NowConfig
) {
const cancelWait = output.spinner(
`Fetching deployment to alias in ${chalk.bold(contextName)}`
);
output.spinner(`Fetching deployment to alias in ${chalk.bold(contextName)}`);
// When there are no args at all we try to get the targets from the config
if (args.length === 2) {
@@ -49,7 +47,7 @@ export async function getDeploymentForAlias(
contextName,
deploymentId
);
cancelWait();
output.stopSpinner();
return deployment;
}
@@ -68,6 +66,6 @@ export async function getDeploymentForAlias(
user,
contextName
);
cancelWait();
output.stopSpinner();
return deployment;
}

View File

@@ -13,20 +13,21 @@ export default async function createCertificateForAlias(
alias: string,
shouldBeWildcard: boolean
) {
output.spinner(`Generating a certificate…`);
const cns = shouldBeWildcard ? getWildcardCnsForAlias(alias) : [alias];
const cancelMessage = output.spinner(`Generating a certificate...`);
const certStamp = stamp();
const cert = await createCertForCns(client, cns, context);
if (cert instanceof NowError) {
cancelMessage();
output.stopSpinner();
return cert;
}
cancelMessage();
output.log(
`Certificate for ${joinWords(cert.cns)} (${
cert.uid
}) created ${certStamp()}`
);
return cert;
}

View File

@@ -33,6 +33,7 @@ export default class Client extends EventEmitter {
forceNew = false,
withCache = false,
debug = false,
output = createOutput({ debug }),
}: {
apiUrl: string;
token: string;
@@ -40,13 +41,14 @@ export default class Client extends EventEmitter {
forceNew?: boolean;
withCache?: boolean;
debug?: boolean;
output?: Output;
}) {
super();
this._token = token;
this._debug = debug;
this._forceNew = forceNew;
this._withCache = withCache;
this._output = createOutput({ debug });
this._output = output;
this._apiUrl = apiUrl;
this._onRetry = this._onRetry.bind(this);
this.currentTeam = currentTeam;

View File

@@ -22,25 +22,21 @@ export default async function generateCertForDeploy(
return new InvalidDomain(deployURL);
}
const cancelSetupWait = output.spinner(
`Setting custom suffix domain ${domain}`
);
output.spinner(`Setting custom suffix domain ${domain}`);
const result = await setupDomain(output, client, domain, contextName);
cancelSetupWait();
output.stopSpinner();
if (result instanceof NowError) {
return result;
}
// Generate the certificate with the given parameters
const cancelCertWait = output.spinner(
`Generating a wildcard certificate for ${domain}`
);
output.spinner(`Generating a wildcard certificate for ${domain}`);
const cert = await createCertForCns(
client,
[domain, `*.${domain}`],
contextName
);
cancelCertWait();
output.stopSpinner();
if (cert instanceof NowError) {
return cert;
}

View File

@@ -100,11 +100,7 @@ export default async function processDeployment({
skipAutoDetectionConfirmation,
};
let queuedSpinner = null;
let buildSpinner = null;
let deploySpinner = null;
const deployingSpinner = output.spinner(
output.spinner(
isSettingUpProject
? 'Setting up project'
: `Deploying ${chalk.bold(`${org.slug}/${projectName}`)}`,
@@ -138,16 +134,7 @@ export default async function processDeployment({
.map((sha: string) => event.payload.total.get(sha).data.length)
.reduce((a: number, b: number) => a + b, 0);
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
if (deploySpinner) {
deploySpinner();
}
deployingSpinner();
output.stopSpinner();
bar = new Progress(`${chalk.gray('>')} Upload [:bar] :percent :etas`, {
width: 20,
complete: '=',
@@ -170,8 +157,6 @@ export default async function processDeployment({
}
if (event.type === 'created') {
deployingSpinner();
if (bar && !bar.complete) {
bar.tick(bar.total + 1);
}
@@ -191,63 +176,36 @@ export default async function processDeployment({
now.url = event.payload.url;
output.stopSpinner();
printInspectUrl(output, event.payload.url, deployStamp, org.slug);
if (quiet) {
process.stdout.write(`https://${event.payload.url}`);
}
if (queuedSpinner === null) {
queuedSpinner =
event.payload.readyState === 'QUEUED'
? output.spinner('Queued', 0)
: output.spinner('Building', 0);
}
output.spinner(
event.payload.readyState === 'QUEUED' ? 'Queued' : 'Building',
0
);
}
if (event.type === 'building') {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner === null) {
buildSpinner = output.spinner('Building', 0);
}
output.spinner('Building', 0);
}
if (event.type === 'canceled') {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
output.stopSpinner();
return event.payload;
}
if (event.type === 'ready') {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
deploySpinner = output.spinner('Completing', 0);
output.spinner('Completing', 0);
}
// Handle error events
if (event.type === 'error') {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
if (deploySpinner) {
deploySpinner();
}
deployingSpinner();
output.stopSpinner();
const error = await now.handleDeploymentError(event.payload, {
hashes,
@@ -263,32 +221,12 @@ export default async function processDeployment({
// Handle alias-assigned event
if (event.type === 'alias-assigned') {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
if (deploySpinner) {
deploySpinner();
}
deployingSpinner();
event.payload.indications = indications;
return event.payload;
}
}
} catch (err) {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
if (deploySpinner) {
deploySpinner();
}
deployingSpinner();
output.stopSpinner();
throw err;
}
}

View File

@@ -222,14 +222,14 @@ async function npmInstall(
) {
const sortedPackages = packagesToInstall.sort();
const stopSpinner = silent
? () => {}
: output.spinner(
`Installing ${pluralize(
'Runtime',
sortedPackages.length
)}: ${sortedPackages.join(', ')}`
);
if (!silent) {
output.spinner(
`Installing ${pluralize(
'Runtime',
sortedPackages.length
)}: ${sortedPackages.join(', ')}`
);
}
output.debug(`Running npm install in ${cwd}`);
@@ -253,7 +253,7 @@ async function npmInstall(
stdio: output.isDebugEnabled() ? 'inherit' : 'pipe',
});
if (result.failed) {
stopSpinner();
output.stopSpinner();
if (result.stdout) {
console.log(result.stdout);
}
@@ -272,7 +272,7 @@ async function npmInstall(
});
}
} finally {
stopSpinner();
output.stopSpinner();
}
}

View File

@@ -280,7 +280,6 @@ export async function executeBuild(
path = extensionless;
}
delete output[path];
output[path] = value;
});
@@ -402,7 +401,7 @@ export async function getBuildMatches(
const builds = nowConfig.builds || [{ src: '**', use: '@vercel/static' }];
for (const buildConfig of builds) {
let { src = '**', use } = buildConfig;
let { src = '**', use, config = {} } = buildConfig;
if (!use) {
continue;
@@ -437,6 +436,15 @@ export async function getBuildMatches(
for (const file of files) {
src = relative(cwd, file);
// Remove the output directory prefix
if (config.zeroConfig && config.outputDirectory) {
const outputMatch = config.outputDirectory + '/';
if (src.startsWith(outputMatch)) {
src = src.slice(outputMatch.length);
}
}
const builderWithPkg = await getBuilder(use, output);
matches.push({
...buildConfig,

View File

@@ -1,7 +1,7 @@
import { basename, extname, join } from 'path';
import {
FileFsRef,
BuildOptions,
shouldServe as defaultShouldServe,
ShouldServeOptions,
} from '@vercel/build-utils';
import { BuildResult } from './types';
@@ -11,50 +11,32 @@ export const version = 2;
export function build({
files,
entrypoint,
config,
config: { zeroConfig, outputDirectory },
}: BuildOptions): BuildResult {
let path = entrypoint;
const outputDir = config.zeroConfig ? config.outputDirectory : '';
const outputMatch = outputDir + '/';
if (outputDir && path.startsWith(outputMatch)) {
// static output files are moved to the root directory
path = path.slice(outputMatch.length);
}
const output = {
[path]: files[entrypoint] as FileFsRef,
const path =
zeroConfig && outputDirectory
? `${outputDirectory}/${entrypoint}`
: entrypoint;
return {
output: {
[entrypoint]: files[path] as FileFsRef,
},
routes: [],
watch: [path],
};
const watch = [path];
return { output, routes: [], watch };
}
export function shouldServe({
entrypoint,
files,
requestPath,
config = {},
}: ShouldServeOptions) {
let outputPrefix = '';
const outputDir = config.zeroConfig ? config.outputDirectory : '';
const outputMatch = outputDir + '/';
if (outputDir && entrypoint.startsWith(outputMatch)) {
// static output files are moved to the root directory
entrypoint = entrypoint.slice(outputMatch.length);
outputPrefix = outputMatch;
export function shouldServe(_opts: ShouldServeOptions) {
const opts = { ..._opts };
let {
config: { zeroConfig, outputDirectory },
} = opts;
// Add the output directory prefix
if (zeroConfig && outputDirectory) {
opts.entrypoint = `${outputDirectory}/${opts.entrypoint}`;
opts.requestPath = `${outputDirectory}/${opts.requestPath}`;
}
const isMatch = (f: string) => entrypoint === f && outputPrefix + f in files;
if (isIndex(entrypoint)) {
const indexPath = join(requestPath, basename(entrypoint));
if (isMatch(indexPath)) {
return true;
}
}
return isMatch(requestPath);
}
function isIndex(path: string): boolean {
const ext = extname(path);
const name = basename(path, ext);
return name === 'index';
return defaultShouldServe(opts);
}

View File

@@ -19,7 +19,7 @@ export default async function purchaseDomainIfAvailable(
domain: string,
contextName: string
) {
const cancelWait = output.spinner(`Checking status of ${chalk.bold(domain)}`);
output.spinner(`Checking status of ${chalk.bold(domain)}`);
const buyDomainStamp = stamp();
const { available } = await getDomainStatus(client, domain);
@@ -32,7 +32,7 @@ export default async function purchaseDomainIfAvailable(
output.debug(`Domain ${domain} is available to be purchased`);
const domainPrice = await getDomainPrice(client, domain).finally(() => {
cancelWait();
output.stopSpinner();
});
if (domainPrice instanceof ERRORS.UnsupportedTLD) {

View File

@@ -12,11 +12,12 @@ type Response = {
export default async function purchaseDomain(
client: Client,
name: string,
expectedPrice: number
expectedPrice: number,
renew: boolean = true
) {
try {
return await client.fetch<Response>(`/v3/domains/buy`, {
body: { name, expectedPrice },
body: { name, expectedPrice, renew },
method: 'POST',
});
} catch (error) {

View File

@@ -7,16 +7,21 @@ import { eraseLines } from 'ansi-escapes';
import jsonlines from 'jsonlines';
import retry from 'async-retry';
// Utilities
import createOutput from './output';
async function printEvents(
now,
deploymentIdOrURL,
currentTeam = null,
{ mode, onOpen = () => {}, onEvent, quiet, debugEnabled, findOpts } = {}
{
mode,
onOpen = () => {},
onEvent,
quiet,
debugEnabled,
findOpts,
output,
} = {}
) {
const { log, debug } = createOutput({ debug: debugEnabled });
const { log, debug } = output;
let onOpenCalled = false;
function callOnOpenOnce() {

View File

@@ -23,6 +23,7 @@ export default class Now extends EventEmitter {
forceNew = false,
withCache = false,
debug = false,
output = createOutput({ debug }),
}) {
super();
@@ -30,7 +31,7 @@ export default class Now extends EventEmitter {
this._debug = debug;
this._forceNew = forceNew;
this._withCache = withCache;
this._output = createOutput({ debug });
this._output = output;
this._apiUrl = apiUrl;
this._onRetry = this._onRetry.bind(this);
this.currentTeam = currentTeam;

View File

@@ -19,10 +19,7 @@ export default async function inputProject(
// attempt to auto-detect a project to link
let detectedProject = null;
const existingProjectSpinner = output.spinner(
'Searching for existing projects…',
1000
);
output.spinner('Searching for existing projects…', 1000);
try {
const [project, slugifiedProject] = await Promise.all([
getProjectByIdOrName(client, detectedProjectName, org.id),
@@ -36,7 +33,7 @@ export default async function inputProject(
? slugifiedProject
: null;
} catch (error) {}
existingProjectSpinner();
output.stopSpinner();
if (autoConfirm) {
return detectedProject || detectedProjectName;
@@ -84,11 +81,11 @@ export default async function inputProject(
continue;
}
const spinner = output.spinner('Verifying project name…', 1000);
output.spinner('Verifying project name…', 1000);
try {
project = await getProjectByIdOrName(client, projectName, org.id);
} finally {
spinner();
output.stopSpinner();
}
if (project instanceof ProjectNotFound) {
@@ -116,7 +113,7 @@ export default async function inputProject(
continue;
}
const spinner = output.spinner('Verifying project name…', 1000);
output.spinner('Verifying project name…', 1000);
let existingProject: Project | ProjectNotFound;
try {
existingProject = await getProjectByIdOrName(
@@ -125,7 +122,7 @@ export default async function inputProject(
org.id
);
} finally {
spinner();
output.stopSpinner();
}
if (existingProject && !(existingProject instanceof ProjectNotFound)) {

View File

@@ -23,7 +23,7 @@ export default async function promptBool(label: string, options: Options = {}) {
trailing = '',
} = options;
return new Promise(resolve => {
return new Promise<boolean>(resolve => {
const isRaw = Boolean(stdin && stdin.isRaw);
if (stdin) {

View File

@@ -16,13 +16,13 @@ export default async function selectOrg(
): Promise<Org> {
require('./patch-inquirer');
const spinner = output.spinner('Loading scopes…', 1000);
output.spinner('Loading scopes…', 1000);
let user: User;
let teams: Team[];
try {
[user, teams] = await Promise.all([getUser(client), getTeams(client)]);
} finally {
spinner();
output.stopSpinner();
}
const choices: Choice[] = [

View File

@@ -3,7 +3,6 @@ import chalk from 'chalk';
import { remove } from 'fs-extra';
import { NowContext, ProjectLinkResult, ProjectSettings } from '../../types';
import { NowConfig } from '../dev/types';
import { Output } from '../output';
import {
getLinkedProject,
linkFolderToProject,
@@ -30,7 +29,6 @@ import Now from '../index';
export default async function setupAndLink(
ctx: NowContext,
output: Output,
path: string,
forceDelete: boolean,
autoConfirm: boolean,
@@ -39,15 +37,17 @@ export default async function setupAndLink(
): Promise<ProjectLinkResult> {
const {
authConfig: { token },
apiUrl,
output,
config,
} = ctx;
const { apiUrl } = ctx;
const debug = output.isDebugEnabled();
const client = new Client({
apiUrl,
token,
currentTeam: config.currentTeam,
debug,
output,
});
const isFile = !isDirectory(path);
@@ -161,6 +161,7 @@ export default async function setupAndLink(
apiUrl,
token,
debug,
output,
currentTeam: client.currentTeam,
});
const createArgs: any = {

View File

@@ -3,16 +3,34 @@ import boxen from 'boxen';
import { format } from 'util';
import { Console } from 'console';
import renderLink from './link';
import wait from './wait';
import wait, { StopSpinner } from './wait';
export type Output = ReturnType<typeof createOutput>;
export type Output = ReturnType<typeof _createOutput>;
export interface OutputOptions {
debug?: boolean;
}
// Singleton
let instance: Output | null = null;
export default function createOutput(opts?: OutputOptions) {
if (!instance) {
instance = _createOutput(opts);
}
return instance;
}
function _createOutput({ debug: debugEnabled = false }: OutputOptions = {}) {
let spinnerMessage = '';
let spinner: StopSpinner | null = null;
export default function createOutput({ debug: debugEnabled = false } = {}) {
function isDebugEnabled() {
return debugEnabled;
}
function print(str: string) {
stopSpinner();
process.stderr.write(str);
}
@@ -94,18 +112,30 @@ export default function createOutput({ debug: debugEnabled = false } = {}) {
}
}
function spinner(message: string, delay: number = 300) {
function setSpinner(message: string, delay: number = 300): void {
spinnerMessage = message;
if (debugEnabled) {
debug(`Spinner invoked (${message}) with a ${delay}ms delay`);
let isEnded = false;
return () => {
if (isEnded) return;
isEnded = true;
debug(`Spinner ended (${message})`);
};
return;
}
if (spinner) {
spinner.text = message;
} else {
spinner = wait(message, delay);
}
}
return wait(message, delay);
function stopSpinner() {
if (debugEnabled && spinnerMessage) {
const msg = `Spinner stopped (${spinnerMessage})`;
spinnerMessage = '';
debug(msg);
}
if (spinner) {
spinner();
spinner = null;
spinnerMessage = '';
}
}
const c = {
@@ -141,6 +171,7 @@ export default function createOutput({ debug: debugEnabled = false } = {}) {
dim,
time,
note,
spinner,
spinner: setSpinner,
stopSpinner,
};
}

View File

@@ -1 +1,2 @@
export { default, Output } from './create-output';
export { StopSpinner } from './wait';

View File

@@ -2,28 +2,50 @@ import ora from 'ora';
import chalk from 'chalk';
import eraseLines from './erase-lines';
export default function wait(msg: string, delay: number = 300, _ora = ora) {
let spinner: ReturnType<typeof _ora>;
let running = false;
export interface StopSpinner {
(): void;
text: string;
}
const planned = setTimeout(() => {
export default function wait(
msg: string,
delay: number = 300,
_ora = ora
): StopSpinner {
let spinner: ReturnType<typeof _ora> | null = null;
const timeout = setTimeout(() => {
spinner = _ora(chalk.gray(msg));
spinner.color = 'gray';
spinner.start();
running = true;
}, delay);
const cancel = () => {
clearTimeout(planned);
if (running) {
const stop = () => {
clearTimeout(timeout);
if (spinner) {
spinner.stop();
spinner = null;
process.stderr.write(eraseLines(1));
running = false;
}
process.removeListener('nowExit', cancel);
};
stop.text = msg;
// Allow `text` property to update the text while the spinner is in action
Object.defineProperty(stop, 'text', {
get() {
return msg;
},
set(v: string) {
msg = v;
if (spinner) {
spinner.text = chalk.gray(v);
}
},
});
// @ts-ignore
process.on('nowExit', cancel);
return cancel;
process.once('nowExit', stop);
return stop;
}

View File

@@ -138,7 +138,7 @@ export async function getLinkedProject(
return { status: 'not_linked', org: null, project: null };
}
const spinner = output.spinner('Retrieving project…', 1000);
output.spinner('Retrieving project…', 1000);
let org: Org | null = null;
let project: Project | ProjectNotFound | null = null;
try {
@@ -148,7 +148,7 @@ export async function getLinkedProject(
]);
} catch (err) {
if (err?.status === 403) {
spinner();
output.stopSpinner();
throw new NowBuildError({
message: `Could not retrieve Project Settings. To link your Project, remove the ${outputCode(
VERCEL_DIR
@@ -161,7 +161,7 @@ export async function getLinkedProject(
// Not a special case 403, we should still throw it
throw err;
} finally {
spinner();
output.stopSpinner();
}
if (!org || !project || project instanceof ProjectNotFound) {
@@ -176,7 +176,7 @@ export async function getLinkedProject(
} else {
output.print(
prependEmoji(
'Your project was either removed from Vercel or youre not a member of it anymore.\n',
'Your Project was either deleted, transferred to a new Team, or you dont have access to it anymore.\n',
emoji('warning')
)
);

View File

@@ -0,0 +1,2 @@
.vercel
!public

View File

@@ -0,0 +1 @@
body { color: green }

View File

@@ -0,0 +1,5 @@
{
"version": 2,
"cleanUrls": true,
"trailingSlash": false
}

View File

@@ -105,7 +105,8 @@ function validateResponseHeaders(t, res) {
}
async function exec(directory, args = []) {
return execa(binaryPath, ['dev', directory, ...args], {
const token = await fetchCachedToken();
return execa(binaryPath, ['dev', directory, '-t', token, ...args], {
reject: false,
shell: true,
env: { __VERCEL_SKIP_DEV_CMD: 1 },
@@ -166,9 +167,10 @@ async function testPath(
async function testFixture(directory, opts = {}, args = []) {
await runNpmInstall(directory);
const token = await fetchCachedToken();
const dev = execa(
binaryPath,
['dev', directory, '-l', String(port), ...args],
['dev', directory, '-t', token, '-l', String(port), ...args],
{
reject: false,
detached: true,
@@ -918,6 +920,32 @@ test(
})
);
test(
'[vercel dev] test cleanUrls serve correct content when using `outputDirectory`',
testFixtureStdio('test-clean-urls-with-output-directory', async testPath => {
await testPath(200, '/', 'Index Page');
await testPath(200, '/about', 'About Page');
await testPath(200, '/sub', 'Sub Index Page');
await testPath(200, '/sub/another', 'Sub Another Page');
await testPath(200, '/style.css', 'body { color: green }');
await testPath(308, '/index.html', 'Redirecting to / (308)', {
Location: '/',
});
await testPath(308, '/about.html', 'Redirecting to /about (308)', {
Location: '/about',
});
await testPath(308, '/sub/index.html', 'Redirecting to /sub (308)', {
Location: '/sub',
});
await testPath(
308,
'/sub/another.html',
'Redirecting to /sub/another (308)',
{ Location: '/sub/another' }
);
})
);
test(
'[vercel dev] should serve custom 404 when `cleanUrls: true`',
testFixtureStdio('test-clean-urls-custom-404', async testPath => {

View File

@@ -3,6 +3,7 @@ import path from 'path';
import { URL, parse as parseUrl } from 'url';
import test from 'ava';
import semVer from 'semver';
import { Readable } from 'stream';
import { homedir } from 'os';
import _execa from 'execa';
import XDGAppPaths from 'xdg-app-paths';
@@ -1171,11 +1172,11 @@ test('login with unregistered user', async t => {
console.log(exitCode);
const goal = `Error! Please sign up: https://vercel.com/signup`;
const lines = stdout.trim().split('\n');
const lines = stderr.trim().split('\n');
const last = lines[lines.length - 1];
t.is(exitCode, 1);
t.is(last, goal);
t.true(last.includes(goal));
});
test('ignore files specified in .nowignore', async t => {
@@ -1249,10 +1250,10 @@ test('list the scopes', async t => {
t.is(exitCode, 0);
const include = `${contextName} ${email}`;
const include = new RegExp(`${contextName}\\s+${email}`);
t.true(
stdout.includes(include),
include.test(stdout),
`Expected: ${include}\n\nReceived instead:\n${stdout}\n${stderr}`
);
});
@@ -1335,12 +1336,23 @@ test('try to purchase a domain', async t => {
return;
}
const stream = new Readable();
stream._read = () => {};
setTimeout(async () => {
await sleep(ms('1s'));
stream.push('y');
await sleep(ms('1s'));
stream.push('y');
stream.push(null);
}, ms('1s'));
const { stderr, stdout, exitCode } = await execa(
binaryPath,
['domains', 'buy', `${session}-test.org`, ...defaultArgs],
{
reject: false,
input: 'y',
input: stream,
}
);
@@ -1588,7 +1600,8 @@ test('output logs with "short" output', async t => {
t.truthy(
stdout.match(
/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/
)
),
formatOutput({ stderr, stdout })
);
t.is(exitCode, 0);
@@ -3120,7 +3133,7 @@ test('deploy shows notice when project in `.vercel` does not exists', async t =>
detectedNotice =
detectedNotice ||
chunk.includes(
'Your project was either removed from Vercel or youre not a member of it anymore'
'Your Project was either deleted, transferred to a new Team, or you dont have access to it anymore'
);
return /Set up and deploy [^?]+\?/.test(chunk);

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "9.0.5",
"version": "9.0.6",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -37,7 +37,7 @@
]
},
"dependencies": {
"@vercel/build-utils": "2.7.0",
"@vercel/build-utils": "2.9.0",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",
"async-sema": "3.0.0",

View File

@@ -3,4 +3,3 @@ dist
*.log
/go
/analyze
*.js

58
packages/now-go/build.js Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env node
const fs = require('fs-extra');
const execa = require('execa');
const { join } = require('path');
async function main() {
const outDir = join(__dirname, 'dist');
// Start fresh
await fs.remove(outDir);
// Build with `ncc`
await execa(
'ncc',
[
'build',
'index.ts',
'-e',
'@vercel/build-utils',
'-e',
'@now/build-utils',
'-o',
outDir,
],
{
stdio: 'inherit',
}
);
const installDir = join(outDir, 'install');
await execa(
'ncc',
[
'build',
'install.ts',
'-e',
'@vercel/build-utils',
'-e',
'@now/build-utils',
'-o',
installDir,
],
{
stdio: 'inherit',
}
);
// Move compiled ncc file to out dir
await fs.rename(join(installDir, 'index.js'), join(outDir, 'install.js'));
// Delete leftover "install" dir
await fs.remove(installDir);
}
main().catch(err => {
console.error(err);
process.exit(1);
});

View File

@@ -1,13 +0,0 @@
#!/bin/bash
set -euo pipefail
# Start fresh
rm -rf dist
# Build with `ncc`
ncc build index.ts -e @vercel/build-utils -e @now/build-utils -o dist
ncc build install.ts -e @vercel/build-utils -e @now/build-utils -o dist/install
# Move `install.js` to dist
mv dist/install/index.js dist/install.js
rm -rf dist/install

View File

@@ -125,7 +125,7 @@ Learn more: https://github.com/golang/go/wiki/Modules
const forceMove = Boolean(meta.isDev);
const srcPath = join(goPath, 'src', 'lambda');
let downloadPath = meta.isDev ? workPath : srcPath;
let downloadPath = (meta.isDev || meta.skipDownload) ? workPath : srcPath;
let downloadedFiles = await download(files, downloadPath, meta);
debug(`Parsing AST for "${entrypoint}"`);

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/go",
"version": "1.1.7",
"version": "1.1.8",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
@@ -10,9 +10,9 @@
"directory": "packages/now-go"
},
"scripts": {
"build": "./build.sh",
"build": "node build",
"test-integration-once": "jest --env node --verbose --runInBand --bail",
"prepublish": "./build.sh",
"prepublish": "node build",
"now-postinstall": "node dist/install.js"
},
"files": [

113
packages/now-node/build.js Normal file
View File

@@ -0,0 +1,113 @@
#!/usr/bin/env node
const fs = require('fs-extra');
const execa = require('execa');
const { join } = require('path');
async function main() {
const srcDir = join(__dirname, 'src');
const outDir = join(__dirname, 'dist');
const bridgeDir = join(__dirname, '../now-node-bridge');
// Copy shared dependencies
await Promise.all([
fs.copyFile(join(bridgeDir, 'src/bridge.ts'), join(srcDir, 'bridge.ts')),
fs.copyFile(
join(bridgeDir, 'src/launcher.ts'),
join(srcDir, 'launcher.ts')
),
]);
// Start fresh
await fs.remove(outDir);
// Build TypeScript files
await execa('tsc', [], {
stdio: 'inherit',
});
// Copy type file for ts test
await fs.copyFile(
join(outDir, 'types.d.ts'),
join(__dirname, 'test/fixtures/15-helpers/ts/types.d.ts')
);
// Setup symlink for symlink test
const symlinkTarget = join(__dirname, 'test/fixtures/11-symlinks/symlink');
await fs.remove(symlinkTarget);
await fs.symlink('symlinked-asset', symlinkTarget);
// Use types.d.ts as the main types export
await Promise.all(
(await fs.readdir(outDir))
.filter(p => p.endsWith('.d.ts') && p !== 'types.d.ts')
.map(p => fs.remove(join(outDir, p)))
);
await fs.rename(join(outDir, 'types.d.ts'), join(outDir, 'index.d.ts'));
// Bundle helpers.ts with ncc
await fs.remove(join(outDir, 'helpers.js'));
const helpersDir = join(outDir, 'helpers');
await execa(
'ncc',
[
'build',
join(srcDir, 'helpers.ts'),
'-e',
'@vercel/build-utils',
'-e',
'@now/build-utils',
'-o',
helpersDir,
],
{ stdio: 'inherit' }
);
await fs.rename(join(helpersDir, 'index.js'), join(outDir, 'helpers.js'));
await fs.remove(helpersDir);
// Build source-map-support/register for source maps
const sourceMapSupportDir = join(outDir, 'source-map-support');
await execa(
'ncc',
[
'build',
join(__dirname, '../../node_modules/source-map-support/register'),
'-e',
'@vercel/build-utils',
'-e',
'@now/build-utils',
'-o',
sourceMapSupportDir,
],
{ stdio: 'inherit' }
);
await fs.rename(
join(sourceMapSupportDir, 'index.js'),
join(outDir, 'source-map-support.js')
);
await fs.remove(sourceMapSupportDir);
const mainDir = join(outDir, 'main');
await execa(
'ncc',
[
'build',
join(srcDir, 'index.ts'),
'-e',
'@vercel/build-utils',
'-e',
'@now/build-utils',
'-e',
'typescript',
'-o',
mainDir,
],
{ stdio: 'inherit' }
);
await fs.rename(join(mainDir, 'index.js'), join(outDir, 'index.js'));
await fs.remove(mainDir);
}
main().catch(err => {
console.error(err);
process.exit(1);
});

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