mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-26 19:00:08 +00:00
Compare commits
47 Commits
@vercel/ru
...
@vercel/ne
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c23b9ccd1d | ||
|
|
f61b36495c | ||
|
|
a1ec28ba36 | ||
|
|
97eeddb6ac | ||
|
|
69b62b8101 | ||
|
|
591c247c4e | ||
|
|
8dc3744cf3 | ||
|
|
8f108c51fa | ||
|
|
c8e7b266bc | ||
|
|
378d62083c | ||
|
|
8189fb2373 | ||
|
|
76f544cbea | ||
|
|
e246d2ed71 | ||
|
|
ed4c759c49 | ||
|
|
2656647891 | ||
|
|
3b3c468f3a | ||
|
|
4214de2ce0 | ||
|
|
40b7af4349 | ||
|
|
f4d86cb9df | ||
|
|
0492fcb0fc | ||
|
|
3a217dc750 | ||
|
|
b626f3fe57 | ||
|
|
96bc0c9bee | ||
|
|
f4ff8c0268 | ||
|
|
1a5681f287 | ||
|
|
88b66ae646 | ||
|
|
db0124782a | ||
|
|
a7a5d4d169 | ||
|
|
4b39e96c28 | ||
|
|
f459db9f83 | ||
|
|
ad19021969 | ||
|
|
95e41874e4 | ||
|
|
4d20a1d77b | ||
|
|
325ee261cb | ||
|
|
2e0cee490d | ||
|
|
8a9b67a3f3 | ||
|
|
f1d9a5da96 | ||
|
|
8d9c463e1f | ||
|
|
e07e8f841a | ||
|
|
0b8a2c0dab | ||
|
|
9c2b7132fa | ||
|
|
d4639a5108 | ||
|
|
ba25004ea8 | ||
|
|
639a9b03d2 | ||
|
|
28ffdfbeef | ||
|
|
5e222d3c03 | ||
|
|
94c8464728 |
2
.github/workflows/cancel.yml
vendored
2
.github/workflows/cancel.yml
vendored
@@ -2,7 +2,7 @@ name: Cancel
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
- '**'
|
||||
- '!master'
|
||||
|
||||
jobs:
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -10,8 +10,6 @@ coverage.lcov
|
||||
*.swp
|
||||
*.bak
|
||||
*.tgz
|
||||
packages/now-cli/.builders
|
||||
packages/now-cli/assets
|
||||
packages/now-cli/src/util/dev/templates/*.ts
|
||||
packages/now-cli/src/util/constants.ts
|
||||
packages/now-cli/test/**/yarn.lock
|
||||
|
||||
12
README.md
12
README.md
@@ -1,11 +1,15 @@
|
||||

|
||||
<p align="center">
|
||||
<img src="https://assets.vercel.com/image/upload/v1588805858/repositories/vercel/logo.png" height="96">
|
||||
<h3 align="center">Vercel</h3>
|
||||
<p align="center">Develop. Preview. Ship.</p>
|
||||
</p>
|
||||
|
||||
[](https://github.com/zeit/now/actions?workflow=CI)
|
||||
[](https://github.com/zeit/now/discussions)
|
||||
|
||||
## Usage
|
||||
|
||||
Get started by [Importing a Git Project](https://vercel.com/import) and use `git push` to deploy. Alternatively, you can [install Now CLI](https://vercel.com/download).
|
||||
Get started by [Importing a Git Project](https://vercel.com/import) and use `git push` to deploy. Alternatively, you can [install Vercel CLI](https://vercel.com/download).
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -17,12 +21,10 @@ For details on how to use Vercel, check out our [documentation](https://vercel.c
|
||||
2. Install dependencies with `yarn install`
|
||||
3. Compile the code: `yarn build`
|
||||
4. Link the package to the global module directory: `cd ./packages/now-cli && yarn link`
|
||||
5. You can now start using `now` anywhere inside the command line
|
||||
5. You can start using `vercel` anywhere inside the command line
|
||||
|
||||
As always, you should use `yarn test-unit` to run the tests and see if your changes have broken anything.
|
||||
|
||||
## How to Create a Release
|
||||
|
||||
If you have write access to this repository, you can read more about how to publish a release [here](https://github.com/zeit/now/wiki/Creating-a-Release).
|
||||
|
||||
|
||||
|
||||
@@ -19,5 +19,11 @@ export default withApiHandler(async function(
|
||||
req: NowRequest,
|
||||
res: NowResponse
|
||||
) {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Methods', 'GET');
|
||||
res.setHeader(
|
||||
'Access-Control-Allow-Headers',
|
||||
'Authorization, Accept, Content-Type'
|
||||
);
|
||||
return res.status(200).json(frameworks);
|
||||
});
|
||||
|
||||
25
lerna.json
25
lerna.json
@@ -1,14 +1,13 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"packages": ["packages/*"],
|
||||
"command": {
|
||||
"publish": {
|
||||
"npmClient": "npm",
|
||||
"allowBranch": ["master", "canary"],
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
}
|
||||
},
|
||||
"version": "independent"
|
||||
}
|
||||
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"packages": ["packages/*"],
|
||||
"command": {
|
||||
"publish": {
|
||||
"npmClient": "npm",
|
||||
"allowBranch": ["master"],
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
}
|
||||
},
|
||||
"version": "independent"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/frameworks",
|
||||
"version": "0.0.14-canary.0",
|
||||
"version": "0.0.14",
|
||||
"main": "frameworks.json",
|
||||
"license": "UNLICENSED"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "2.2.2-canary.7",
|
||||
"version": "2.3.2-canary.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { getPlatformEnv } from './';
|
||||
|
||||
export default function debug(message: string, ...additional: any[]) {
|
||||
if (process.env.NOW_BUILDER_DEBUG) {
|
||||
if (getPlatformEnv('BUILDER_DEBUG')) {
|
||||
console.log(message, ...additional);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ export async function detectBuilders(
|
||||
// we'll default to the root directory.
|
||||
if (hasUsedOutputDirectory && outputDirectory !== '') {
|
||||
frontendBuilder = {
|
||||
use: '@now/static',
|
||||
use: '@vercel/static',
|
||||
src: `${usedOutputDirectory}/**/*`,
|
||||
config: {
|
||||
zeroConfig: true,
|
||||
@@ -249,7 +249,7 @@ export async function detectBuilders(
|
||||
// Everything besides the api directory
|
||||
// and package.json can be served as static files
|
||||
frontendBuilder = {
|
||||
use: '@now/static',
|
||||
use: '@vercel/static',
|
||||
src: '!{api/**,package.json}',
|
||||
config: {
|
||||
zeroConfig: true,
|
||||
|
||||
@@ -23,7 +23,7 @@ interface Props {
|
||||
message: string;
|
||||
/**
|
||||
* A unique error code for this particular error.
|
||||
* Should start with the builder name such as `NOW_NODE_`.
|
||||
* Should start with the builder name such as `NODE_`.
|
||||
*/
|
||||
code: string;
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { intersects } from 'semver';
|
||||
import { intersects, validRange } from 'semver';
|
||||
import boxen from 'boxen';
|
||||
import { NodeVersion } from '../types';
|
||||
import { NowBuildError } from '../errors';
|
||||
@@ -40,12 +40,14 @@ export async function getSupportedNodeVersion(
|
||||
let selection = getLatestNodeVersion();
|
||||
|
||||
if (engineRange) {
|
||||
const found = allOptions.some(o => {
|
||||
// the array is already in order so return the first
|
||||
// match which will be the newest version of node
|
||||
selection = o;
|
||||
return intersects(o.range, engineRange);
|
||||
});
|
||||
const found =
|
||||
validRange(engineRange) &&
|
||||
allOptions.some(o => {
|
||||
// the array is already in order so return the first
|
||||
// match which will be the newest version of node
|
||||
selection = o;
|
||||
return intersects(o.range, engineRange);
|
||||
});
|
||||
if (!found) {
|
||||
const intro =
|
||||
isAuto || !engineRange
|
||||
@@ -54,7 +56,7 @@ export async function getSupportedNodeVersion(
|
||||
engineRange +
|
||||
'".';
|
||||
throw new NowBuildError({
|
||||
code: 'NOW_BUILD_UTILS_NODE_VERSION_INVALID',
|
||||
code: 'BUILD_UTILS_NODE_VERSION_INVALID',
|
||||
link:
|
||||
'https://vercel.com/docs/runtimes#official-runtimes/node-js/node-js-version',
|
||||
message: intro + '\n' + pleaseSet,
|
||||
@@ -72,7 +74,7 @@ export async function getSupportedNodeVersion(
|
||||
engineRange +
|
||||
'".';
|
||||
throw new NowBuildError({
|
||||
code: 'NOW_BUILD_UTILS_NODE_VERSION_DISCONTINUED',
|
||||
code: 'BUILD_UTILS_NODE_VERSION_DISCONTINUED',
|
||||
link:
|
||||
'https://vercel.com/docs/runtimes#official-runtimes/node-js/node-js-version',
|
||||
message: intro + '\n' + pleaseSet + '\n' + upstreamProvider,
|
||||
|
||||
@@ -39,7 +39,7 @@ export function spawnAsync(
|
||||
: 'Command';
|
||||
reject(
|
||||
new NowBuildError({
|
||||
code: `NOW_BUILD_UTILS_SPAWN_${code || signal}`,
|
||||
code: `BUILD_UTILS_SPAWN_${code || signal}`,
|
||||
message:
|
||||
opts.stdio === 'inherit'
|
||||
? `${cmd} exited with ${code || signal}`
|
||||
@@ -81,7 +81,7 @@ export function execAsync(
|
||||
|
||||
return reject(
|
||||
new NowBuildError({
|
||||
code: `NOW_BUILD_UTILS_EXEC_${code || signal}`,
|
||||
code: `BUILD_UTILS_EXEC_${code || signal}`,
|
||||
message: `${cmd} exited with ${code || signal}`,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -85,9 +85,37 @@ export * from './errors';
|
||||
* Helper function to support both `@vercel` and legacy `@now` official Runtimes.
|
||||
*/
|
||||
export const isOfficialRuntime = (desired: string, name?: string): boolean => {
|
||||
if (typeof name !== 'string') {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
typeof name === 'string' &&
|
||||
(name.startsWith(`@vercel/${desired}`) ||
|
||||
name.startsWith(`@now/${desired}`))
|
||||
name === `@vercel/${desired}` ||
|
||||
name === `@now/${desired}` ||
|
||||
name.startsWith(`@vercel/${desired}@`) ||
|
||||
name.startsWith(`@now/${desired}@`)
|
||||
);
|
||||
};
|
||||
|
||||
export const isStaticRuntime = (name?: string): boolean => {
|
||||
return isOfficialRuntime('static', name);
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to support both `VERCEL_` and legacy `NOW_` env vars.
|
||||
* Throws an error if *both* env vars are defined.
|
||||
*/
|
||||
export const getPlatformEnv = (name: string): string | undefined => {
|
||||
const vName = `VERCEL_${name}`;
|
||||
const nName = `NOW_${name}`;
|
||||
const v = process.env[vName];
|
||||
const n = process.env[nName];
|
||||
if (typeof v === 'string') {
|
||||
if (typeof n === 'string') {
|
||||
throw new Error(
|
||||
`Both "${vName}" and "${nName}" env vars are defined. Please only define the "${vName}" env var`
|
||||
);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
return n;
|
||||
};
|
||||
|
||||
@@ -49,8 +49,9 @@ export interface Config {
|
||||
|
||||
export interface Meta {
|
||||
isDev?: boolean;
|
||||
devCacheDir?: string;
|
||||
skipDownload?: boolean;
|
||||
requestPath?: string;
|
||||
requestPath?: string | null;
|
||||
filesChanged?: string[];
|
||||
filesRemoved?: string[];
|
||||
env?: Env;
|
||||
@@ -187,6 +188,34 @@ export interface ShouldServeOptions {
|
||||
config: Config;
|
||||
}
|
||||
|
||||
export interface StartDevServerOptions {
|
||||
/**
|
||||
* Name of entrypoint file for this particular build job. Value
|
||||
* `files[entrypoint]` is guaranteed to exist and be a valid File reference.
|
||||
* `entrypoint` is always a discrete file and never a glob, since globs are
|
||||
* expanded into separate builds at deployment time.
|
||||
*/
|
||||
entrypoint: string;
|
||||
|
||||
/**
|
||||
* A writable temporary directory where you are encouraged to perform your
|
||||
* build process. This directory will be populated with the restored cache.
|
||||
*/
|
||||
workPath: string;
|
||||
|
||||
/**
|
||||
* An arbitrary object passed by the user in the build definition defined
|
||||
* in `now.json`.
|
||||
*/
|
||||
config: Config;
|
||||
|
||||
/**
|
||||
* Runtime environment variables configuration from the project's `now.json`
|
||||
* and local `.env` file.
|
||||
*/
|
||||
env: Env;
|
||||
}
|
||||
|
||||
export interface StartDevServerSuccess {
|
||||
/**
|
||||
* Port number where the dev server can be connected to, assumed to be running
|
||||
@@ -212,7 +241,7 @@ export type StartDevServerResult = StartDevServerSuccess | null;
|
||||
* Source: https://gist.github.com/iainreid820/5c1cc527fe6b5b7dba41fec7fe54bf6e
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace PackageJson {
|
||||
export namespace PackageJson {
|
||||
/**
|
||||
* An author or contributor
|
||||
*/
|
||||
|
||||
@@ -68,7 +68,7 @@ describe('Test `detectBuilders`', () => {
|
||||
it('no package.json + public', async () => {
|
||||
const files = ['api/users.js', 'public/index.html'];
|
||||
const { builders, errors } = await detectBuilders(files);
|
||||
expect(builders![1].use).toBe('@now/static');
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
expect(errors).toBe(null);
|
||||
});
|
||||
|
||||
@@ -77,7 +77,7 @@ describe('Test `detectBuilders`', () => {
|
||||
const { builders, errors } = await detectBuilders(files);
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/users.js');
|
||||
expect(builders![1].use).toBe('@now/static');
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
expect(builders![1].src).toBe('!{api/**,package.json}');
|
||||
expect(builders!.length).toBe(2);
|
||||
expect(errors).toBe(null);
|
||||
@@ -88,7 +88,7 @@ describe('Test `detectBuilders`', () => {
|
||||
const { builders, errors } = await detectBuilders(files);
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/[endpoint].js');
|
||||
expect(builders![1].use).toBe('@now/static');
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
expect(builders![1].src).toBe('!{api/**,package.json}');
|
||||
expect(builders!.length).toBe(2);
|
||||
expect(errors).toBe(null);
|
||||
@@ -143,7 +143,7 @@ describe('Test `detectBuilders`', () => {
|
||||
const { builders } = await detectBuilders(files);
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/endpoint.js');
|
||||
expect(builders![1].use).toBe('@now/static');
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
expect(builders![1].src).toBe('!{api/**,package.json}');
|
||||
expect(builders!.length).toBe(2);
|
||||
});
|
||||
@@ -159,7 +159,7 @@ describe('Test `detectBuilders`', () => {
|
||||
const { builders } = await detectBuilders(files);
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/endpoint.js');
|
||||
expect(builders![1].use).toBe('@now/static');
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
expect(builders![1].src).toBe('public/**/*');
|
||||
expect(builders!.length).toBe(2);
|
||||
});
|
||||
@@ -346,7 +346,7 @@ describe('Test `detectBuilders`', () => {
|
||||
expect(builders!.length).toBe(2);
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/index.ts');
|
||||
expect(builders![1].use).toBe('@now/static');
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
expect(builders![1].src).toBe('!{api/**,package.json}');
|
||||
});
|
||||
|
||||
@@ -667,7 +667,7 @@ describe('Test `detectBuilders`', () => {
|
||||
|
||||
expect(builders!.length).toBe(1);
|
||||
expect(builders![0].src).toBe('dist/**/*');
|
||||
expect(builders![0].use).toBe('@now/static');
|
||||
expect(builders![0].use).toBe('@vercel/static');
|
||||
|
||||
expect(defaultRoutes!.length).toBe(1);
|
||||
expect((defaultRoutes![0] as any).src).toBe('/(.*)');
|
||||
@@ -687,7 +687,7 @@ describe('Test `detectBuilders`', () => {
|
||||
|
||||
expect(builders!.length).toBe(2);
|
||||
expect(builders![1].src).toBe('output/**/*');
|
||||
expect(builders![1].use).toBe('@now/static');
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
|
||||
expect(defaultRoutes!.length).toBe(3);
|
||||
expect((defaultRoutes![1] as any).status).toBe(404);
|
||||
@@ -807,7 +807,7 @@ describe('Test `detectBuilders`', () => {
|
||||
projectSettings,
|
||||
});
|
||||
expect(errors).toBe(null);
|
||||
expect(builders![0]!.use).toBe('@now/static');
|
||||
expect(builders![0]!.use).toBe('@vercel/static');
|
||||
expect(builders![0]!.src).toBe('out/**/*');
|
||||
});
|
||||
|
||||
@@ -930,7 +930,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
redirectRoutes,
|
||||
rewriteRoutes,
|
||||
} = await detectBuilders(files, null, { featHandleMiss });
|
||||
expect(builders![1].use).toBe('@now/static');
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
expect(errors).toBe(null);
|
||||
|
||||
expect(defaultRoutes!.length).toBe(2);
|
||||
@@ -952,7 +952,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
} = await detectBuilders(files, null, { featHandleMiss });
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/users.js');
|
||||
expect(builders![1].use).toBe('@now/static');
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
expect(builders![1].src).toBe('!{api/**,package.json}');
|
||||
expect(builders!.length).toBe(2);
|
||||
expect(errors).toBe(null);
|
||||
@@ -972,7 +972,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
});
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/[endpoint].js');
|
||||
expect(builders![1].use).toBe('@now/static');
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
expect(builders![1].src).toBe('!{api/**,package.json}');
|
||||
expect(builders!.length).toBe(2);
|
||||
expect(errors).toBe(null);
|
||||
@@ -1069,7 +1069,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
} = await detectBuilders(files, null, { featHandleMiss });
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/endpoint.js');
|
||||
expect(builders![1].use).toBe('@now/static');
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
expect(builders![1].src).toBe('!{api/**,package.json}');
|
||||
expect(builders!.length).toBe(2);
|
||||
|
||||
@@ -1096,7 +1096,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
} = await detectBuilders(files, pkg, { featHandleMiss });
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/version.js');
|
||||
expect(builders![1].use).toBe('@now/static');
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
expect(builders![1].src).toBe('!{api/**,package.json}');
|
||||
expect(builders!.length).toBe(2);
|
||||
|
||||
@@ -1121,7 +1121,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
});
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/endpoint.js');
|
||||
expect(builders![1].use).toBe('@now/static');
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
expect(builders![1].src).toBe('public/**/*');
|
||||
expect(builders!.length).toBe(2);
|
||||
});
|
||||
@@ -1334,7 +1334,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(builders!.length).toBe(2);
|
||||
expect(builders![0].use).toBe('@vercel/node');
|
||||
expect(builders![0].src).toBe('api/index.ts');
|
||||
expect(builders![1].use).toBe('@now/static');
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
expect(builders![1].src).toBe('!{api/**,package.json}');
|
||||
});
|
||||
|
||||
@@ -1688,7 +1688,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
|
||||
expect(builders!.length).toBe(1);
|
||||
expect(builders![0].src).toBe('dist/**/*');
|
||||
expect(builders![0].use).toBe('@now/static');
|
||||
expect(builders![0].use).toBe('@vercel/static');
|
||||
|
||||
expect(defaultRoutes).toStrictEqual([]);
|
||||
expect(redirectRoutes).toStrictEqual([]);
|
||||
@@ -1714,7 +1714,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
|
||||
expect(builders!.length).toBe(2);
|
||||
expect(builders![1].src).toBe('output/**/*');
|
||||
expect(builders![1].use).toBe('@now/static');
|
||||
expect(builders![1].use).toBe('@vercel/static');
|
||||
|
||||
expect(defaultRoutes!.length).toBe(2);
|
||||
expect((defaultRoutes![0] as Handler).handle).toBe('miss');
|
||||
@@ -1848,7 +1848,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
featHandleMiss,
|
||||
});
|
||||
expect(errors).toBe(null);
|
||||
expect(builders![0]!.use).toBe('@now/static');
|
||||
expect(builders![0]!.use).toBe('@vercel/static');
|
||||
expect(builders![0]!.src).toBe('out/**/*');
|
||||
});
|
||||
|
||||
@@ -2843,7 +2843,7 @@ describe('Test `detectOutputDirectory`', () => {
|
||||
it('should be `null` with no config', async () => {
|
||||
const builders = [
|
||||
{
|
||||
use: '@now/static',
|
||||
use: '@vercel/static',
|
||||
src: 'public/**/*',
|
||||
},
|
||||
];
|
||||
@@ -2854,7 +2854,7 @@ describe('Test `detectOutputDirectory`', () => {
|
||||
it('should be `null` with no zero config builds', async () => {
|
||||
const builders = [
|
||||
{
|
||||
use: '@now/static',
|
||||
use: '@vercel/static',
|
||||
src: 'public/**/*',
|
||||
config: {},
|
||||
},
|
||||
@@ -2866,7 +2866,7 @@ describe('Test `detectOutputDirectory`', () => {
|
||||
it('should be `public` with one zero config', async () => {
|
||||
const builders = [
|
||||
{
|
||||
use: '@now/static',
|
||||
use: '@vercel/static',
|
||||
src: 'public/**/*',
|
||||
config: { zeroConfig: true },
|
||||
},
|
||||
@@ -2878,7 +2878,7 @@ describe('Test `detectOutputDirectory`', () => {
|
||||
it('should be `public` with one zero config and one without config', async () => {
|
||||
const builders = [
|
||||
{
|
||||
use: '@now/static',
|
||||
use: '@vercel/static',
|
||||
src: 'public/**/*',
|
||||
config: { zeroConfig: true },
|
||||
},
|
||||
|
||||
45
packages/now-build-utils/test/unit.get-platform-env.test.ts
vendored
Normal file
45
packages/now-build-utils/test/unit.get-platform-env.test.ts
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
import assert from 'assert';
|
||||
import { getPlatformEnv } from '../src';
|
||||
|
||||
describe('Test `getPlatformEnv()`', () => {
|
||||
it('should support `VERCEL_` prefix', () => {
|
||||
try {
|
||||
assert.equal(undefined, getPlatformEnv('FOO'));
|
||||
|
||||
process.env.VERCEL_FOO = 'bar';
|
||||
assert.equal('bar', getPlatformEnv('FOO'));
|
||||
} finally {
|
||||
delete process.env.VERCEL_FOO;
|
||||
}
|
||||
});
|
||||
|
||||
it('should support `NOW_` prefix', () => {
|
||||
try {
|
||||
assert.equal(undefined, getPlatformEnv('FOO'));
|
||||
|
||||
process.env.NOW_FOO = 'bar';
|
||||
assert.equal('bar', getPlatformEnv('FOO'));
|
||||
} finally {
|
||||
delete process.env.NOW_FOO;
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw an error if both env vars exist', () => {
|
||||
let err: Error | null = null;
|
||||
try {
|
||||
process.env.NOW_FOO = 'bar';
|
||||
process.env.VERCEL_FOO = 'baz';
|
||||
getPlatformEnv('FOO');
|
||||
} catch (_err) {
|
||||
err = _err;
|
||||
} finally {
|
||||
delete process.env.NOW_FOO;
|
||||
delete process.env.VERCEL_FOO;
|
||||
}
|
||||
assert(err);
|
||||
assert.equal(
|
||||
err!.message,
|
||||
'Both "VERCEL_FOO" and "NOW_FOO" env vars are defined. Please only define the "VERCEL_FOO" env var'
|
||||
);
|
||||
});
|
||||
});
|
||||
40
packages/now-build-utils/test/unit.is-official-runtime.test.ts
vendored
Normal file
40
packages/now-build-utils/test/unit.is-official-runtime.test.ts
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
import assert from 'assert';
|
||||
import { isOfficialRuntime, isStaticRuntime } from '../src';
|
||||
|
||||
describe('Test `isOfficialRuntime()`', () => {
|
||||
it('should be correct', () => {
|
||||
assert.equal(true, isOfficialRuntime('static', '@vercel/static'));
|
||||
assert.equal(true, isOfficialRuntime('static', '@now/static'));
|
||||
assert.equal(false, isOfficialRuntime('static', '@vercel/static-build'));
|
||||
assert.equal(false, isOfficialRuntime('static', '@now/static-build'));
|
||||
|
||||
assert.equal(true, isOfficialRuntime('node', '@vercel/node'));
|
||||
assert.equal(true, isOfficialRuntime('node', '@now/node'));
|
||||
assert.equal(true, isOfficialRuntime('node', '@vercel/node@1.0.0'));
|
||||
assert.equal(true, isOfficialRuntime('node', '@now/node@1.0.0'));
|
||||
assert.equal(false, isOfficialRuntime('node', '@my-fork/node'));
|
||||
assert.equal(false, isOfficialRuntime('node', '@now/node-server'));
|
||||
|
||||
assert.equal(
|
||||
true,
|
||||
isOfficialRuntime('static-build', '@vercel/static-build')
|
||||
);
|
||||
assert.equal(true, isOfficialRuntime('static-build', '@now/static-build'));
|
||||
assert.equal(
|
||||
true,
|
||||
isOfficialRuntime('static-build', '@vercel/static-build@1.0.0')
|
||||
);
|
||||
assert.equal(false, isOfficialRuntime('static-build', '@vercel/static'));
|
||||
assert.equal(false, isOfficialRuntime('static-build', '@now/static'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test `isStaticRuntime()`', () => {
|
||||
it('should be correct', () => {
|
||||
assert.equal(true, isStaticRuntime('@vercel/static'));
|
||||
assert.equal(true, isStaticRuntime('@now/static'));
|
||||
assert.equal(false, isStaticRuntime('@vercel/static-build'));
|
||||
assert.equal(false, isStaticRuntime('@now/static-build'));
|
||||
assert.equal(false, isStaticRuntime('@now/node'));
|
||||
});
|
||||
});
|
||||
45
packages/now-build-utils/test/unit.test.js
vendored
45
packages/now-build-utils/test/unit.test.js
vendored
@@ -1,6 +1,6 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const assert = require('assert');
|
||||
const assert = require('assert').strict;
|
||||
const { createZip } = require('../dist/lambda');
|
||||
const { glob, spawnAsync, download } = require('../');
|
||||
const { getSupportedNodeVersion } = require('../dist/fs/node-version');
|
||||
@@ -10,6 +10,20 @@ const {
|
||||
getDiscontinuedNodeVersions,
|
||||
} = require('../dist');
|
||||
|
||||
async function expectBuilderError(promise, pattern) {
|
||||
let result;
|
||||
try {
|
||||
result = await promise;
|
||||
} catch (error) {
|
||||
result = error;
|
||||
}
|
||||
assert('message' in result, `Expected error message but found ${result}`);
|
||||
assert(
|
||||
pattern.test(result.message),
|
||||
`Expected ${pattern} but found "${result.message}"`
|
||||
);
|
||||
}
|
||||
|
||||
it('should re-create symlinks properly', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log('Skipping test on windows');
|
||||
@@ -72,6 +86,16 @@ it('should only match supported node versions', async () => {
|
||||
expect(getSupportedNodeVersion('999.x', false)).rejects.toThrow();
|
||||
expect(getSupportedNodeVersion('foo', false)).rejects.toThrow();
|
||||
|
||||
const autoMessage = /This project is using an invalid version of Node.js and must be changed/;
|
||||
await expectBuilderError(
|
||||
getSupportedNodeVersion('8.11.x', true),
|
||||
autoMessage
|
||||
);
|
||||
await expectBuilderError(getSupportedNodeVersion('6.x', true), autoMessage);
|
||||
await expectBuilderError(getSupportedNodeVersion('999.x', true), autoMessage);
|
||||
await expectBuilderError(getSupportedNodeVersion('foo', true), autoMessage);
|
||||
await expectBuilderError(getSupportedNodeVersion('=> 10', true), autoMessage);
|
||||
|
||||
expect(await getSupportedNodeVersion('10.x', true)).toHaveProperty(
|
||||
'major',
|
||||
10
|
||||
@@ -80,10 +104,21 @@ it('should only match supported node versions', async () => {
|
||||
'major',
|
||||
12
|
||||
);
|
||||
expect(getSupportedNodeVersion('8.11.x', true)).rejects.toThrow();
|
||||
expect(getSupportedNodeVersion('6.x', true)).rejects.toThrow();
|
||||
expect(getSupportedNodeVersion('999.x', true)).rejects.toThrow();
|
||||
expect(getSupportedNodeVersion('foo', true)).rejects.toThrow();
|
||||
const foundMessage = /Found `engines` in `package\.json` with an invalid Node\.js version range/;
|
||||
await expectBuilderError(
|
||||
getSupportedNodeVersion('8.11.x', false),
|
||||
foundMessage
|
||||
);
|
||||
await expectBuilderError(getSupportedNodeVersion('6.x', false), foundMessage);
|
||||
await expectBuilderError(
|
||||
getSupportedNodeVersion('999.x', false),
|
||||
foundMessage
|
||||
);
|
||||
await expectBuilderError(getSupportedNodeVersion('foo', false), foundMessage);
|
||||
await expectBuilderError(
|
||||
getSupportedNodeVersion('=> 10', false),
|
||||
foundMessage
|
||||
);
|
||||
});
|
||||
|
||||
it('should match all semver ranges', async () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/cgi",
|
||||
"version": "1.0.5-canary.1",
|
||||
"version": "1.0.5",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||

|
||||
<p align="center">
|
||||
<img src="https://assets.vercel.com/image/upload/v1588805858/repositories/vercel/logo.png" height="96">
|
||||
<h3 align="center">Vercel</h3>
|
||||
<p align="center">Develop. Preview. Ship.</p>
|
||||
</p>
|
||||
|
||||
[](https://github.com/zeit/now/discussions)
|
||||
|
||||
## Usages
|
||||
|
||||
To install the latest version of Now CLI, visit [vercel.com/download](https://vercel.com/download) or run this command:
|
||||
To install the latest version of Vercel CLI, visit [vercel.com/download](https://vercel.com/download) or run this command:
|
||||
|
||||
```sh
|
||||
npm i -g now
|
||||
npm i -g vercel
|
||||
```
|
||||
|
||||
To quickly start a new project, run the following commands:
|
||||
|
||||
```
|
||||
now init # Pick an example project
|
||||
vercel init # Pick an example project
|
||||
cd <PROJECT> # Change directory to the new project
|
||||
now # Deploy to the cloud
|
||||
vercel # Deploy to the cloud
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
For details on how to use Now CLI, check out our [documentation](https://vercel.com/docs).
|
||||
For details on how to use Vercel CLI, check out our [documentation](https://vercel.com/docs).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "18.0.1-canary.19",
|
||||
"version": "19.0.2-canary.1",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Now",
|
||||
@@ -41,7 +41,7 @@
|
||||
"all": true
|
||||
},
|
||||
"bin": {
|
||||
"now": "./dist/index.js",
|
||||
"vc": "./dist/index.js",
|
||||
"vercel": "./dist/index.js"
|
||||
},
|
||||
"files": [
|
||||
@@ -61,9 +61,19 @@
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.3.2-canary.0",
|
||||
"@vercel/go": "1.1.1",
|
||||
"@vercel/next": "2.6.2-canary.0",
|
||||
"@vercel/node": "1.6.2-canary.0",
|
||||
"@vercel/python": "1.2.1",
|
||||
"@vercel/ruby": "1.2.1",
|
||||
"@vercel/static-build": "0.17.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sentry/node": "5.5.0",
|
||||
"@sindresorhus/slugify": "0.11.0",
|
||||
"@tootallnate/once": "1.1.2",
|
||||
"@types/ansi-escapes": "3.0.0",
|
||||
"@types/ansi-regex": "4.0.0",
|
||||
"@types/async-retry": "1.2.1",
|
||||
@@ -179,7 +189,6 @@
|
||||
"which": "2.0.2",
|
||||
"which-promise": "1.0.0",
|
||||
"write-json-file": "2.2.0",
|
||||
"xdg-app-paths": "5.1.0",
|
||||
"yarn": "1.22.0"
|
||||
"xdg-app-paths": "5.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +1,10 @@
|
||||
import cpy from 'cpy';
|
||||
import tar from 'tar-fs';
|
||||
import execa from 'execa';
|
||||
import { join } from 'path';
|
||||
import pipe from 'promisepipe';
|
||||
import { createGzip } from 'zlib';
|
||||
import {
|
||||
createWriteStream,
|
||||
mkdirp,
|
||||
remove,
|
||||
writeJSON,
|
||||
writeFile,
|
||||
} from 'fs-extra';
|
||||
|
||||
import { getDistTag } from '../src/util/get-dist-tag';
|
||||
import pkg from '../package.json';
|
||||
import { getBundledBuilders } from '../src/util/dev/get-bundled-builders';
|
||||
import { remove, writeFile } from 'fs-extra';
|
||||
|
||||
const dirRoot = join(__dirname, '..');
|
||||
|
||||
async function createBuildersTarball() {
|
||||
const distTag = getDistTag(pkg.version);
|
||||
const builders = Array.from(getBundledBuilders()).map(b => `${b}@${distTag}`);
|
||||
console.log(`Creating builders tarball with: ${builders.join(', ')}`);
|
||||
|
||||
const buildersDir = join(dirRoot, '.builders');
|
||||
const assetsDir = join(dirRoot, 'assets');
|
||||
await mkdirp(buildersDir);
|
||||
await mkdirp(assetsDir);
|
||||
|
||||
const buildersTarballPath = join(assetsDir, 'builders.tar.gz');
|
||||
|
||||
try {
|
||||
const buildersPkg = join(buildersDir, 'package.json');
|
||||
await writeJSON(buildersPkg, { private: true }, { flag: 'wx' });
|
||||
} catch (err) {
|
||||
if (err.code !== 'EEXIST') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
const yarn = join(dirRoot, '../../node_modules/yarn/bin/yarn.js');
|
||||
await execa(process.execPath, [yarn, 'add', '--no-lockfile', ...builders], {
|
||||
cwd: buildersDir,
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
const packer = tar.pack(buildersDir);
|
||||
await pipe(
|
||||
packer,
|
||||
createGzip(),
|
||||
createWriteStream(buildersTarballPath)
|
||||
);
|
||||
}
|
||||
|
||||
async function createConstants() {
|
||||
console.log('Creating constants.ts');
|
||||
const filename = join(dirRoot, 'src/util/constants.ts');
|
||||
@@ -81,10 +33,6 @@ async function main() {
|
||||
// During local development, these secrets will be empty.
|
||||
await createConstants();
|
||||
|
||||
// Create a tarball from all the `@now` scoped builders which will be bundled
|
||||
// with Now CLI
|
||||
await createBuildersTarball();
|
||||
|
||||
// `now dev` uses chokidar to watch the filesystem, but opts-out of the
|
||||
// `fsevents` feature using `useFsEvents: false`, so delete the module here so
|
||||
// that it is not compiled by ncc, which makes the npm package size larger
|
||||
|
||||
@@ -9,8 +9,7 @@ import getScope from '../../util/get-scope.ts';
|
||||
import stamp from '../../util/output/stamp.ts';
|
||||
import strlen from '../../util/strlen.ts';
|
||||
import getCommandFlags from '../../util/get-command-flags';
|
||||
import cmd from '../../util/output/cmd.ts';
|
||||
import { getPkgName } from '../../util/pkg-name.ts';
|
||||
import { getCommandName } from '../../util/pkg-name.ts';
|
||||
|
||||
export default async function ls(ctx, opts, args, output) {
|
||||
const {
|
||||
@@ -56,7 +55,7 @@ export default async function ls(ctx, opts, args, output) {
|
||||
if (args.length > 1) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} alias ls [alias]`
|
||||
`${getCommandName('alias ls [alias]')}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
@@ -107,8 +106,8 @@ export default async function ls(ctx, opts, args, output) {
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(opts, ['_', '--next']);
|
||||
output.log(
|
||||
`To display the next page run ${cmd(
|
||||
`${getPkgName()} alias ls${flags} --next ${pagination.next}`
|
||||
`To display the next page run ${getCommandName(
|
||||
`alias ls${flags} --next ${pagination.next}`
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import table from 'text-table';
|
||||
import Now from '../../util';
|
||||
import cmd from '../../util/output/cmd.ts';
|
||||
import Client from '../../util/client.ts';
|
||||
import getScope from '../../util/get-scope.ts';
|
||||
import removeAliasById from '../../util/alias/remove-alias-by-id';
|
||||
@@ -11,7 +10,7 @@ import strlen from '../../util/strlen.ts';
|
||||
import promptBool from '../../util/prompt-bool';
|
||||
import { isValidName } from '../../util/is-valid-name';
|
||||
import findAliasByAliasOrId from '../../util/alias/find-alias-by-alias-or-id';
|
||||
import { getPkgName } from '../../util/pkg-name.ts';
|
||||
import { getCommandName } from '../../util/pkg-name.ts';
|
||||
|
||||
export default async function rm(ctx, opts, args, output) {
|
||||
const {
|
||||
@@ -47,16 +46,14 @@ export default async function rm(ctx, opts, args, output) {
|
||||
if (args.length !== 1) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} alias rm <alias>`
|
||||
`${getCommandName('alias rm <alias>')}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!aliasOrId) {
|
||||
output.error(
|
||||
`${cmd(`${getPkgName()} alias rm <alias>`)} expects one argument`
|
||||
);
|
||||
output.error(`${getCommandName('alias rm <alias>')} expects one argument`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -71,7 +68,7 @@ export default async function rm(ctx, opts, args, output) {
|
||||
output.error(
|
||||
`Alias not found by "${aliasOrId}" under ${chalk.bold(contextName)}`
|
||||
);
|
||||
output.log(`Run ${cmd(`${getPkgName} alias ls`)} to see your aliases.`);
|
||||
output.log(`Run ${getCommandName('alias ls')} to see your aliases.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import { Output } from '../../util/output';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import assignAlias from '../../util/alias/assign-alias';
|
||||
import Client from '../../util/client';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import formatDnsTable from '../../util/format-dns-table';
|
||||
import formatNSTable from '../../util/format-ns-table';
|
||||
import getDeploymentForAlias from '../../util/alias/get-deployment-for-alias';
|
||||
@@ -23,7 +22,7 @@ import handleCertError from '../../util/certs/handle-cert-error';
|
||||
import isWildcardAlias from '../../util/alias/is-wildcard-alias';
|
||||
import link from '../../util/output/link';
|
||||
import { User } from '../../types';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -78,8 +77,8 @@ export default async function set(
|
||||
// If there are more than two args we have to error
|
||||
if (args.length > 2) {
|
||||
output.error(
|
||||
`${cmd(
|
||||
`${getPkgName()} alias <deployment> <target>`
|
||||
`${getCommandName(
|
||||
`alias <deployment> <target>`
|
||||
)} accepts at most two arguments`
|
||||
);
|
||||
return 1;
|
||||
@@ -129,7 +128,7 @@ export default async function set(
|
||||
output.error(
|
||||
`To ship to production, optionally configure your domains (${link(
|
||||
'https://vercel.com/docs/v2/custom-domains'
|
||||
)}) and run ${cmd(`${getPkgName()} --prod`)}.`
|
||||
)}) and run ${getCommandName(`--prod`)}.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
@@ -304,8 +303,8 @@ function handleSetupDomainError<T>(
|
||||
)}\n\n`
|
||||
);
|
||||
output.print(
|
||||
` Once your domain uses either the nameservers or the TXT DNS record from above, run again ${cmd(
|
||||
`${getPkgName()} domains verify <domain>`
|
||||
` Once your domain uses either the nameservers or the TXT DNS record from above, run again ${getCommandName(
|
||||
'domains verify <domain>'
|
||||
)}.\n`
|
||||
);
|
||||
output.print(
|
||||
@@ -313,8 +312,8 @@ function handleSetupDomainError<T>(
|
||||
);
|
||||
} else {
|
||||
output.print(
|
||||
` Once your domain uses the nameservers from above, run again ${cmd(
|
||||
`${getPkgName()} domains verify <domain>`
|
||||
` Once your domain uses the nameservers from above, run again ${getCommandName(
|
||||
'domains verify <domain>'
|
||||
)}.\n`
|
||||
);
|
||||
}
|
||||
@@ -477,7 +476,7 @@ function handleCreateAliasError<T>(
|
||||
output.log(
|
||||
`Update the scale settings on ${chalk.dim(
|
||||
error.meta.url
|
||||
)} with \`${getPkgName()} scale\` and try again`
|
||||
)} with ${getCommandName('scale')} and try again`
|
||||
);
|
||||
output.log('Read more: https://err.sh/now/v2-no-min');
|
||||
return 1;
|
||||
@@ -493,7 +492,7 @@ function handleCreateAliasError<T>(
|
||||
output.log(
|
||||
`Update the scale settings on ${chalk.dim(
|
||||
error.meta.url
|
||||
)} with \`${getPkgName()} scale\` and try again`
|
||||
)} with ${getCommandName('scale')} and try again`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
@@ -513,7 +512,7 @@ function handleCreateAliasError<T>(
|
||||
output.log(
|
||||
`Update the scale settings on ${chalk.dim(
|
||||
error.meta.url
|
||||
)} with \`${getPkgName()} scale\` and try again`
|
||||
)} with ${getCommandName('scale')} and try again`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
@@ -523,8 +522,8 @@ function handleCreateAliasError<T>(
|
||||
`There is no certificate for the domain ${error.meta.domain} and it could not be created.`
|
||||
);
|
||||
output.log(
|
||||
`Please generate a new certificate manually with ${cmd(
|
||||
`${getPkgName()} certs issue ${error.meta.domain}`
|
||||
`Please generate a new certificate manually with ${getCommandName(
|
||||
`certs issue ${error.meta.domain}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
|
||||
@@ -9,7 +9,7 @@ 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 { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
interface Options {
|
||||
'--overwrite'?: boolean;
|
||||
@@ -77,7 +77,9 @@ async function add(
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan(
|
||||
`${getPkgName()} certs add --crt <domain.crt> --key <domain.key> --ca <ca.crt>`
|
||||
`${getCommandName(
|
||||
'certs add --crt <domain.crt> --key <domain.key> --ca <ca.crt>'
|
||||
)}`
|
||||
)}\n`
|
||||
);
|
||||
now.close();
|
||||
@@ -89,9 +91,9 @@ async function add(
|
||||
} else {
|
||||
output.warn(
|
||||
`${chalk.cyan(
|
||||
`${getPkgName()} certs add`
|
||||
getCommandName('certs add')
|
||||
)} will be soon deprecated. Please use ${chalk.cyan(
|
||||
`${getPkgName()} certs issue <cn> <cns>`
|
||||
getCommandName('certs issue <cn> <cns>')
|
||||
)} instead`
|
||||
);
|
||||
|
||||
@@ -100,7 +102,7 @@ async function add(
|
||||
`Invalid number of arguments to create a custom certificate entry. Usage:`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan(`${getPkgName()} certs add <cn>[, <cn>]`)}\n`
|
||||
` ${chalk.cyan(getCommandName('certs add <cn>[, <cn>]'))}\n`
|
||||
);
|
||||
now.close();
|
||||
return 1;
|
||||
|
||||
@@ -14,7 +14,7 @@ import getScope from '../../util/get-scope';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import startCertOrder from '../../util/certs/start-cert-order';
|
||||
import handleCertError from '../../util/certs/handle-cert-error';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--ca': string;
|
||||
@@ -81,7 +81,9 @@ export default async function issue(
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan(
|
||||
`${getPkgName()} certs issue --crt <domain.crt> --key <domain.key> --ca <ca.crt>`
|
||||
getCommandName(
|
||||
'certs issue --crt <domain.crt> --key <domain.key> --ca <ca.crt>'
|
||||
)
|
||||
)}\n`
|
||||
);
|
||||
return 1;
|
||||
@@ -109,7 +111,7 @@ export default async function issue(
|
||||
`Invalid number of arguments to create a custom certificate entry. Usage:`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan(`${getPkgName()} certs issue <cn>[, <cn>]`)}\n`
|
||||
` ${chalk.cyan(getCommandName('certs issue <cn>[, <cn>]'))}\n`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
@@ -193,7 +195,7 @@ async function runStartOrder(
|
||||
` There are no pending challenges. Finish the issuance by running: \n`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan(`${getPkgName()} certs issue ${cns.join(' ')}`)}\n`
|
||||
` ${chalk.cyan(getCommandName(`certs issue ${cns.join(' ')}`))}\n`
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -226,7 +228,7 @@ async function runStartOrder(
|
||||
process.stdout.write(`${rows.join('\n')}\n\n`);
|
||||
output.log(`To issue the certificate once the records are added, run:`);
|
||||
output.print(
|
||||
` ${chalk.cyan(`${getPkgName()} certs issue ${cns.join(' ')}`)}\n`
|
||||
` ${chalk.cyan(getCommandName(`certs issue ${cns.join(' ')}`))}\n`
|
||||
);
|
||||
output.print(' Read more: https://err.sh/now/solve-challenges-manually\n');
|
||||
return 0;
|
||||
|
||||
@@ -3,7 +3,6 @@ import ms from 'ms';
|
||||
import table from 'text-table';
|
||||
// @ts-ignore
|
||||
import Now from '../../util';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import Client from '../../util/client';
|
||||
import getScope from '../../util/get-scope';
|
||||
import stamp from '../../util/output/stamp';
|
||||
@@ -12,7 +11,7 @@ import strlen from '../../util/strlen';
|
||||
import { Output } from '../../util/output';
|
||||
import { NowContext, Cert } from '../../types';
|
||||
import getCommandFlags from '../../util/get-command-flags';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
interface Options {
|
||||
'--debug'?: boolean;
|
||||
@@ -55,7 +54,7 @@ async function ls(
|
||||
if (args.length !== 0) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} certs ls`
|
||||
`${getCommandName('certs ls')}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
@@ -79,8 +78,8 @@ async function ls(
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(opts, ['_', '--next']);
|
||||
output.log(
|
||||
`To display the next page run ${cmd(
|
||||
`${getPkgName()} certs ls${flags} --next ${pagination.next}`
|
||||
`To display the next page run ${getCommandName(
|
||||
`certs ls${flags} --next ${pagination.next}`
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import Client from '../../util/client';
|
||||
import getScope from '../../util/get-scope';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import param from '../../util/output/param';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -50,7 +50,7 @@ async function rm(
|
||||
if (args.length !== 1) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} certs rm <id or cn>`
|
||||
`${getCommandName('certs rm <id or cn>')}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import fs from 'fs-extra';
|
||||
import { resolve, basename, parse, join } 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';
|
||||
@@ -112,11 +113,10 @@ export default async ctx => {
|
||||
}
|
||||
}
|
||||
|
||||
const file = highlight('vercel.json');
|
||||
const prop = code('version');
|
||||
|
||||
if (localConfig) {
|
||||
const { version } = localConfig;
|
||||
const file = highlight(localConfig[fileNameSymbol]);
|
||||
const prop = code('version');
|
||||
|
||||
if (version) {
|
||||
if (typeof version === 'number') {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { join } from 'path';
|
||||
import { write as copy } from 'clipboardy';
|
||||
import chalk from 'chalk';
|
||||
import title from 'title';
|
||||
import { fileNameSymbol } from '@vercel/client';
|
||||
import Client from '../../util/client';
|
||||
import { handleError } from '../../util/error';
|
||||
import getArgs from '../../util/get-args';
|
||||
@@ -54,7 +55,7 @@ import validatePaths, {
|
||||
validateRootDirectory,
|
||||
} from '../../util/validate-paths';
|
||||
import { readLocalConfig } from '../../util/config/files';
|
||||
import { getPkgName } from '../../util/pkg-name.ts';
|
||||
import { getCommandName } from '../../util/pkg-name.ts';
|
||||
|
||||
const addProcessEnv = async (log, env) => {
|
||||
let val;
|
||||
@@ -122,7 +123,10 @@ const printDeploymentStatus = async (
|
||||
// but fallback to the first alias in the list
|
||||
const mainAlias =
|
||||
aliasList.find(
|
||||
alias => !alias.endsWith('.now.sh') && !isWildcardAlias(alias)
|
||||
alias =>
|
||||
!alias.endsWith('.now.sh') &&
|
||||
!alias.endsWith('.vercel.app') &&
|
||||
!isWildcardAlias(alias)
|
||||
) || aliasList[0];
|
||||
|
||||
isWildcard = isWildcardAlias(mainAlias);
|
||||
@@ -370,7 +374,7 @@ export default async function main(
|
||||
output.print(
|
||||
`${prependEmoji(
|
||||
`The ${highlight(
|
||||
'vercel.json'
|
||||
localConfig[fileNameSymbol]
|
||||
)} file should be inside of the provided root directory.`,
|
||||
emoji('warning')
|
||||
)}\n`
|
||||
@@ -384,7 +388,7 @@ export default async function main(
|
||||
output.print(
|
||||
`${prependEmoji(
|
||||
`The ${code('name')} property in ${highlight(
|
||||
'vercel.json'
|
||||
localConfig[fileNameSymbol]
|
||||
)} is deprecated (https://zeit.ink/5F)`,
|
||||
emoji('warning')
|
||||
)}\n`
|
||||
@@ -401,7 +405,7 @@ export default async function main(
|
||||
if (typeof localConfig.env !== 'undefined' && !isObject(localConfig.env)) {
|
||||
error(
|
||||
`The ${code('env')} property in ${highlight(
|
||||
'vercel.json'
|
||||
localConfig[fileNameSymbol]
|
||||
)} needs to be an object`
|
||||
);
|
||||
return 1;
|
||||
@@ -411,7 +415,7 @@ export default async function main(
|
||||
if (!isObject(localConfig.build)) {
|
||||
error(
|
||||
`The ${code('build')} property in ${highlight(
|
||||
'vercel.json'
|
||||
localConfig[fileNameSymbol]
|
||||
)} needs to be an object`
|
||||
);
|
||||
return 1;
|
||||
@@ -423,7 +427,7 @@ export default async function main(
|
||||
) {
|
||||
error(
|
||||
`The ${code('build.env')} property in ${highlight(
|
||||
'vercel.json'
|
||||
localConfig[fileNameSymbol]
|
||||
)} needs to be an object`
|
||||
);
|
||||
return 1;
|
||||
@@ -634,11 +638,11 @@ export default async function main(
|
||||
}
|
||||
|
||||
if (purchase === false || purchase instanceof UserAborted) {
|
||||
handleCreateDeployError(output, deployment);
|
||||
handleCreateDeployError(output, deployment, localConfig);
|
||||
return 1;
|
||||
}
|
||||
|
||||
handleCreateDeployError(output, purchase);
|
||||
handleCreateDeployError(output, purchase, localConfig);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -658,15 +662,15 @@ export default async function main(
|
||||
err instanceof ConflictingFilePath ||
|
||||
err instanceof ConflictingPathSegment
|
||||
) {
|
||||
handleCreateDeployError(output, err);
|
||||
handleCreateDeployError(output, err, localConfig);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (err instanceof BuildError) {
|
||||
output.error('Build failed');
|
||||
output.error(
|
||||
`Check your logs at https://${now.url}/_logs or run ${code(
|
||||
`${getPkgName()} logs ${now.url}`,
|
||||
`Check your logs at https://${now.url}/_logs or run ${getCommandName(
|
||||
`logs ${now.url}`,
|
||||
{
|
||||
// Backticks are interpreted as part of the URL, causing CMD+Click
|
||||
// behavior to fail in editors like VSCode.
|
||||
@@ -704,7 +708,7 @@ export default async function main(
|
||||
);
|
||||
}
|
||||
|
||||
function handleCreateDeployError(output, error) {
|
||||
function handleCreateDeployError(output, error, localConfig) {
|
||||
if (error instanceof InvalidDomain) {
|
||||
output.error(`The domain ${error.meta.domain} is not valid`);
|
||||
return 1;
|
||||
@@ -733,7 +737,7 @@ function handleCreateDeployError(output, error) {
|
||||
|
||||
output.error(
|
||||
`The property ${code(prop)} is not allowed in ${highlight(
|
||||
'vercel.json'
|
||||
localConfig[fileNameSymbol]
|
||||
)} – please remove it.`
|
||||
);
|
||||
|
||||
@@ -758,7 +762,7 @@ function handleCreateDeployError(output, error) {
|
||||
|
||||
output.error(
|
||||
`The property ${code(prop)} in ${highlight(
|
||||
'vercel.json'
|
||||
localConfig[fileNameSymbol]
|
||||
)} can only be of type ${code(title(params.type))}.`
|
||||
);
|
||||
|
||||
@@ -769,7 +773,7 @@ function handleCreateDeployError(output, error) {
|
||||
|
||||
output.error(
|
||||
`Failed to validate ${highlight(
|
||||
'vercel.json'
|
||||
localConfig[fileNameSymbol]
|
||||
)}: ${message}\nDocumentation: ${link}`
|
||||
);
|
||||
|
||||
@@ -797,7 +801,7 @@ function handleCreateDeployError(output, error) {
|
||||
if (error instanceof BuildsRateLimited) {
|
||||
output.error(error.message);
|
||||
output.note(
|
||||
`Run ${code(`${getPkgName()} upgrade`)} to increase your builds limit.`
|
||||
`Run ${getCommandName('upgrade')} to increase your builds limit.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import link from '../../util/output/link';
|
||||
import exit from '../../util/exit';
|
||||
// @ts-ignore
|
||||
import Now from '../../util';
|
||||
// @ts-ignore
|
||||
import NowSecrets from '../../util/secrets';
|
||||
import uniq from '../../util/unique-strings';
|
||||
import promptBool from '../../util/input/prompt-bool';
|
||||
// @ts-ignore
|
||||
@@ -633,20 +635,7 @@ async function sync({
|
||||
nowConfig.build.env = deploymentBuildEnv;
|
||||
}
|
||||
|
||||
const hasSecrets = Object.keys(deploymentEnv).some(key =>
|
||||
(deploymentEnv[key] || '').startsWith('@')
|
||||
);
|
||||
|
||||
const secretsPromise = hasSecrets ? now.listSecrets() : null;
|
||||
|
||||
const findSecret = async (uidOrName: string) => {
|
||||
const secrets = await Promise.resolve(secretsPromise);
|
||||
|
||||
return secrets.filter(
|
||||
(secret: { name: string; uid: string }) =>
|
||||
secret.name === uidOrName || secret.uid === uidOrName
|
||||
);
|
||||
};
|
||||
const nowSecrets = new NowSecrets({ apiUrl, token, debug, currentTeam });
|
||||
|
||||
const env_ = await Promise.all(
|
||||
Object.keys(deploymentEnv).map(async (key: string) => {
|
||||
@@ -673,36 +662,17 @@ async function sync({
|
||||
|
||||
if (val[0] === '@') {
|
||||
const uidOrName = val.substr(1);
|
||||
const _secrets = await findSecret(uidOrName);
|
||||
const secret = await nowSecrets.getSecretByNameOrId(uidOrName);
|
||||
|
||||
if (_secrets.length === 0) {
|
||||
if (uidOrName === '') {
|
||||
error(
|
||||
`Empty reference provided for env key ${chalk.bold(
|
||||
`"${chalk.bold(key)}"`
|
||||
)}`
|
||||
);
|
||||
} else {
|
||||
error(
|
||||
`No secret found by uid or name ${chalk.bold(
|
||||
`"${uidOrName}"`
|
||||
)}`,
|
||||
'env-no-secret'
|
||||
);
|
||||
}
|
||||
|
||||
await exit(1);
|
||||
} else if (_secrets.length > 1) {
|
||||
if (!secret) {
|
||||
error(
|
||||
`Ambiguous secret ${chalk.bold(
|
||||
`"${uidOrName}"`
|
||||
)} (matches ${chalk.bold(_secrets.length)} secrets)`
|
||||
`No secret found by uid or name ${chalk.bold(`"${uidOrName}"`)}`,
|
||||
'env-no-secret'
|
||||
);
|
||||
|
||||
await exit(1);
|
||||
}
|
||||
|
||||
val = { uid: _secrets[0].uid };
|
||||
val = { uid: secret.uid };
|
||||
}
|
||||
|
||||
return [key, typeof val === 'string' ? val.replace(/^\\@/, '@') : val];
|
||||
|
||||
@@ -43,7 +43,7 @@ export default async function dev(
|
||||
return link.exitCode;
|
||||
}
|
||||
|
||||
if (link.status === 'not_linked' && !process.env.__NOW_SKIP_DEV_COMMAND) {
|
||||
if (link.status === 'not_linked' && !process.env.__VERCEL_SKIP_DEV_CMD) {
|
||||
output.error(
|
||||
`Your codebase isn’t linked to a project on Vercel. Run ${getCommandName()} to link it.`
|
||||
);
|
||||
@@ -84,6 +84,7 @@ export default async function dev(
|
||||
});
|
||||
|
||||
process.once('SIGINT', () => devServer.stop());
|
||||
process.once('SIGTERM', () => devServer.stop());
|
||||
|
||||
await devServer.start(...listen);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import cmd from '../../util/output/cmd';
|
||||
import dev from './dev';
|
||||
import readPackage from '../../util/read-package';
|
||||
import readConfig from '../../util/config/read-config';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getPkgName, getCommandName } from '../../util/pkg-name';
|
||||
|
||||
const COMMAND_CONFIG = {
|
||||
dev: ['dev'],
|
||||
@@ -110,9 +110,7 @@ export default async function main(ctx: NowContext) {
|
||||
}
|
||||
|
||||
if (argv._.length > 2) {
|
||||
output.error(
|
||||
`${cmd(`${getPkgName()} dev [dir]`)} accepts at most one argument`
|
||||
);
|
||||
output.error(`${getCommandName(`dev [dir]`)} accepts at most one argument`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import getScope from '../../util/get-scope';
|
||||
import parseAddDNSRecordArgs from '../../util/dns/parse-add-dns-record-args';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import getDNSData from '../../util/dns/get-dns-data';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -50,7 +50,7 @@ export default async function add(
|
||||
if (!parsedParams) {
|
||||
output.error(
|
||||
`Invalid number of arguments. See: ${chalk.cyan(
|
||||
`${getPkgName()} dns --help`
|
||||
`${getCommandName('dns --help')}`
|
||||
)} for usage.`
|
||||
);
|
||||
return 1;
|
||||
|
||||
@@ -6,7 +6,7 @@ import getScope from '../../util/get-scope';
|
||||
import { DomainNotFound, InvalidDomain } from '../../util/errors-ts';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import importZonefile from '../../util/dns/import-zonefile';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -42,7 +42,7 @@ export default async function add(
|
||||
if (args.length !== 2) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} dns import <domain> <zonefile>`
|
||||
`${getCommandName('dns import <domain> <zonefile>')}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
|
||||
@@ -85,8 +85,8 @@ const help = () => {
|
||||
'`1584722256178`'
|
||||
)} is the time in milliseconds since the UNIX epoch.
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} dns ls --next 1584722256178`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} dns ls zeit.rocks --next 1584722256178`)}
|
||||
|
||||
`);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import plural from 'pluralize';
|
||||
import { Output } from '../../util/output';
|
||||
import { DomainNotFound } from '../../util/errors-ts';
|
||||
import { ThenArg, DNSRecord, NowContext } from '../../types';
|
||||
import { DNSRecord, NowContext } from '../../types';
|
||||
import Client from '../../util/client';
|
||||
import formatTable from '../../util/format-table';
|
||||
import getDNSRecords from '../../util/dns/get-dns-records';
|
||||
import getDNSRecords, {
|
||||
DomainRecordsItem,
|
||||
} from '../../util/dns/get-dns-records';
|
||||
import getDomainDNSRecords from '../../util/dns/get-domain-dns-records';
|
||||
import getScope from '../../util/get-scope';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import getCommandFlags from '../../util/get-command-flags';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
type DNSRecords = ThenArg<ReturnType<typeof getDNSRecords>>;
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -54,7 +52,7 @@ export default async function ls(
|
||||
if (args.length > 1) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} dns ls [domain]`
|
||||
`${getCommandName('dns ls [domain]')}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
@@ -94,10 +92,8 @@ export default async function ls(
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(opts, ['_', '--next']);
|
||||
output.log(
|
||||
`To display the next page run ${cmd(
|
||||
`${getPkgName()} dns ls ${domainName}${flags} --next ${
|
||||
pagination.next
|
||||
}`
|
||||
`To display the next page run ${getCommandName(
|
||||
`dns ls ${domainName}${flags} --next ${pagination.next}`
|
||||
)}`
|
||||
);
|
||||
}
|
||||
@@ -105,18 +101,31 @@ export default async function ls(
|
||||
return 0;
|
||||
}
|
||||
|
||||
const dnsRecords = await getDNSRecords(output, client, contextName);
|
||||
const { records: dnsRecords, pagination } = await getDNSRecords(
|
||||
output,
|
||||
client,
|
||||
contextName,
|
||||
nextTimestamp
|
||||
);
|
||||
const nRecords = dnsRecords.reduce((p, r) => r.records.length + p, 0);
|
||||
output.log(
|
||||
`${plural('Record', nRecords, true)} found under ${chalk.bold(
|
||||
`${nRecords > 0 ? 'Records' : 'No records'} found under ${chalk.bold(
|
||||
contextName
|
||||
)} ${chalk.gray(lsStamp())}`
|
||||
);
|
||||
console.log(getDNSRecordsTable(dnsRecords));
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(opts, ['_', '--next']);
|
||||
output.log(
|
||||
`To display the next page run ${getCommandName(
|
||||
`dns ls${flags} --next ${pagination.next}`
|
||||
)}`
|
||||
);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getDNSRecordsTable(dnsRecords: DNSRecords) {
|
||||
function getDNSRecordsTable(dnsRecords: DomainRecordsItem[]) {
|
||||
return formatTable(
|
||||
['', 'id', 'name', 'type', 'value', 'created'],
|
||||
['l', 'r', 'l', 'l', 'l', 'l'],
|
||||
@@ -130,7 +139,7 @@ function getDNSRecordsTable(dnsRecords: DNSRecords) {
|
||||
function getDNSRecordRow(record: DNSRecord) {
|
||||
const isSystemRecord = record.creator === 'system';
|
||||
const createdAt = `${ms(
|
||||
Date.now() - new Date(Number(record.created)).getTime()
|
||||
Date.now() - new Date(Number(record.createdAt)).getTime()
|
||||
)} ago`;
|
||||
const priority = record.mxPriority || record.priority || null;
|
||||
return [
|
||||
|
||||
@@ -8,7 +8,7 @@ import deleteDNSRecordById from '../../util/dns/delete-dns-record-by-id';
|
||||
import getDNSRecordById from '../../util/dns/get-dns-record-by-id';
|
||||
import getScope from '../../util/get-scope';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -28,10 +28,9 @@ export default async function rm(
|
||||
const { apiUrl } = ctx;
|
||||
const debug = opts['--debug'];
|
||||
const client = new Client({ apiUrl, token, currentTeam, debug });
|
||||
let contextName = null;
|
||||
|
||||
try {
|
||||
({ contextName } = await getScope(client));
|
||||
await getScope(client);
|
||||
} catch (err) {
|
||||
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
|
||||
output.error(err.message);
|
||||
@@ -45,25 +44,20 @@ export default async function rm(
|
||||
if (args.length !== 1) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} dns rm <id>`
|
||||
`${getCommandName('dns rm <id>')}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const domainRecord = await getDNSRecordById(
|
||||
output,
|
||||
client,
|
||||
contextName,
|
||||
recordId
|
||||
);
|
||||
const record = await getDNSRecordById(client, recordId);
|
||||
|
||||
if (!domainRecord) {
|
||||
if (!record) {
|
||||
output.error('DNS record not found');
|
||||
return 1;
|
||||
}
|
||||
|
||||
const { domainName, record } = domainRecord;
|
||||
const { domain: domainName } = record;
|
||||
const yes = await readConfirmation(
|
||||
output,
|
||||
'The following record will be removed permanently',
|
||||
@@ -127,7 +121,7 @@ function getDeleteTableRow(domainName: string, record: DNSRecord) {
|
||||
`${recordName} ${record.type} ${record.value} ${record.mxPriority || ''}`
|
||||
),
|
||||
chalk.gray(
|
||||
`${ms(Date.now() - new Date(Number(record.created)).getTime())} ago`
|
||||
`${ms(Date.now() - new Date(Number(record.createdAt)).getTime())} ago`
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import formatNSTable from '../../util/format-ns-table';
|
||||
import getScope from '../../util/get-scope';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import param from '../../util/output/param';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName, getTitleName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--cdn': boolean;
|
||||
@@ -48,13 +48,13 @@ export default async function add(
|
||||
}
|
||||
|
||||
if (opts['--cdn'] !== undefined || opts['--no-cdn'] !== undefined) {
|
||||
output.error(`Toggling CF from ${getPkgName()} CLI is deprecated.`);
|
||||
output.error(`Toggling CF from ${getTitleName()} CLI is deprecated.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (args.length !== 1) {
|
||||
output.error(
|
||||
`${cmd(`${getPkgName()} domains add <domain>`)} expects one argument`
|
||||
`${getCommandName('domains add <domain>')} expects one argument`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
@@ -76,7 +76,7 @@ export default async function add(
|
||||
output.error(
|
||||
`You are adding '${domainName}' as a domain name containing a subdomain part '${subdomain}'\n` +
|
||||
` This feature is deprecated, please add just the root domain: ${chalk.cyan(
|
||||
`${getPkgName()} domain add ${domain}`
|
||||
`${getCommandName(`domain add ${domain}`)}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
@@ -142,7 +142,7 @@ export default async function add(
|
||||
);
|
||||
output.print(
|
||||
` If you want to force running a verification, you can run ${cmd(
|
||||
`${getPkgName()} domains verify <domain>`
|
||||
`${getCommandName('domains verify <domain>')}`
|
||||
)}\n`
|
||||
);
|
||||
output.print(' Read more: https://err.sh/now/domain-verification\n\n');
|
||||
|
||||
@@ -5,7 +5,6 @@ import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import Client from '../../util/client';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import getDomainPrice from '../../util/domains/get-domain-price';
|
||||
import getDomainStatus from '../../util/domains/get-domain-status';
|
||||
import getScope from '../../util/get-scope';
|
||||
@@ -13,7 +12,7 @@ import param from '../../util/output/param';
|
||||
import promptBool from '../../util/input/prompt-bool';
|
||||
import purchaseDomain from '../../util/domains/purchase-domain';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -49,7 +48,7 @@ export default async function buy(
|
||||
const [domainName] = args;
|
||||
if (!domainName) {
|
||||
output.error(
|
||||
`Missing domain name. Run ${cmd(`${getPkgName()} domains --help`)}`
|
||||
`Missing domain name. Run ${getCommandName(`domains --help`)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
@@ -63,8 +62,8 @@ export default async function buy(
|
||||
const { domain: rootDomain, subdomain } = parsedDomain;
|
||||
if (subdomain || !rootDomain) {
|
||||
output.error(
|
||||
`Invalid domain name "${domainName}". Run ${cmd(
|
||||
`${getPkgName()} domains --help`
|
||||
`Invalid domain name "${domainName}". Run ${getCommandName(
|
||||
`domains --help`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
@@ -121,8 +120,8 @@ export default async function buy(
|
||||
|
||||
if (buyResult instanceof ERRORS.SourceNotFound) {
|
||||
output.error(
|
||||
`Could not purchase domain. Please add a payment method using ${cmd(
|
||||
`${getPkgName()} billing add`
|
||||
`Could not purchase domain. Please add a payment method using ${getCommandName(
|
||||
`billing add`
|
||||
)}.`
|
||||
);
|
||||
return 1;
|
||||
@@ -189,8 +188,8 @@ export default async function buy(
|
||||
);
|
||||
} else {
|
||||
output.note(
|
||||
`You may now use your domain as an alias to your deployments. Run ${cmd(
|
||||
`${getPkgName()} alias --help`
|
||||
`You may now use your domain as an alias to your deployments. Run ${getCommandName(
|
||||
`alias --help`
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { DomainNotFound, DomainPermissionDenied } from '../../util/errors-ts';
|
||||
import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import Client from '../../util/client';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import dnsTable from '../../util/format-dns-table';
|
||||
import formatDate from '../../util/format-date';
|
||||
@@ -11,7 +10,7 @@ import formatNSTable from '../../util/format-ns-table';
|
||||
import getDomainByName from '../../util/domains/get-domain-by-name';
|
||||
import getScope from '../../util/get-scope';
|
||||
import getDomainPrice from '../../util/domains/get-domain-price';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -50,7 +49,7 @@ export default async function inspect(
|
||||
|
||||
if (!domainName) {
|
||||
output.error(
|
||||
`${cmd(`${getPkgName()} domains inspect <domain>`)} expects one argument`
|
||||
`${getCommandName(`domains inspect <domain>`)} expects one argument`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
@@ -58,7 +57,7 @@ export default async function inspect(
|
||||
if (args.length !== 1) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} domains inspect <domain>`
|
||||
`${getCommandName('domains inspect <domain>')}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
@@ -75,7 +74,7 @@ export default async function inspect(
|
||||
output.error(
|
||||
`Domain not found by "${domainName}" under ${chalk.bold(contextName)}`
|
||||
);
|
||||
output.log(`Run ${cmd(`${getPkgName()} domains ls`)} to see your domains.`);
|
||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -85,7 +84,7 @@ export default async function inspect(
|
||||
contextName
|
||||
)}`
|
||||
);
|
||||
output.log(`Run ${cmd(`${getPkgName()} domains ls`)} to see your domains.`);
|
||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -172,8 +171,8 @@ export default async function inspect(
|
||||
` We will run a verification for you and you will receive an email upon completion.\n`
|
||||
);
|
||||
output.print(
|
||||
` If you want to force running a verification, you can run ${cmd(
|
||||
`${getPkgName()} domains verify <domain>`
|
||||
` If you want to force running a verification, you can run ${getCommandName(
|
||||
`domains verify <domain>`
|
||||
)}\n`
|
||||
);
|
||||
output.print(' Read more: https://err.sh/now/domain-verification\n\n');
|
||||
|
||||
@@ -10,8 +10,7 @@ import strlen from '../../util/strlen';
|
||||
import { Output } from '../../util/output';
|
||||
import { Domain, NowContext } from '../../types';
|
||||
import getCommandFlags from '../../util/get-command-flags';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -55,7 +54,7 @@ export default async function ls(
|
||||
if (args.length !== 0) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} domains ls`
|
||||
`${getCommandName('domains ls')}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
@@ -76,8 +75,8 @@ export default async function ls(
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(opts, ['_', '--next']);
|
||||
output.log(
|
||||
`To display the next page run ${cmd(
|
||||
`${getPkgName()} domains ls${flags} --next ${pagination.next}`
|
||||
`To display the next page run ${getCommandName(
|
||||
`domains ls${flags} --next ${pagination.next}`
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import { NowContext, User, Team } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import Client from '../../util/client';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import getScope from '../../util/get-scope';
|
||||
import withSpinner from '../../util/with-spinner';
|
||||
import moveOutDomain from '../../util/domains/move-out-domain';
|
||||
@@ -16,7 +15,7 @@ import getDomainAliases from '../../util/alias/get-domain-aliases';
|
||||
import getDomainByName from '../../util/domains/get-domain-by-name';
|
||||
import promptBool from '../../util/input/prompt-bool';
|
||||
import getTeams from '../../util/get-teams';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -54,8 +53,8 @@ export default async function move(
|
||||
const { domainName, destination } = await getArgs(args);
|
||||
if (!isRootDomain(domainName)) {
|
||||
output.error(
|
||||
`Invalid domain name "${domainName}". Run ${cmd(
|
||||
`${getPkgName()} domains --help`
|
||||
`Invalid domain name "${domainName}". Run ${getCommandName(
|
||||
`domains --help`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
@@ -64,7 +63,7 @@ export default async function move(
|
||||
const domain = await getDomainByName(client, contextName, domainName);
|
||||
if (domain instanceof ERRORS.DomainNotFound) {
|
||||
output.error(`Domain not found under ${chalk.bold(contextName)}`);
|
||||
output.log(`Run ${cmd(`${getPkgName()} domains ls`)} to see your domains.`);
|
||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
||||
return 1;
|
||||
}
|
||||
if (domain instanceof ERRORS.DomainPermissionDenied) {
|
||||
@@ -103,9 +102,7 @@ export default async function move(
|
||||
output.warn(
|
||||
`This domain's ${chalk.bold(
|
||||
plural('alias', aliases.length, true)
|
||||
)} will be removed. Run ${chalk.dim(
|
||||
`${getPkgName()} alias ls`
|
||||
)} to list them.`
|
||||
)} will be removed. Run ${getCommandName(`alias ls`)} to list them.`
|
||||
);
|
||||
if (
|
||||
!(await promptBool(
|
||||
@@ -145,7 +142,7 @@ export default async function move(
|
||||
}
|
||||
if (moveTokenResult instanceof ERRORS.DomainNotFound) {
|
||||
output.error(`Domain not found under ${chalk.bold(contextName)}`);
|
||||
output.log(`Run ${cmd(`${getPkgName()} domains ls`)} to see your domains.`);
|
||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
||||
return 1;
|
||||
}
|
||||
if (moveTokenResult instanceof ERRORS.DomainPermissionDenied) {
|
||||
|
||||
@@ -5,7 +5,6 @@ import { DomainNotFound, DomainPermissionDenied } from '../../util/errors-ts';
|
||||
import { NowContext, Domain } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import Client from '../../util/client';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import deleteCertById from '../../util/certs/delete-cert-by-id';
|
||||
import getDomainByName from '../../util/domains/get-domain-by-name';
|
||||
import getScope from '../../util/get-scope';
|
||||
@@ -16,7 +15,7 @@ import * as ERRORS from '../../util/errors-ts';
|
||||
import param from '../../util/output/param';
|
||||
import promptBool from '../../util/input/prompt-bool';
|
||||
import setCustomSuffix from '../../util/domains/set-custom-suffix';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -53,7 +52,7 @@ export default async function rm(
|
||||
|
||||
if (!domainName) {
|
||||
output.error(
|
||||
`${cmd(`${getPkgName()} domains rm <domain>`)} expects one argument`
|
||||
`${getCommandName(`domains rm <domain>`)} expects one argument`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
@@ -61,7 +60,7 @@ export default async function rm(
|
||||
if (args.length !== 1) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} domains rm <domain>`
|
||||
`${getCommandName('domains rm <domain>')}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
@@ -72,7 +71,7 @@ export default async function rm(
|
||||
output.error(
|
||||
`Domain not found by "${domainName}" under ${chalk.bold(contextName)}`
|
||||
);
|
||||
output.log(`Run ${cmd(`${getPkgName()} domains ls`)} to see your domains.`);
|
||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -82,7 +81,7 @@ export default async function rm(
|
||||
contextName
|
||||
)}`
|
||||
);
|
||||
output.log(`Run ${cmd(`${getPkgName()} domains ls`)} to see your domains.`);
|
||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -149,7 +148,7 @@ async function removeDomain(
|
||||
|
||||
if (removeResult instanceof ERRORS.DomainNotFound) {
|
||||
output.error(`Domain not found under ${chalk.bold(contextName)}`);
|
||||
output.log(`Run ${cmd(`${getPkgName()} domains ls`)} to see your domains.`);
|
||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -207,9 +206,7 @@ async function removeDomain(
|
||||
output.warn(
|
||||
`This domain's ${chalk.bold(
|
||||
plural('alias', aliases.length, true)
|
||||
)} will be removed. Run ${chalk.dim(
|
||||
`${getPkgName()} alias ls`
|
||||
)} to list them.`
|
||||
)} will be removed. Run ${getCommandName(`alias ls`)} to list them.`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -217,9 +214,7 @@ async function removeDomain(
|
||||
output.warn(
|
||||
`This domain's ${chalk.bold(
|
||||
plural('certificate', certs.length, true)
|
||||
)} will be removed. Run ${chalk.dim(
|
||||
`${getPkgName()} cert ls`
|
||||
)} to list them.`
|
||||
)} will be removed. Run ${getCommandName(`cert ls`)} to list them.`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import Client from '../../util/client';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import getScope from '../../util/get-scope';
|
||||
import param from '../../util/output/param';
|
||||
import transferInDomain from '../../util/domains/transfer-in-domain';
|
||||
@@ -15,7 +14,7 @@ import getDomainPrice from '../../util/domains/get-domain-price';
|
||||
import checkTransfer from '../../util/domains/check-transfer';
|
||||
import promptBool from '../../util/input/prompt-bool';
|
||||
import isRootDomain from '../../util/is-root-domain';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -52,15 +51,15 @@ export default async function transferIn(
|
||||
const [domainName] = args;
|
||||
if (!domainName) {
|
||||
output.error(
|
||||
`Missing domain name. Run ${cmd(`${getPkgName()} domains --help`)}`
|
||||
`Missing domain name. Run ${getCommandName(`domains --help`)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!isRootDomain(domainName)) {
|
||||
output.error(
|
||||
`Invalid domain name ${param(domainName)}. Run ${cmd(
|
||||
`${getPkgName()} domains --help`
|
||||
`Invalid domain name ${param(domainName)}. Run ${getCommandName(
|
||||
`domains --help`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
@@ -130,8 +129,8 @@ export default async function transferIn(
|
||||
|
||||
if (transferInResult instanceof ERRORS.SourceNotFound) {
|
||||
output.error(
|
||||
`Could not purchase domain. Please add a payment method using ${cmd(
|
||||
`${getPkgName()} billing add`
|
||||
`Could not purchase domain. Please add a payment method using ${getCommandName(
|
||||
`billing add`
|
||||
)}.`
|
||||
);
|
||||
return 1;
|
||||
@@ -161,7 +160,7 @@ export default async function transferIn(
|
||||
);
|
||||
output.print(` Then import it to Vercel DNS by using:\n`);
|
||||
output.print(
|
||||
` ${cmd(`${getPkgName()} dns import ${domainName} <zonefile>`)}\n\n`
|
||||
` ${getCommandName(`dns import ${domainName} <zonefile>`)}\n\n`
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3,14 +3,13 @@ import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import Client from '../../util/client';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import formatDnsTable from '../../util/format-dns-table';
|
||||
import formatNSTable from '../../util/format-ns-table';
|
||||
import getDomainByName from '../../util/domains/get-domain-by-name';
|
||||
import getScope from '../../util/get-scope';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import verifyDomain from '../../util/domains/verify-domain';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -48,7 +47,7 @@ export default async function verify(
|
||||
|
||||
if (!domainName) {
|
||||
output.error(
|
||||
`${cmd(`${getPkgName()} domains verify <domain>`)} expects one argument`
|
||||
`${getCommandName(`domains verify <domain>`)} expects one argument`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
@@ -56,7 +55,7 @@ export default async function verify(
|
||||
if (args.length !== 1) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} domains verify <domain>`
|
||||
`${getCommandName('domains verify <domain>')}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
@@ -67,7 +66,7 @@ export default async function verify(
|
||||
output.error(
|
||||
`Domain not found by "${domainName}" under ${chalk.bold(contextName)}`
|
||||
);
|
||||
output.log(`Run ${cmd(`${getPkgName()} domains ls`)} to see your domains.`);
|
||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -77,7 +76,7 @@ export default async function verify(
|
||||
contextName
|
||||
)}`
|
||||
);
|
||||
output.log(`Run ${cmd(`${getPkgName()} domains ls`)} to see your domains.`);
|
||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -114,8 +113,8 @@ export default async function verify(
|
||||
)}\n\n`
|
||||
);
|
||||
output.print(
|
||||
` Once your domain uses either the nameservers or the TXT DNS record from above, run again ${cmd(
|
||||
`${getPkgName()} domains verify <domain>`
|
||||
` Once your domain uses either the nameservers or the TXT DNS record from above, run again ${getCommandName(
|
||||
`domains verify <domain>`
|
||||
)}.\n`
|
||||
);
|
||||
output.print(
|
||||
@@ -140,8 +139,8 @@ export default async function verify(
|
||||
)} was verified using DNS TXT record. ${verifyStamp()}`
|
||||
);
|
||||
output.print(
|
||||
` You can verify with nameservers too. Run ${cmd(
|
||||
`${getPkgName()} domains inspect ${domain.name}`
|
||||
` You can verify with nameservers too. Run ${getCommandName(
|
||||
`domains inspect ${domain.name}`
|
||||
)} to find out the intended set.\n`
|
||||
);
|
||||
return 0;
|
||||
|
||||
21
packages/now-cli/src/commands/env/add.ts
vendored
21
packages/now-cli/src/commands/env/add.ts
vendored
@@ -12,12 +12,11 @@ import {
|
||||
getEnvTargetChoices,
|
||||
} from '../../util/env/env-target';
|
||||
import readStandardInput from '../../util/input/read-standard-input';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import param from '../../util/output/param';
|
||||
import withSpinner from '../../util/with-spinner';
|
||||
import { emoji, prependEmoji } from '../../util/emoji';
|
||||
import { isKnownError } from '../../util/env/known-error';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -35,8 +34,8 @@ export default async function add(
|
||||
|
||||
if (args.length > 2) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${cmd(
|
||||
`${getPkgName()} env add <name> ${getEnvTargetPlaceholder()}`
|
||||
`Invalid number of arguments. Usage: ${getCommandName(
|
||||
`env add <name> ${getEnvTargetPlaceholder()}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
@@ -44,8 +43,8 @@ export default async function add(
|
||||
|
||||
if (stdInput && (!envName || !envTarget)) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${cmd(
|
||||
`${getPkgName()} env add <name> <target> < <file>`
|
||||
`Invalid number of arguments. Usage: ${getCommandName(
|
||||
`env add <name> <target> < <file>`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
@@ -88,8 +87,8 @@ export default async function add(
|
||||
output.error(
|
||||
`The variable ${param(
|
||||
envName
|
||||
)} has already been added to all Environments. To remove, run ${cmd(
|
||||
`${getPkgName()} env rm ${envName}`
|
||||
)} has already been added to all Environments. To remove, run ${getCommandName(
|
||||
`env rm ${envName}`
|
||||
)}.`
|
||||
);
|
||||
return 1;
|
||||
@@ -99,6 +98,8 @@ export default async function add(
|
||||
|
||||
if (stdInput) {
|
||||
envValue = stdInput;
|
||||
} else if (isSystemEnvVariable(envName)) {
|
||||
envValue = '';
|
||||
} else {
|
||||
const { inputValue } = await inquirer.prompt({
|
||||
type: 'password',
|
||||
@@ -147,3 +148,7 @@ export default async function add(
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function isSystemEnvVariable(envName: string) {
|
||||
return envName.startsWith('VERCEL_');
|
||||
}
|
||||
|
||||
11
packages/now-cli/src/commands/env/ls.ts
vendored
11
packages/now-cli/src/commands/env/ls.ts
vendored
@@ -10,10 +10,9 @@ import {
|
||||
getEnvTargetPlaceholder,
|
||||
} from '../../util/env/env-target';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import param from '../../util/output/param';
|
||||
import getCommandFlags from '../../util/get-command-flags';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -31,8 +30,8 @@ export default async function ls(
|
||||
|
||||
if (args.length > 1) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${cmd(
|
||||
`${getPkgName()} env ls ${getEnvTargetPlaceholder()}`
|
||||
`Invalid number of arguments. Usage: ${getCommandName(
|
||||
`env ls ${getEnvTargetPlaceholder()}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
@@ -75,8 +74,8 @@ export default async function ls(
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(opts, ['_', '--next']);
|
||||
output.log(
|
||||
`To display the next page run ${cmd(
|
||||
`${getPkgName()} env ls${flags} --next ${pagination.next}`
|
||||
`To display the next page run ${getCommandName(
|
||||
`env ls${flags} --next ${pagination.next}`
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
39
packages/now-cli/src/commands/env/pull.ts
vendored
39
packages/now-cli/src/commands/env/pull.ts
vendored
@@ -6,13 +6,12 @@ import Client from '../../util/client';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import getEnvVariables from '../../util/env/get-env-records';
|
||||
import getDecryptedSecret from '../../util/env/get-decrypted-secret';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import param from '../../util/output/param';
|
||||
import withSpinner from '../../util/with-spinner';
|
||||
import { join } from 'path';
|
||||
import { promises, existsSync } from 'fs';
|
||||
import { emoji, prependEmoji } from '../../util/emoji';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
const { writeFile } = promises;
|
||||
|
||||
type Options = {
|
||||
@@ -29,9 +28,7 @@ export default async function pull(
|
||||
) {
|
||||
if (args.length > 1) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${cmd(
|
||||
`${getPkgName()} env pull <file>`
|
||||
)}`
|
||||
`Invalid number of arguments. Usage: ${getCommandName(`env pull <file>`)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
@@ -63,18 +60,40 @@ export default async function pull(
|
||||
const records = await withSpinner('Downloading', async () => {
|
||||
const dev = ProjectEnvTarget.Development;
|
||||
const envs = await getEnvVariables(output, client, project.id, 4, dev);
|
||||
const values = await Promise.all(
|
||||
envs.map(env => getDecryptedSecret(output, client, env.value))
|
||||
const decryptedValues = await Promise.all(
|
||||
envs.map(async env => {
|
||||
try {
|
||||
const value = await getDecryptedSecret(output, client, env.value);
|
||||
return { value, found: true };
|
||||
} catch (error) {
|
||||
if (error && error.status === 404) {
|
||||
return { value: '', found: false };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
})
|
||||
);
|
||||
const results: { key: string; value: string }[] = [];
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
results.push({ key: envs[i].key, value: values[i] });
|
||||
const results: { key: string; value: string; found: boolean }[] = [];
|
||||
for (let i = 0; i < decryptedValues.length; i++) {
|
||||
const { key } = envs[i];
|
||||
const { value, found } = decryptedValues[i];
|
||||
results.push({ key, value, found });
|
||||
}
|
||||
return results;
|
||||
});
|
||||
|
||||
const contents =
|
||||
records
|
||||
.filter(obj => {
|
||||
if (!obj.found) {
|
||||
output.print('');
|
||||
output.warn(
|
||||
`Unable to download variable ${obj.key} because associated secret was deleted`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map(({ key, value }) => `${key}="${escapeValue(value)}"`)
|
||||
.join('\n') + '\n';
|
||||
|
||||
|
||||
7
packages/now-cli/src/commands/env/rm.ts
vendored
7
packages/now-cli/src/commands/env/rm.ts
vendored
@@ -12,12 +12,11 @@ import {
|
||||
} from '../../util/env/env-target';
|
||||
import Client from '../../util/client';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import param from '../../util/output/param';
|
||||
import withSpinner from '../../util/with-spinner';
|
||||
import { emoji, prependEmoji } from '../../util/emoji';
|
||||
import { isKnownError } from '../../util/env/known-error';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -33,8 +32,8 @@ export default async function rm(
|
||||
) {
|
||||
if (args.length > 2) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${cmd(
|
||||
`${getPkgName()} env rm <name> ${getEnvTargetPlaceholder()}`
|
||||
`Invalid number of arguments. Usage: ${getCommandName(
|
||||
`env rm <name> ${getEnvTargetPlaceholder()}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
|
||||
@@ -15,7 +15,7 @@ import success from '../../util/output/success';
|
||||
import info from '../../util/output/info';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import didYouMean from '../../util/init/did-you-mean';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -153,11 +153,11 @@ async function extractExample(
|
||||
const folderRel = path.relative(process.cwd(), folder);
|
||||
const deployHint =
|
||||
folderRel === ''
|
||||
? listItem(`To deploy, run ${cmd(getPkgName())}.`)
|
||||
? listItem(`To deploy, run ${getCommandName()}.`)
|
||||
: listItem(
|
||||
`To deploy, ${cmd(`cd ${folderRel}`)} and run ${cmd(
|
||||
getPkgName()
|
||||
)}.`
|
||||
`To deploy, ${cmd(
|
||||
`cd ${folderRel}`
|
||||
)} and run ${getCommandName()}.`
|
||||
);
|
||||
console.log(success(`${successLog}\n${deployHint}`));
|
||||
return 0;
|
||||
@@ -207,8 +207,8 @@ function prepareFolder(cwd: string, folder: string, force?: boolean) {
|
||||
*/
|
||||
async function guess(exampleList: string[], name: string) {
|
||||
const GuessError = new Error(
|
||||
`No example found for ${chalk.bold(name)}, run ${cmd(
|
||||
`${getPkgName()} init`
|
||||
`No example found for ${chalk.bold(name)}, run ${getCommandName(
|
||||
`init`
|
||||
)} to see the list of available examples.`
|
||||
);
|
||||
|
||||
|
||||
@@ -4,7 +4,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 cmd from '../util/output/cmd.ts';
|
||||
import createOutput from '../util/output';
|
||||
import Now from '../util';
|
||||
import logo from '../util/output/logo';
|
||||
@@ -13,7 +12,7 @@ import { handleError } from '../util/error';
|
||||
import strlen from '../util/strlen.ts';
|
||||
import Client from '../util/client.ts';
|
||||
import getScope from '../util/get-scope.ts';
|
||||
import { getPkgName } from '../util/pkg-name.ts';
|
||||
import { getPkgName, getCommandName } from '../util/pkg-name.ts';
|
||||
|
||||
const STATIC = 'STATIC';
|
||||
|
||||
@@ -74,9 +73,7 @@ export default async function main(ctx) {
|
||||
id = argv._[1];
|
||||
|
||||
if (argv._.length !== 2) {
|
||||
error(
|
||||
`${cmd(`${getPkgName()} inspect <url>`)} expects exactly one argument`
|
||||
);
|
||||
error(`${getCommandName('inspect <url>')} expects exactly one argument`);
|
||||
help();
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import toHost from '../util/to-host';
|
||||
import parseMeta from '../util/parse-meta';
|
||||
import { isValidName } from '../util/is-valid-name';
|
||||
import getCommandFlags from '../util/get-command-flags';
|
||||
import { getPkgName } from '../util/pkg-name.ts';
|
||||
import { getPkgName, getCommandName } from '../util/pkg-name.ts';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
@@ -98,7 +98,7 @@ export default async function main(ctx) {
|
||||
});
|
||||
|
||||
if (argv._.length > 2) {
|
||||
error(`${cmd(`${getPkgName()} ls [app]`)} accepts at most one argument`);
|
||||
error(`${getCommandName('ls [app]')} accepts at most one argument`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -163,12 +163,14 @@ export default async function main(ctx) {
|
||||
|
||||
// Some people are using entire domains as app names, so
|
||||
// we need to account for this here
|
||||
if (app && toHost(app).endsWith('.now.sh')) {
|
||||
const asHost = app ? toHost(app) : '';
|
||||
if (asHost.endsWith('.now.sh') || asHost.endsWith('.vercel.app')) {
|
||||
note(
|
||||
`We suggest using \`${getPkgName()} inspect <deployment>\` for retrieving details about a single deployment`
|
||||
`We suggest using ${getCommandName(
|
||||
'inspect <deployment>'
|
||||
)} for retrieving details about a single deployment`
|
||||
);
|
||||
|
||||
const asHost = toHost(app);
|
||||
const hostParts = asHost.split('-');
|
||||
|
||||
if (hostParts < 2) {
|
||||
@@ -234,9 +236,7 @@ export default async function main(ctx) {
|
||||
now.close();
|
||||
stopSpinner();
|
||||
log(`Found matching path alias: ${chalk.cyan(item.alias)}`);
|
||||
log(
|
||||
`Please run ${cmd(`${getPkgName()} alias ls ${item.alias}`)} instead`
|
||||
);
|
||||
log(`Please run ${getCommandName(`alias ls ${item.alias}`)} instead`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -287,13 +287,13 @@ export default async function main(ctx) {
|
||||
if (app == null) {
|
||||
log(
|
||||
`To list more deployments for a project run ${cmd(
|
||||
`${getPkgName()} ls [project]`
|
||||
`${getCommandName('ls [project]')}`
|
||||
)}`
|
||||
);
|
||||
} else if (!argv['--all']) {
|
||||
log(
|
||||
`To list deployment instances run ${cmd(
|
||||
`${getPkgName()} ls --all [project]`
|
||||
`${getCommandName('ls --all [project]')}`
|
||||
)}`
|
||||
);
|
||||
}
|
||||
@@ -348,10 +348,8 @@ export default async function main(ctx) {
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(argv, ['_', '--next']);
|
||||
log(
|
||||
`To display the next page run ${cmd(
|
||||
`${getPkgName()} ls${app ? ' ' + app : ''}${flags} --next ${
|
||||
pagination.next
|
||||
}`
|
||||
`To display the next page run ${getCommandName(
|
||||
`ls${app ? ' ' + app : ''}${flags} --next ${pagination.next}`
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import getArgs from '../util/get-args';
|
||||
import error from '../util/output/error';
|
||||
import highlight from '../util/output/highlight';
|
||||
import ok from '../util/output/ok';
|
||||
import cmd from '../util/output/cmd.ts';
|
||||
import param from '../util/output/param.ts';
|
||||
import eraseLines from '../util/output/erase-lines';
|
||||
import sleep from '../util/sleep';
|
||||
@@ -115,8 +114,8 @@ const readEmail = async () => {
|
||||
if (err.message === 'stdin lacks setRawMode support') {
|
||||
throw new Error(
|
||||
error(
|
||||
`Interactive mode not supported – please run ${cmd(
|
||||
`${getPkgName()} login you@domain.com`
|
||||
`Interactive mode not supported – please run ${getCommandName(
|
||||
`login you@domain.com`
|
||||
)}`
|
||||
)
|
||||
);
|
||||
|
||||
@@ -10,10 +10,9 @@ 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 cmd from '../util/output/cmd.ts';
|
||||
import wait from '../util/output/wait';
|
||||
import getPrefixedFlags from '../util/get-prefixed-flags';
|
||||
import { getPkgName } from '../util/pkg-name.ts';
|
||||
import { getPkgName, getCommandName } from '../util/pkg-name.ts';
|
||||
|
||||
const e = encodeURIComponent;
|
||||
|
||||
@@ -123,7 +122,7 @@ async function run({ client, contextName }) {
|
||||
console.error(
|
||||
error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} projects ls`
|
||||
`${getCommandName('projects ls')}`
|
||||
)}`
|
||||
)
|
||||
);
|
||||
@@ -184,10 +183,8 @@ async function run({ client, contextName }) {
|
||||
'-d',
|
||||
'-y',
|
||||
]);
|
||||
const nextCmd = `${getPkgName()} projects ls${flags} --next ${
|
||||
pagination.next
|
||||
}`;
|
||||
console.log(`To display the next page run ${cmd(nextCmd)}`);
|
||||
const nextCmd = `projects ls${flags} --next ${pagination.next}`;
|
||||
console.log(`To display the next page run ${getCommandName(nextCmd)}`);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -198,7 +195,7 @@ async function run({ client, contextName }) {
|
||||
console.error(
|
||||
error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} project rm <name>`
|
||||
`${getCommandName('project rm <name>')}`
|
||||
)}`
|
||||
)
|
||||
);
|
||||
@@ -240,14 +237,14 @@ async function run({ client, contextName }) {
|
||||
console.error(
|
||||
error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} projects add <name>`
|
||||
`${getCommandName('projects add <name>')}`
|
||||
)}`
|
||||
)
|
||||
);
|
||||
|
||||
if (args.length > 1) {
|
||||
const example = chalk.cyan(
|
||||
`$ ${getPkgName()} projects add "${args.join(' ')}"`
|
||||
`${getCommandName(`projects add "${args.join(' ')}"`)}`
|
||||
);
|
||||
console.log(
|
||||
`> If your project name has spaces, make sure to wrap it in quotes. Example: \n ${example} `
|
||||
|
||||
@@ -7,7 +7,6 @@ import Now from '../util';
|
||||
import getAliases from '../util/alias/get-aliases';
|
||||
import createOutput from '../util/output';
|
||||
import logo from '../util/output/logo';
|
||||
import cmd from '../util/output/cmd.ts';
|
||||
import elapsed from '../util/output/elapsed.ts';
|
||||
import { normalizeURL } from '../util/url';
|
||||
import Client from '../util/client.ts';
|
||||
@@ -18,7 +17,7 @@ import removeProject from '../util/projects/remove-project';
|
||||
import getProjectByIdOrName from '../util/projects/get-project-by-id-or-name';
|
||||
import getDeploymentByIdOrHost from '../util/deploy/get-deployment-by-id-or-host';
|
||||
import getDeploymentsByProjectId from '../util/deploy/get-deployments-by-project-id';
|
||||
import { getPkgName } from '../util/pkg-name.ts';
|
||||
import { getPkgName, getCommandName } from '../util/pkg-name.ts';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
@@ -94,7 +93,7 @@ export default async function main(ctx) {
|
||||
}
|
||||
|
||||
if (ids.length < 1) {
|
||||
error(`${cmd(`${getPkgName()} rm`)} expects at least one argument`);
|
||||
error(`${getCommandName('rm')} expects at least one argument`);
|
||||
help();
|
||||
return 1;
|
||||
}
|
||||
@@ -215,9 +214,9 @@ export default async function main(ctx) {
|
||||
log(
|
||||
`Could not find ${argv.safe ? 'unaliased' : 'any'} deployments ` +
|
||||
`or projects matching ` +
|
||||
`${ids.map(id => chalk.bold(`"${id}"`)).join(', ')}. Run ${cmd(
|
||||
`${getPkgName()} ls`
|
||||
)} to list.`
|
||||
`${ids
|
||||
.map(id => chalk.bold(`"${id}"`))
|
||||
.join(', ')}. Run ${getCommandName('ls')} to list.`
|
||||
);
|
||||
client.close();
|
||||
return 1;
|
||||
|
||||
@@ -12,9 +12,8 @@ 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 cmd from '../util/output/cmd.ts';
|
||||
import getPrefixedFlags from '../util/get-prefixed-flags';
|
||||
import { getPkgName } from '../util/pkg-name.ts';
|
||||
import { getPkgName, getCommandName } from '../util/pkg-name.ts';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
@@ -149,7 +148,7 @@ async function run({ output, token, contextName, currentTeam, ctx }) {
|
||||
console.error(
|
||||
error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} secret ls`
|
||||
`${getCommandName('secret ls')}`
|
||||
)}`
|
||||
)
|
||||
);
|
||||
@@ -197,10 +196,8 @@ async function run({ output, token, contextName, currentTeam, ctx }) {
|
||||
'-d',
|
||||
'-y',
|
||||
]);
|
||||
const nextCmd = `${getPkgName()} secrets ${subcommand}${flags} --next ${
|
||||
pagination.next
|
||||
}`;
|
||||
output.log(`To display the next page run ${cmd(nextCmd)}`);
|
||||
const nextCmd = `secrets ${subcommand}${flags} --next ${pagination.next}`;
|
||||
output.log(`To display the next page run ${getCommandName(nextCmd)}`);
|
||||
}
|
||||
return secrets.close();
|
||||
}
|
||||
@@ -210,7 +207,7 @@ async function run({ output, token, contextName, currentTeam, ctx }) {
|
||||
console.error(
|
||||
error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} secret rm <name>`
|
||||
`${getCommandName('secret rm <name>')}`
|
||||
)}`
|
||||
)
|
||||
);
|
||||
@@ -252,7 +249,7 @@ async function run({ output, token, contextName, currentTeam, ctx }) {
|
||||
console.error(
|
||||
error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} secret rename <old-name> <new-name>`
|
||||
`${getCommandName('secret rename <old-name> <new-name>')}`
|
||||
)}`
|
||||
)
|
||||
);
|
||||
@@ -275,14 +272,14 @@ async function run({ output, token, contextName, currentTeam, ctx }) {
|
||||
console.error(
|
||||
error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getPkgName()} secret add <name> <value>`
|
||||
`${getCommandName('secret add <name> <value>')}`
|
||||
)}`
|
||||
)
|
||||
);
|
||||
|
||||
if (args.length > 2) {
|
||||
const example = chalk.cyan(
|
||||
`$ ${getPkgName()} secret add -- "${args[0]}"`
|
||||
`$ ${getCommandName('secret add -- "${args[0]}"')}`
|
||||
);
|
||||
console.log(
|
||||
`If your secret has spaces or starts with '-', make sure to terminate command options with double dash and wrap it in quotes. Example: \n ${example} `
|
||||
@@ -307,7 +304,9 @@ async function run({ output, token, contextName, currentTeam, ctx }) {
|
||||
}
|
||||
|
||||
if (typeof value === 'boolean') {
|
||||
const example = chalk.cyan(`$ ${getPkgName()} secret add -- "${name}"`);
|
||||
const example = chalk.cyan(
|
||||
`$ ${getCommandName('secret add -- "${name}"')}`
|
||||
);
|
||||
console.log(
|
||||
`If your secret starts with '-', make sure to terminate command options with double dash and wrap it in quotes. Example: \n ${example} `
|
||||
);
|
||||
|
||||
@@ -32,6 +32,7 @@ const help = () => {
|
||||
'DIR'
|
||||
)} Path to the global ${'`.vercel`'} directory
|
||||
-d, --debug Debug mode [off]
|
||||
-N, --next Show next page of results
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
@@ -51,6 +52,12 @@ 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.
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} teams ls --next 1584722256178`)}
|
||||
`);
|
||||
};
|
||||
|
||||
@@ -66,6 +73,7 @@ const main = async ctx => {
|
||||
help: 'h',
|
||||
debug: 'd',
|
||||
switch: 'change',
|
||||
next: 'N',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -118,6 +126,7 @@ async function run({ token, config }) {
|
||||
config,
|
||||
apiUrl,
|
||||
token,
|
||||
argv,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -7,12 +7,11 @@ import rightPad from '../../util/output/right-pad';
|
||||
import eraseLines from '../../util/output/erase-lines';
|
||||
import chars from '../../util/output/chars';
|
||||
import success from '../../util/output/success';
|
||||
import cmd from '../../util/output/cmd.ts';
|
||||
import note from '../../util/output/note';
|
||||
import textInput from '../../util/input/text';
|
||||
import invite from './invite';
|
||||
import { writeToConfigFile } from '../../util/config/files';
|
||||
import { getPkgName } from '../../util/pkg-name.ts';
|
||||
import { getPkgName, getCommandName } from '../../util/pkg-name.ts';
|
||||
|
||||
const validateSlugKeypress = (data, value) =>
|
||||
// TODO: the `value` here should contain the current value + the keypress
|
||||
@@ -27,8 +26,8 @@ const validateNameKeypress = (data, value) =>
|
||||
const gracefulExit = () => {
|
||||
console.log(); // Blank line
|
||||
note(
|
||||
`Your team is now active for all ${getPkgName()} commands!\n Run ${cmd(
|
||||
`${getPkgName()} switch`
|
||||
`Your team is now active for all ${getPkgName()} commands!\n Run ${getCommandName(
|
||||
`switch`
|
||||
)} to change it in the future.`
|
||||
);
|
||||
return 0;
|
||||
@@ -152,8 +151,8 @@ export default async function({ apiUrl, token, teams, config }) {
|
||||
apiUrl,
|
||||
config,
|
||||
introMsg: 'Invite your teammates! When done, press enter on an empty field',
|
||||
noopMsg: `You can invite teammates later by running ${cmd(
|
||||
`${getPkgName()} teams invite`
|
||||
noopMsg: `You can invite teammates later by running ${getCommandName(
|
||||
`teams invite`
|
||||
)}`,
|
||||
});
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import eraseLines from '../../util/output/erase-lines';
|
||||
import success from '../../util/output/success';
|
||||
import getUser from '../../util/get-user.ts';
|
||||
import Client from '../../util/client.ts';
|
||||
import { getPkgName } from '../../util/pkg-name.ts';
|
||||
import { getCommandName } from '../../util/pkg-name.ts';
|
||||
|
||||
const validateEmail = data => regexEmail.test(data.trim()) || data.length === 0;
|
||||
|
||||
@@ -97,8 +97,8 @@ export default async function({
|
||||
// We specifically need a team scope here
|
||||
let err = `You can't run this command under ${param(
|
||||
user.username || user.email
|
||||
)}.\nPlease select a team scope using ${cmd(
|
||||
`${getPkgName()} switch`
|
||||
)}.\nPlease select a team scope using ${getCommandName(
|
||||
`switch`
|
||||
)} or use ${cmd('--scope')}`;
|
||||
return fatalError(err);
|
||||
}
|
||||
|
||||
@@ -8,12 +8,28 @@ 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';
|
||||
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 }) {
|
||||
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');
|
||||
return 1;
|
||||
}
|
||||
|
||||
export default async function({ teams, config, apiUrl, token }) {
|
||||
const stopSpinner = wait('Fetching teams');
|
||||
const list = (await teams.ls()).teams;
|
||||
const { teams: list, pagination } = await teams.ls({
|
||||
next,
|
||||
apiVersion: 2,
|
||||
});
|
||||
let { currentTeam } = config;
|
||||
const accountIsCurrent = !currentTeam;
|
||||
|
||||
@@ -78,4 +94,14 @@ export default async function({ teams, config, apiUrl, token }) {
|
||||
teamList.map(team => [team.current, team.value, team.name]),
|
||||
[1, 5]
|
||||
);
|
||||
|
||||
if (pagination && pagination.count === 20) {
|
||||
const prefixedArgs = getPrefixedFlags(argv);
|
||||
const flags = getCommandFlags(prefixedArgs, ['_', '--next', '-N', '-d']);
|
||||
const nextCmd = `${getPkgName()} teams ls${flags} --next ${
|
||||
pagination.next
|
||||
}`;
|
||||
console.log(); // empty line
|
||||
output.log(`To display the next page run ${cmd(nextCmd)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import getArgs from '../util/get-args';
|
||||
import { NowContext } from '../types';
|
||||
import createOutput from '../util/output';
|
||||
import getUpdateCommand from '../util/get-update-command';
|
||||
import { getPkgName } from '../util/pkg-name';
|
||||
import { getPkgName, getTitleName } from '../util/pkg-name';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
@@ -27,7 +27,7 @@ const help = () => {
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Update ${getPkgName()} CLI to the latest "canary" version
|
||||
${chalk.gray('–')} Update ${getTitleName()} CLI to the latest "canary" version
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} update --channel=canary`)}
|
||||
`);
|
||||
@@ -58,7 +58,9 @@ export default async function main(ctx: NowContext): Promise<number> {
|
||||
const debugEnabled = argv['--debug'];
|
||||
const output = createOutput({ debug: debugEnabled });
|
||||
output.log(
|
||||
`Please run ${cmd(await getUpdateCommand())} to update ${getPkgName()} CLI`
|
||||
`Please run ${cmd(
|
||||
await getUpdateCommand()
|
||||
)} to update ${getTitleName()} CLI`
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ import { NowError } from './util/now-error';
|
||||
import { SENTRY_DSN } from './util/constants.ts';
|
||||
import getUpdateCommand from './util/get-update-command';
|
||||
import { metrics, shouldCollectMetrics } from './util/metrics.ts';
|
||||
import { getPkgName } from './util/pkg-name.ts';
|
||||
import { getCommandName, getTitleName } from './util/pkg-name.ts';
|
||||
|
||||
const VERCEL_DIR = getGlobalPathConfig();
|
||||
const VERCEL_CONFIG_PATH = configFiles.getConfigFilePath();
|
||||
@@ -151,20 +151,20 @@ const main = async argv_ => {
|
||||
`${chalk.bgRed('UPDATE AVAILABLE')} ` +
|
||||
`Run ${cmd(
|
||||
await getUpdateCommand()
|
||||
)} to install ${getPkgName()} CLI ${update.latest}`
|
||||
)} to install ${getTitleName()} CLI ${update.latest}`
|
||||
)
|
||||
);
|
||||
|
||||
console.log(
|
||||
info(
|
||||
`Changelog: https://github.com/zeit/now/releases/tag/now@${update.latest}`
|
||||
`Changelog: https://github.com/zeit/now/releases/tag/vercel@${update.latest}`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
output.print(
|
||||
`${chalk.grey(
|
||||
`${getPkgName()} CLI ${pkg.version}${
|
||||
`${getTitleName()} CLI ${pkg.version}${
|
||||
targetOrSubcommand === 'dev' ? ' dev (beta)' : ''
|
||||
}${
|
||||
pkg.version.includes('canary') || targetOrSubcommand === 'dev'
|
||||
@@ -322,7 +322,9 @@ const main = async argv_ => {
|
||||
console.error(
|
||||
error(
|
||||
`The content of "${hp(VERCEL_AUTH_CONFIG_PATH)}" is invalid. ` +
|
||||
`No \`token\` property found inside. Run \`${getPkgName()} login\` to authorize.`
|
||||
`No \`token\` property found inside. Run ${getCommandName(
|
||||
'login'
|
||||
)} to authorize.`
|
||||
)
|
||||
);
|
||||
return 1;
|
||||
@@ -464,7 +466,7 @@ const main = async argv_ => {
|
||||
error({
|
||||
message:
|
||||
'No existing credentials found. Please run ' +
|
||||
`${param(`${getPkgName()} login`)} or pass ${param('--token')}`,
|
||||
`${getCommandName('login')} or pass ${param('--token')}`,
|
||||
slug: 'no-credentials-found',
|
||||
})
|
||||
);
|
||||
|
||||
@@ -2,8 +2,6 @@ import { NowConfig } from './util/dev/types';
|
||||
|
||||
export type ThenArg<T> = T extends Promise<infer U> ? U : T;
|
||||
|
||||
export type Config = NowConfig;
|
||||
|
||||
export interface NowContext {
|
||||
argv: string[];
|
||||
apiUrl: string;
|
||||
@@ -14,7 +12,7 @@ export interface NowContext {
|
||||
currentTeam: string;
|
||||
updateChannel: string;
|
||||
};
|
||||
localConfig: Config;
|
||||
localConfig: NowConfig;
|
||||
}
|
||||
|
||||
type Billing = {
|
||||
@@ -187,6 +185,9 @@ export type DNSRecord = {
|
||||
value: string;
|
||||
created: number;
|
||||
updated: number;
|
||||
createdAt: number;
|
||||
updatedAt: number;
|
||||
domain: string;
|
||||
};
|
||||
|
||||
type SRVRecordData = {
|
||||
|
||||
@@ -14,8 +14,6 @@ import setupDomain from '../domains/setup-domain';
|
||||
import stamp from '../output/stamp';
|
||||
import waitForScale from '../scale/wait-verify-deployment-scale';
|
||||
|
||||
const NOW_SH_REGEX = /\.now\.sh$/;
|
||||
|
||||
export default async function assignAlias(
|
||||
output: Output,
|
||||
client: Client,
|
||||
@@ -87,7 +85,11 @@ export default async function assignAlias(
|
||||
|
||||
// Check if the alias is a custom domain and if case we have a positive
|
||||
// we have to configure the DNS records and certificate
|
||||
if (alias.indexOf('.') !== -1 && !NOW_SH_REGEX.test(alias)) {
|
||||
if (
|
||||
alias.indexOf('.') !== -1 &&
|
||||
!alias.endsWith('.now.sh') &&
|
||||
!alias.endsWith('.vercel.app')
|
||||
) {
|
||||
// Now the domain shouldn't be available and it might or might not belong to the user
|
||||
const result = await setupDomain(output, client, alias, contextName);
|
||||
if (result instanceof Error) {
|
||||
|
||||
@@ -2,7 +2,8 @@ import path from 'path';
|
||||
import chalk from 'chalk';
|
||||
import Client from '../client';
|
||||
import { Output } from '../output';
|
||||
import { User, Config } from '../../types';
|
||||
import { User } from '../../types';
|
||||
import { NowConfig } from '../dev/types';
|
||||
import getDeploymentsByAppName from '../deploy/get-deployments-by-appname';
|
||||
import getDeploymentByIdOrHost from '../deploy/get-deployment-by-id-or-host';
|
||||
|
||||
@@ -34,7 +35,7 @@ export async function getDeploymentForAlias(
|
||||
localConfigPath: string | undefined,
|
||||
user: User,
|
||||
contextName: string,
|
||||
localConfig: Config
|
||||
localConfig: NowConfig
|
||||
) {
|
||||
const cancelWait = output.spinner(
|
||||
`Fetching deployment to alias in ${chalk.bold(contextName)}`
|
||||
|
||||
@@ -5,7 +5,8 @@ import getAppName from '../deploy/get-app-name';
|
||||
import fetchDeploymentByIdOrHost from '../deploy/get-deployment-by-id-or-host';
|
||||
import Client from '../client';
|
||||
import { Output } from '../output';
|
||||
import { User, Config } from '../../types';
|
||||
import { User } from '../../types';
|
||||
import { NowConfig } from '../dev/types';
|
||||
|
||||
export default async function getDeploymentForAlias(
|
||||
client: Client,
|
||||
@@ -14,7 +15,7 @@ export default async function getDeploymentForAlias(
|
||||
localConfigPath: string | undefined,
|
||||
user: User,
|
||||
contextName: string,
|
||||
localConfig: Config
|
||||
localConfig: NowConfig
|
||||
) {
|
||||
const cancelWait = output.spinner(
|
||||
`Fetching deployment to alias in ${chalk.bold(contextName)}`
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import toHost from '../to-host';
|
||||
import { Config } from '../../types';
|
||||
import { NowConfig } from '../dev/types';
|
||||
import * as ERRORS from '../errors-ts';
|
||||
|
||||
export function getTargetsForAlias(args: string[], { alias }: Config) {
|
||||
export function getTargetsForAlias(args: string[], { alias }: NowConfig) {
|
||||
if (args.length) {
|
||||
return targetsToHosts([args[args.length - 1]]);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import Client from '../client';
|
||||
import createCertForAlias from '../certs/create-cert-for-alias';
|
||||
import setupDomain from '../domains/setup-domain';
|
||||
|
||||
const NOW_SH_REGEX = /\.now\.sh$/;
|
||||
|
||||
type AliasRecord = {
|
||||
uid: string;
|
||||
alias: string;
|
||||
@@ -23,7 +21,7 @@ export default async function upsertPathAlias(
|
||||
) {
|
||||
let externalDomain = false;
|
||||
|
||||
if (!NOW_SH_REGEX.test(alias)) {
|
||||
if (!alias.endsWith('.now.sh') && !alias.endsWith('.vercel.app')) {
|
||||
const domainInfo = await setupDomain(output, client, alias, contextName);
|
||||
if (domainInfo instanceof Error) {
|
||||
return domainInfo;
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import { join as joinPath } from 'path';
|
||||
import { join, basename } from 'path';
|
||||
import loadJSON from 'load-json-file';
|
||||
import writeJSON from 'write-json-file';
|
||||
import { existsSync } from 'fs';
|
||||
import { fileNameSymbol } from '@vercel/client';
|
||||
import getGlobalPathConfig from './global-path';
|
||||
import getLocalPathConfig from './local-path';
|
||||
import { NowError } from '../now-error';
|
||||
import error from '../output/error';
|
||||
import highlight from '../output/highlight';
|
||||
import { NowConfig } from '../dev/types';
|
||||
|
||||
const VERCEL_DIR = getGlobalPathConfig();
|
||||
const CONFIG_FILE_PATH = joinPath(VERCEL_DIR, 'config.json');
|
||||
const AUTH_CONFIG_FILE_PATH = joinPath(VERCEL_DIR, 'auth.json');
|
||||
const CONFIG_FILE_PATH = join(VERCEL_DIR, 'config.json');
|
||||
const AUTH_CONFIG_FILE_PATH = join(VERCEL_DIR, 'auth.json');
|
||||
|
||||
// reads `CONFIG_FILE_PATH` atomically
|
||||
export const readConfigFile = () => loadJSON.sync(CONFIG_FILE_PATH);
|
||||
@@ -87,7 +89,10 @@ export function getAuthConfigFilePath() {
|
||||
return AUTH_CONFIG_FILE_PATH;
|
||||
}
|
||||
|
||||
export function readLocalConfig(prefix: string = process.cwd()) {
|
||||
export function readLocalConfig(
|
||||
prefix: string = process.cwd()
|
||||
): NowConfig | null {
|
||||
let config: NowConfig | null = null;
|
||||
let target = '';
|
||||
|
||||
try {
|
||||
@@ -105,29 +110,24 @@ export function readLocalConfig(prefix: string = process.cwd()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let localConfigExists;
|
||||
|
||||
try {
|
||||
localConfigExists = existsSync(target);
|
||||
if (existsSync(target)) {
|
||||
config = loadJSON.sync(target);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(error(`Config file does not exist: ${target}`));
|
||||
if (err.name === 'JSONError') {
|
||||
console.error(error(err.message));
|
||||
} else {
|
||||
const code = err.code ? ` (${err.code})` : '';
|
||||
console.error(error(`Failed to read config file: ${target}${code}`));
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (localConfigExists) {
|
||||
try {
|
||||
return loadJSON.sync(target);
|
||||
} catch (err) {
|
||||
if (err.name === 'JSONError') {
|
||||
console.log(error(err.message));
|
||||
} else {
|
||||
const code = err.code ? `(${err.code})` : '';
|
||||
console.error(error(`Failed to read config file: ${target} (${code})`));
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
if (!config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
config[fileNameSymbol] = basename(target);
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ const getGlobalPathConfig = (): string => {
|
||||
|
||||
const possibleConfigPaths = [
|
||||
...vercelDirectories, // latest vercel directory
|
||||
...XDGAppPaths('now').dataDirs(), // legacy now directory
|
||||
path.join(homedir(), '.now'), // legacy config in user's home directory.
|
||||
path.join(homedir(), '.now'), // legacy config in user's home directory
|
||||
...XDGAppPaths('now').dataDirs(), // legacy XDG directory
|
||||
];
|
||||
|
||||
// The customPath flag is the preferred location,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { join } from 'path';
|
||||
import { CantParseJSONFile } from '../errors-ts';
|
||||
import readJSONFile from '../read-json-file';
|
||||
import { Config } from '../../types';
|
||||
import { NowConfig } from '../dev/types';
|
||||
import getLocalConfigPath from './local-path';
|
||||
|
||||
export default async function readConfig(dir: string) {
|
||||
@@ -13,7 +13,7 @@ export default async function readConfig(dir: string) {
|
||||
}
|
||||
|
||||
if (result) {
|
||||
return result as Config;
|
||||
return result as NowConfig;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import path from 'path';
|
||||
import { NowError } from '../now-error';
|
||||
import { Output } from '../output';
|
||||
import { Config } from '../../types';
|
||||
import { NowConfig } from '../dev/types';
|
||||
import readPackage from '../read-package';
|
||||
|
||||
export default async function getAppName(
|
||||
output: Output,
|
||||
config: Config,
|
||||
config: NowConfig,
|
||||
localConfigPath?: string
|
||||
) {
|
||||
// If the name is in the configuration, return it
|
||||
|
||||
@@ -35,7 +35,7 @@ function printInspectUrl(
|
||||
const projectName = q.join('-');
|
||||
|
||||
const inspectUrl = `https://vercel.com/${orgSlug}/${projectName}/${deploymentShortId}${
|
||||
apex !== 'now.sh' ? `/${apex}` : ''
|
||||
apex !== 'now.sh' && apex !== 'vercel.app' ? `/${apex}` : ''
|
||||
}`;
|
||||
|
||||
output.print(
|
||||
@@ -109,7 +109,7 @@ export default async function processDeployment({
|
||||
let buildSpinner = null;
|
||||
let deploySpinner = null;
|
||||
|
||||
let deployingSpinner = output.spinner(
|
||||
const deployingSpinner = output.spinner(
|
||||
isSettingUpProject
|
||||
? 'Setting up project'
|
||||
: `Deploying ${chalk.bold(`${org.slug}/${projectName}`)}`,
|
||||
@@ -120,170 +120,176 @@ export default async function processDeployment({
|
||||
// the deployment is done
|
||||
const indications = [];
|
||||
|
||||
for await (const event of createDeployment(
|
||||
nowClientOptions,
|
||||
requestBody,
|
||||
nowConfig
|
||||
)) {
|
||||
if (event.type === 'hashes-calculated') {
|
||||
hashes = event.payload;
|
||||
}
|
||||
|
||||
if (['tip', 'notice', 'warning'].includes(event.type)) {
|
||||
indications.push(event);
|
||||
}
|
||||
|
||||
if (event.type === 'file-count') {
|
||||
debug(
|
||||
`Total files ${event.payload.total.size}, ${event.payload.missing.length} changed`
|
||||
);
|
||||
|
||||
const missingSize = event.payload.missing
|
||||
.map((sha: string) => event.payload.total.get(sha).data.length)
|
||||
.reduce((a: number, b: number) => a + b, 0);
|
||||
|
||||
if (queuedSpinner) {
|
||||
queuedSpinner();
|
||||
try {
|
||||
for await (const event of createDeployment(
|
||||
nowClientOptions,
|
||||
requestBody,
|
||||
nowConfig
|
||||
)) {
|
||||
if (event.type === 'hashes-calculated') {
|
||||
hashes = event.payload;
|
||||
}
|
||||
if (buildSpinner) {
|
||||
buildSpinner();
|
||||
|
||||
if (['tip', 'notice', 'warning'].includes(event.type)) {
|
||||
indications.push(event);
|
||||
}
|
||||
if (deploySpinner) {
|
||||
deploySpinner();
|
||||
}
|
||||
if (deployingSpinner) {
|
||||
|
||||
if (event.type === 'file-count') {
|
||||
debug(
|
||||
`Total files ${event.payload.total.size}, ${event.payload.missing.length} changed`
|
||||
);
|
||||
|
||||
const missingSize = event.payload.missing
|
||||
.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();
|
||||
bar = new Progress(`${chalk.gray('>')} Upload [:bar] :percent :etas`, {
|
||||
width: 20,
|
||||
complete: '=',
|
||||
incomplete: '',
|
||||
total: missingSize,
|
||||
clear: true,
|
||||
});
|
||||
}
|
||||
bar = new Progress(`${chalk.gray('>')} Upload [:bar] :percent :etas`, {
|
||||
width: 20,
|
||||
complete: '=',
|
||||
incomplete: '',
|
||||
total: missingSize,
|
||||
clear: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (event.type === 'file-uploaded') {
|
||||
debug(
|
||||
`Uploaded: ${event.payload.file.names.join(' ')} (${bytes(
|
||||
event.payload.file.data.length
|
||||
)})`
|
||||
);
|
||||
if (event.type === 'file-uploaded') {
|
||||
debug(
|
||||
`Uploaded: ${event.payload.file.names.join(' ')} (${bytes(
|
||||
event.payload.file.data.length
|
||||
)})`
|
||||
);
|
||||
|
||||
if (bar) {
|
||||
bar.tick(event.payload.file.data.length);
|
||||
if (bar) {
|
||||
bar.tick(event.payload.file.data.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event.type === 'created') {
|
||||
if (deployingSpinner) {
|
||||
if (event.type === 'created') {
|
||||
deployingSpinner();
|
||||
|
||||
now._host = event.payload.url;
|
||||
|
||||
await linkFolderToProject(
|
||||
output,
|
||||
cwd || paths[0],
|
||||
{
|
||||
orgId: org.id,
|
||||
projectId: event.payload.projectId,
|
||||
},
|
||||
projectName,
|
||||
org.slug
|
||||
);
|
||||
|
||||
now.url = event.payload.url;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
now._host = event.payload.url;
|
||||
if (event.type === 'building') {
|
||||
if (queuedSpinner) {
|
||||
queuedSpinner();
|
||||
}
|
||||
|
||||
await linkFolderToProject(
|
||||
output,
|
||||
cwd || paths[0],
|
||||
{
|
||||
orgId: org.id,
|
||||
projectId: event.payload.projectId,
|
||||
},
|
||||
projectName,
|
||||
org.slug
|
||||
);
|
||||
|
||||
now.url = event.payload.url;
|
||||
|
||||
printInspectUrl(output, event.payload.url, deployStamp, org.slug);
|
||||
|
||||
if (quiet) {
|
||||
process.stdout.write(`https://${event.payload.url}`);
|
||||
if (buildSpinner === null) {
|
||||
buildSpinner = output.spinner('Building', 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (queuedSpinner === null) {
|
||||
queuedSpinner =
|
||||
event.payload.readyState === 'QUEUED'
|
||||
? output.spinner('Queued', 0)
|
||||
: output.spinner('Building', 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.type === 'building') {
|
||||
if (queuedSpinner) {
|
||||
queuedSpinner();
|
||||
if (event.type === 'canceled') {
|
||||
if (queuedSpinner) {
|
||||
queuedSpinner();
|
||||
}
|
||||
if (buildSpinner) {
|
||||
buildSpinner();
|
||||
}
|
||||
return event.payload;
|
||||
}
|
||||
|
||||
if (buildSpinner === null) {
|
||||
buildSpinner = output.spinner('Building', 0);
|
||||
}
|
||||
}
|
||||
if (event.type === 'ready') {
|
||||
if (queuedSpinner) {
|
||||
queuedSpinner();
|
||||
}
|
||||
if (buildSpinner) {
|
||||
buildSpinner();
|
||||
}
|
||||
|
||||
if (event.type === 'canceled') {
|
||||
if (queuedSpinner) {
|
||||
queuedSpinner();
|
||||
}
|
||||
if (buildSpinner) {
|
||||
buildSpinner();
|
||||
}
|
||||
return event.payload;
|
||||
}
|
||||
|
||||
if (event.type === 'ready') {
|
||||
if (queuedSpinner) {
|
||||
queuedSpinner();
|
||||
}
|
||||
if (buildSpinner) {
|
||||
buildSpinner();
|
||||
deploySpinner = output.spinner('Completing', 0);
|
||||
}
|
||||
|
||||
deploySpinner = output.spinner('Completing', 0);
|
||||
}
|
||||
|
||||
// Handle error events
|
||||
if (event.type === 'error') {
|
||||
if (queuedSpinner) {
|
||||
queuedSpinner();
|
||||
}
|
||||
if (buildSpinner) {
|
||||
buildSpinner();
|
||||
}
|
||||
if (deploySpinner) {
|
||||
deploySpinner();
|
||||
}
|
||||
if (deployingSpinner) {
|
||||
// Handle error events
|
||||
if (event.type === 'error') {
|
||||
if (queuedSpinner) {
|
||||
queuedSpinner();
|
||||
}
|
||||
if (buildSpinner) {
|
||||
buildSpinner();
|
||||
}
|
||||
if (deploySpinner) {
|
||||
deploySpinner();
|
||||
}
|
||||
deployingSpinner();
|
||||
|
||||
const error = await now.handleDeploymentError(event.payload, {
|
||||
hashes,
|
||||
env,
|
||||
});
|
||||
|
||||
if (error.code === 'missing_project_settings') {
|
||||
return error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
const error = await now.handleDeploymentError(event.payload, {
|
||||
hashes,
|
||||
env,
|
||||
});
|
||||
|
||||
if (error.code === 'missing_project_settings') {
|
||||
return error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Handle alias-assigned event
|
||||
if (event.type === 'alias-assigned') {
|
||||
if (queuedSpinner) {
|
||||
queuedSpinner();
|
||||
}
|
||||
if (buildSpinner) {
|
||||
buildSpinner();
|
||||
}
|
||||
if (deploySpinner) {
|
||||
deploySpinner();
|
||||
}
|
||||
if (deployingSpinner) {
|
||||
// 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;
|
||||
event.payload.indications = indications;
|
||||
return event.payload;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (queuedSpinner) {
|
||||
queuedSpinner();
|
||||
}
|
||||
if (buildSpinner) {
|
||||
buildSpinner();
|
||||
}
|
||||
if (deploySpinner) {
|
||||
deploySpinner();
|
||||
}
|
||||
deployingSpinner();
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,13 @@
|
||||
import execa from 'execa';
|
||||
import semver from 'semver';
|
||||
import pipe from 'promisepipe';
|
||||
import retry from 'async-retry';
|
||||
import npa from 'npm-package-arg';
|
||||
import pluralize from 'pluralize';
|
||||
import { extract } from 'tar-fs';
|
||||
import { createHash } from 'crypto';
|
||||
import { createGunzip } from 'zlib';
|
||||
import { join } from 'path';
|
||||
import { basename, join } from 'path';
|
||||
import { PackageJson } from '@vercel/build-utils';
|
||||
import XDGAppPaths from 'xdg-app-paths';
|
||||
import {
|
||||
createReadStream,
|
||||
mkdirp,
|
||||
readFile,
|
||||
readJSON,
|
||||
writeFile,
|
||||
} from 'fs-extra';
|
||||
import pkg from '../../../package.json';
|
||||
import { mkdirp, readJSON, writeJSON } from 'fs-extra';
|
||||
import nowCliPkg from '../pkg';
|
||||
|
||||
import { NoBuilderCacheError } from '../errors-ts';
|
||||
import { Output } from '../output';
|
||||
@@ -34,6 +24,7 @@ const registryTypes = new Set(['version', 'tag', 'range']);
|
||||
const createStaticBuilder = (scope: string): BuilderWithPackage => {
|
||||
return {
|
||||
runInProcess: true,
|
||||
requirePath: `${scope}/static`,
|
||||
builder: Object.freeze(staticBuilder),
|
||||
package: Object.freeze({ name: `@${scope}/static`, version: '' }),
|
||||
};
|
||||
@@ -44,36 +35,10 @@ const localBuilders: { [key: string]: BuilderWithPackage } = {
|
||||
'@vercel/static': createStaticBuilder('vercel'),
|
||||
};
|
||||
|
||||
const distTag = getDistTag(pkg.version);
|
||||
const distTag = nowCliPkg.version ? getDistTag(nowCliPkg.version) : 'canary';
|
||||
|
||||
export const cacheDirPromise = prepareCacheDir();
|
||||
export const builderDirPromise = prepareBuilderDir();
|
||||
export const builderModulePathPromise = prepareBuilderModulePath();
|
||||
|
||||
function readFileOrNull(
|
||||
filePath: string,
|
||||
encoding?: null
|
||||
): Promise<Buffer | null>;
|
||||
function readFileOrNull(
|
||||
filePath: string,
|
||||
encoding: string
|
||||
): Promise<string | null>;
|
||||
async function readFileOrNull(
|
||||
filePath: string,
|
||||
encoding?: string | null
|
||||
): Promise<Buffer | string | null> {
|
||||
try {
|
||||
if (encoding) {
|
||||
return await readFile(filePath, encoding);
|
||||
}
|
||||
return await readFile(filePath);
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
return null;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare cache directory for installing now-builders
|
||||
@@ -94,51 +59,19 @@ export async function prepareBuilderDir() {
|
||||
const builderDir = join(await cacheDirPromise, 'builders');
|
||||
await mkdirp(builderDir);
|
||||
|
||||
// Extract the bundled `builders.tar.gz` file, if necessary
|
||||
const bundledTarballPath = join(__dirname, '../../../assets/builders.tar.gz');
|
||||
|
||||
const existingPackageJson =
|
||||
(await readFileOrNull(join(builderDir, 'package.json'), 'utf8')) || '{}';
|
||||
const { dependencies = {} } = JSON.parse(existingPackageJson);
|
||||
|
||||
if (!hasBundledBuilders(dependencies)) {
|
||||
const extractor = extract(builderDir);
|
||||
await pipe(
|
||||
createReadStream(bundledTarballPath),
|
||||
createGunzip(),
|
||||
extractor
|
||||
);
|
||||
// Create an empty `package.json` file, only if one does not already exist
|
||||
try {
|
||||
const buildersPkg = join(builderDir, 'package.json');
|
||||
await writeJSON(buildersPkg, { private: true }, { flag: 'wx' });
|
||||
} catch (err) {
|
||||
if (err.code !== 'EEXIST') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
return builderDir;
|
||||
}
|
||||
|
||||
export async function prepareBuilderModulePath() {
|
||||
const [builderDir, builderContents] = await Promise.all([
|
||||
builderDirPromise,
|
||||
readFile(join(__dirname, 'builder-worker.js')),
|
||||
]);
|
||||
let needsWrite = false;
|
||||
const builderSha = getSha(builderContents);
|
||||
const cachedBuilderPath = join(builderDir, 'builder.js');
|
||||
|
||||
const cachedBuilderContents = await readFileOrNull(cachedBuilderPath);
|
||||
if (cachedBuilderContents) {
|
||||
const cachedBuilderSha = getSha(cachedBuilderContents);
|
||||
if (builderSha !== cachedBuilderSha) {
|
||||
needsWrite = true;
|
||||
}
|
||||
} else {
|
||||
needsWrite = true;
|
||||
}
|
||||
|
||||
if (needsWrite) {
|
||||
await writeFile(cachedBuilderPath, builderContents);
|
||||
}
|
||||
|
||||
return cachedBuilderPath;
|
||||
}
|
||||
|
||||
function getNpmVersion(use = ''): string {
|
||||
const parsed = npa(use);
|
||||
if (registryTypes.has(parsed.type)) {
|
||||
@@ -168,12 +101,20 @@ function parseVersionSafe(rawSpec: string) {
|
||||
export function filterPackage(
|
||||
builderSpec: string,
|
||||
distTag: string,
|
||||
buildersPkg: PackageJson
|
||||
buildersPkg: PackageJson,
|
||||
nowCliPkg: PackageJson
|
||||
) {
|
||||
if (builderSpec in localBuilders) return false;
|
||||
const parsed = npa(builderSpec);
|
||||
const parsedVersion = parseVersionSafe(parsed.rawSpec);
|
||||
// skip install of already installed Runtime
|
||||
|
||||
// If it's a builder that is part of Now CLI's `dependencies` then
|
||||
// the builder is already installed into `node_modules`
|
||||
if (isBundledBuilder(parsed, nowCliPkg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip install of already installed Runtime
|
||||
if (
|
||||
parsed.name &&
|
||||
parsed.type === 'version' &&
|
||||
@@ -215,7 +156,6 @@ export function filterPackage(
|
||||
*/
|
||||
export async function installBuilders(
|
||||
packagesSet: Set<string>,
|
||||
yarnDir: string,
|
||||
output: Output,
|
||||
builderDir?: string
|
||||
): Promise<void> {
|
||||
@@ -231,9 +171,12 @@ export async function installBuilders(
|
||||
if (!builderDir) {
|
||||
builderDir = await builderDirPromise;
|
||||
}
|
||||
const yarnPath = join(yarnDir, 'yarn');
|
||||
const buildersPkgPath = join(builderDir, 'package.json');
|
||||
const buildersPkgBefore = await readJSON(buildersPkgPath);
|
||||
const depsBefore = {
|
||||
...buildersPkgBefore.devDependencies,
|
||||
...buildersPkgBefore.dependencies,
|
||||
};
|
||||
|
||||
packages.push(
|
||||
getBuildUtils(packages, 'vercel'),
|
||||
@@ -242,7 +185,7 @@ export async function installBuilders(
|
||||
|
||||
// Filter out any packages that come packaged with `now-cli`
|
||||
const packagesToInstall = packages.filter(p =>
|
||||
filterPackage(p, distTag, buildersPkgBefore)
|
||||
filterPackage(p, distTag, buildersPkgBefore, nowCliPkg)
|
||||
);
|
||||
|
||||
if (packagesToInstall.length === 0) {
|
||||
@@ -261,13 +204,11 @@ export async function installBuilders(
|
||||
await retry(
|
||||
() =>
|
||||
execa(
|
||||
process.execPath,
|
||||
'npm',
|
||||
[
|
||||
yarnPath,
|
||||
'add',
|
||||
'--exact',
|
||||
'--no-lockfile',
|
||||
'--non-interactive',
|
||||
'install',
|
||||
'--save-exact',
|
||||
'--no-package-lock',
|
||||
...packagesToInstall,
|
||||
],
|
||||
{
|
||||
@@ -282,8 +223,12 @@ export async function installBuilders(
|
||||
|
||||
const updatedPackages: string[] = [];
|
||||
const buildersPkgAfter = await readJSON(buildersPkgPath);
|
||||
for (const [name, version] of Object.entries(buildersPkgAfter.dependencies)) {
|
||||
if (version !== buildersPkgBefore.dependencies[name]) {
|
||||
const depsAfter = {
|
||||
...buildersPkgAfter.devDependencies,
|
||||
...buildersPkgAfter.dependencies,
|
||||
};
|
||||
for (const [name, version] of Object.entries(depsAfter)) {
|
||||
if (version !== depsBefore[name]) {
|
||||
output.debug(`Runtime "${name}" updated to version \`${version}\``);
|
||||
updatedPackages.push(name);
|
||||
}
|
||||
@@ -294,7 +239,6 @@ export async function installBuilders(
|
||||
|
||||
export async function updateBuilders(
|
||||
packagesSet: Set<string>,
|
||||
yarnDir: string,
|
||||
output: Output,
|
||||
builderDir?: string
|
||||
): Promise<string[]> {
|
||||
@@ -302,45 +246,59 @@ export async function updateBuilders(
|
||||
builderDir = await builderDirPromise;
|
||||
}
|
||||
|
||||
const updatedPackages: string[] = [];
|
||||
const packages = Array.from(packagesSet);
|
||||
const yarnPath = join(yarnDir, 'yarn');
|
||||
const buildersPkgPath = join(builderDir, 'package.json');
|
||||
const buildersPkgBefore = await readJSON(buildersPkgPath);
|
||||
const depsBefore = {
|
||||
...buildersPkgBefore.devDependencies,
|
||||
...buildersPkgBefore.dependencies,
|
||||
};
|
||||
|
||||
packages.push(
|
||||
getBuildUtils(packages, 'vercel'),
|
||||
getBuildUtils(packages, 'now')
|
||||
);
|
||||
const packagesToUpdate = packages.filter(p => {
|
||||
if (p in localBuilders) return false;
|
||||
|
||||
await retry(
|
||||
() =>
|
||||
execa(
|
||||
process.execPath,
|
||||
[
|
||||
yarnPath,
|
||||
'add',
|
||||
'--exact',
|
||||
'--no-lockfile',
|
||||
'--non-interactive',
|
||||
...packages.filter(p => p !== '@now/static'),
|
||||
],
|
||||
{
|
||||
cwd: builderDir,
|
||||
}
|
||||
),
|
||||
{ retries: 2 }
|
||||
);
|
||||
|
||||
const updatedPackages: string[] = [];
|
||||
const buildersPkgAfter = await readJSON(buildersPkgPath);
|
||||
for (const [name, version] of Object.entries(buildersPkgAfter.dependencies)) {
|
||||
if (version !== buildersPkgBefore.dependencies[name]) {
|
||||
output.debug(`Runtime "${name}" updated to version \`${version}\``);
|
||||
updatedPackages.push(name);
|
||||
// If it's a builder that is part of Now CLI's `dependencies` then
|
||||
// don't update it
|
||||
if (isBundledBuilder(npa(p), nowCliPkg)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
purgeRequireCache(updatedPackages, builderDir, output);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (packagesToUpdate.length > 0) {
|
||||
packages.push(
|
||||
getBuildUtils(packages, 'vercel'),
|
||||
getBuildUtils(packages, 'now')
|
||||
);
|
||||
|
||||
await retry(
|
||||
() =>
|
||||
execa(
|
||||
'npm',
|
||||
['install', '--save-exact', '--no-package-lock', ...packagesToUpdate],
|
||||
{
|
||||
cwd: builderDir,
|
||||
}
|
||||
),
|
||||
{ retries: 2 }
|
||||
);
|
||||
|
||||
const buildersPkgAfter = await readJSON(buildersPkgPath);
|
||||
const depsAfter = {
|
||||
...buildersPkgAfter.devDependencies,
|
||||
...buildersPkgAfter.dependencies,
|
||||
};
|
||||
for (const [name, version] of Object.entries(depsAfter)) {
|
||||
if (version !== depsBefore[name]) {
|
||||
output.debug(`Runtime "${name}" updated to version \`${version}\``);
|
||||
updatedPackages.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
purgeRequireCache(updatedPackages, builderDir, output);
|
||||
}
|
||||
|
||||
return updatedPackages;
|
||||
}
|
||||
@@ -350,43 +308,83 @@ export async function updateBuilders(
|
||||
*/
|
||||
export async function getBuilder(
|
||||
builderPkg: string,
|
||||
yarnDir: string,
|
||||
output: Output,
|
||||
builderDir?: string
|
||||
builderDir?: string,
|
||||
isRetry = false
|
||||
): Promise<BuilderWithPackage> {
|
||||
let builderWithPkg: BuilderWithPackage = localBuilders[builderPkg];
|
||||
if (!builderWithPkg) {
|
||||
if (!builderDir) {
|
||||
builderDir = await builderDirPromise;
|
||||
}
|
||||
let requirePath: string;
|
||||
const parsed = npa(builderPkg);
|
||||
const buildersPkg = await readJSON(join(builderDir, 'package.json'));
|
||||
const pkgName = getPackageName(parsed, buildersPkg) || builderPkg;
|
||||
const dest = join(builderDir, 'node_modules', pkgName);
|
||||
|
||||
// First check if it's a bundled Runtime in Now CLI's `node_modules`
|
||||
const bundledBuilder = isBundledBuilder(parsed, nowCliPkg);
|
||||
if (bundledBuilder && parsed.name) {
|
||||
requirePath = parsed.name;
|
||||
} else {
|
||||
const buildersPkg = await readJSON(join(builderDir, 'package.json'));
|
||||
const pkgName = getPackageName(parsed, buildersPkg) || builderPkg;
|
||||
requirePath = join(builderDir, 'node_modules', pkgName);
|
||||
}
|
||||
|
||||
try {
|
||||
const mod = require(dest);
|
||||
const pkg = require(join(dest, 'package.json'));
|
||||
output.debug(`Requiring runtime: "${requirePath}"`);
|
||||
const mod = require(requirePath);
|
||||
const pkg = require(join(requirePath, 'package.json'));
|
||||
builderWithPkg = {
|
||||
requirePath,
|
||||
builder: Object.freeze(mod),
|
||||
package: Object.freeze(pkg),
|
||||
};
|
||||
} catch (err) {
|
||||
if (err.code === 'MODULE_NOT_FOUND') {
|
||||
if (err.code === 'MODULE_NOT_FOUND' && !isRetry) {
|
||||
output.debug(
|
||||
`Attempted to require ${builderPkg}, but it is not installed`
|
||||
`Attempted to require ${requirePath}, but it is not installed`
|
||||
);
|
||||
const pkgSet = new Set([builderPkg]);
|
||||
await installBuilders(pkgSet, yarnDir, output, builderDir);
|
||||
await installBuilders(pkgSet, output, builderDir);
|
||||
|
||||
// Run `getBuilder()` again now that the builder has been installed
|
||||
return getBuilder(builderPkg, yarnDir, output, builderDir);
|
||||
return getBuilder(builderPkg, output, builderDir, true);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
// If it's a bundled builder, then cache the require call
|
||||
if (bundledBuilder) {
|
||||
localBuilders[builderPkg] = builderWithPkg;
|
||||
}
|
||||
}
|
||||
return builderWithPkg;
|
||||
}
|
||||
|
||||
export function isBundledBuilder(
|
||||
parsed: npa.Result,
|
||||
pkg: PackageJson
|
||||
): boolean {
|
||||
if (!parsed.name || !pkg.dependencies) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bundledVersion = pkg.dependencies[parsed.name];
|
||||
if (bundledVersion) {
|
||||
if (parsed.type === 'tag') {
|
||||
if (parsed.fetchSpec === 'canary') {
|
||||
return bundledVersion.includes('canary');
|
||||
} else if (parsed.fetchSpec === 'latest') {
|
||||
return !bundledVersion.includes('canary');
|
||||
}
|
||||
} else if (parsed.type === 'version') {
|
||||
return parsed.fetchSpec === bundledVersion;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getPackageName(
|
||||
parsed: npa.Result,
|
||||
buildersPkg: PackageJson
|
||||
@@ -394,30 +392,18 @@ function getPackageName(
|
||||
if (registryTypes.has(parsed.type)) {
|
||||
return parsed.name;
|
||||
}
|
||||
const deps = { ...buildersPkg.devDependencies, ...buildersPkg.dependencies };
|
||||
const deps: { [name: string]: string } = {
|
||||
...buildersPkg.devDependencies,
|
||||
...buildersPkg.dependencies,
|
||||
};
|
||||
for (const [name, dep] of Object.entries(deps)) {
|
||||
if (dep === parsed.raw) {
|
||||
if (dep === parsed.raw || basename(dep) === basename(parsed.raw)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getSha(buffer: Buffer): string {
|
||||
const hash = createHash('sha256');
|
||||
hash.update(buffer);
|
||||
return hash.digest('hex');
|
||||
}
|
||||
|
||||
function hasBundledBuilders(dependencies: { [name: string]: string }): boolean {
|
||||
for (const name of getBundledBuilders()) {
|
||||
if (!(name in dependencies)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function purgeRequireCache(
|
||||
packages: string[],
|
||||
builderDir: string,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* This file gets copied out of the `pkg` snapshot filesystem into the `now dev`
|
||||
* This file gets copied out of the `pkg` snapshot filesystem into the `vc dev`
|
||||
* builder cache directory, so it's very important that it does not rely on any
|
||||
* modules from npm that would not be available in that directory (so basically,
|
||||
* only Now builders and `@now/build-utils`.
|
||||
* only Vercel Runtimes and `@vercel/build-utils`.
|
||||
*/
|
||||
const { FileFsRef } = require('@now/build-utils');
|
||||
const { FileFsRef } = require('@vercel/build-utils');
|
||||
|
||||
process.on('unhandledRejection', err => {
|
||||
console.error('Exiting builder due to build error:');
|
||||
@@ -24,21 +24,21 @@ function onMessage(message) {
|
||||
}
|
||||
|
||||
async function processMessage(message) {
|
||||
const { builderName, buildParams } = message;
|
||||
const builder = require(builderName);
|
||||
const { requirePath, buildOptions } = message;
|
||||
const builder = require(requirePath);
|
||||
|
||||
// Convert the `files` to back into `FileFsRef` instances
|
||||
for (const name of Object.keys(buildParams.files)) {
|
||||
for (const name of Object.keys(buildOptions.files)) {
|
||||
const ref = Object.assign(
|
||||
Object.create(FileFsRef.prototype),
|
||||
buildParams.files[name]
|
||||
buildOptions.files[name]
|
||||
);
|
||||
buildParams.files[name] = ref;
|
||||
buildOptions.files[name] = ref;
|
||||
}
|
||||
|
||||
const result = await builder.build(buildParams);
|
||||
const result = await builder.build(buildOptions);
|
||||
|
||||
// `@now/next` sets this, but it causes "Converting circular
|
||||
// `@vercel/next` sets this, but it causes "Converting circular
|
||||
// structure to JSON" errors, so delete the property...
|
||||
delete result.childProcesses;
|
||||
|
||||
|
||||
@@ -2,31 +2,31 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import ms from 'ms';
|
||||
import bytes from 'bytes';
|
||||
import { promisify } from 'util';
|
||||
import { delimiter, dirname, join } from 'path';
|
||||
import { fork, ChildProcess } from 'child_process';
|
||||
import { createFunction } from '@zeit/fun';
|
||||
import {
|
||||
Builder,
|
||||
BuildOptions,
|
||||
Env,
|
||||
File,
|
||||
Lambda,
|
||||
FileBlob,
|
||||
FileFsRef,
|
||||
isOfficialRuntime,
|
||||
} from '@vercel/build-utils';
|
||||
import plural from 'pluralize';
|
||||
import minimatch from 'minimatch';
|
||||
import _treeKill from 'tree-kill';
|
||||
|
||||
import { isOfficialRuntime } from '../is-official-runtime';
|
||||
import { Output } from '../output';
|
||||
import highlight from '../output/highlight';
|
||||
import { treeKill } from '../tree-kill';
|
||||
import { relative } from '../path-helpers';
|
||||
import { LambdaSizeExceededError } from '../errors-ts';
|
||||
|
||||
import DevServer from './server';
|
||||
import { builderModulePathPromise, getBuilder } from './builder-cache';
|
||||
import { getBuilder } from './builder-cache';
|
||||
import {
|
||||
EnvConfig,
|
||||
NowConfig,
|
||||
BuildMatch,
|
||||
BuildResult,
|
||||
@@ -34,11 +34,11 @@ import {
|
||||
BuilderOutput,
|
||||
BuildResultV3,
|
||||
BuilderOutputs,
|
||||
BuilderParams,
|
||||
EnvConfigs,
|
||||
} from './types';
|
||||
import { normalizeRoutes } from '@vercel/routing-utils';
|
||||
import getUpdateCommand from '../get-update-command';
|
||||
import { getVercelDirectory } from '../projects/link';
|
||||
|
||||
interface BuildMessage {
|
||||
type: string;
|
||||
@@ -50,38 +50,28 @@ interface BuildMessageResult extends BuildMessage {
|
||||
error?: object;
|
||||
}
|
||||
|
||||
const treeKill = promisify(_treeKill);
|
||||
|
||||
async function createBuildProcess(
|
||||
match: BuildMatch,
|
||||
envConfigs: EnvConfigs,
|
||||
workPath: string,
|
||||
output: Output,
|
||||
yarnPath?: string
|
||||
output: Output
|
||||
): Promise<ChildProcess> {
|
||||
const { execPath } = process;
|
||||
const modulePath = await builderModulePathPromise;
|
||||
const builderWorkerPath = join(__dirname, 'builder-worker.js');
|
||||
|
||||
// Ensure that `node` is in the builder's `PATH`
|
||||
let PATH = `${dirname(execPath)}${delimiter}${process.env.PATH}`;
|
||||
let PATH = `${dirname(process.execPath)}${delimiter}${process.env.PATH}`;
|
||||
|
||||
// Ensure that `yarn` is in the builder's `PATH`
|
||||
if (yarnPath) {
|
||||
PATH = `${yarnPath}${delimiter}${PATH}`;
|
||||
}
|
||||
|
||||
const env: EnvConfig = {
|
||||
const env: Env = {
|
||||
...process.env,
|
||||
PATH,
|
||||
...envConfigs.allEnv,
|
||||
NOW_REGION: 'dev1',
|
||||
VERCEL_REGION: 'dev1',
|
||||
};
|
||||
|
||||
const buildProcess = fork(modulePath, [], {
|
||||
const buildProcess = fork(builderWorkerPath, [], {
|
||||
cwd: workPath,
|
||||
env,
|
||||
execPath,
|
||||
execArgv: [],
|
||||
});
|
||||
match.buildProcess = buildProcess;
|
||||
|
||||
@@ -115,10 +105,10 @@ export async function executeBuild(
|
||||
filesRemoved?: string[]
|
||||
): Promise<void> {
|
||||
const {
|
||||
builderWithPkg: { runInProcess, builder, package: pkg },
|
||||
builderWithPkg: { runInProcess, requirePath, builder, package: pkg },
|
||||
} = match;
|
||||
const { entrypoint } = match;
|
||||
const { debug, envConfigs, yarnPath, cwd: workPath } = devServer;
|
||||
const { debug, envConfigs, cwd: workPath } = devServer;
|
||||
|
||||
const startTime = Date.now();
|
||||
const showBuildTimestamp =
|
||||
@@ -142,12 +132,14 @@ export async function executeBuild(
|
||||
match,
|
||||
envConfigs,
|
||||
workPath,
|
||||
devServer.output,
|
||||
yarnPath
|
||||
devServer.output
|
||||
);
|
||||
}
|
||||
|
||||
const buildParams: BuilderParams = {
|
||||
const vercelDir = getVercelDirectory(workPath);
|
||||
const devCacheDir = join(vercelDir, 'cache');
|
||||
|
||||
const buildOptions: BuildOptions = {
|
||||
files,
|
||||
entrypoint,
|
||||
workPath,
|
||||
@@ -155,6 +147,7 @@ export async function executeBuild(
|
||||
meta: {
|
||||
isDev: true,
|
||||
requestPath,
|
||||
devCacheDir,
|
||||
filesChanged,
|
||||
filesRemoved,
|
||||
// This env distiniction is only necessary to maintain
|
||||
@@ -168,8 +161,8 @@ export async function executeBuild(
|
||||
if (buildProcess) {
|
||||
buildProcess.send({
|
||||
type: 'build',
|
||||
builderName: pkg.name,
|
||||
buildParams,
|
||||
requirePath,
|
||||
buildOptions,
|
||||
});
|
||||
|
||||
buildResultOrOutputs = await new Promise((resolve, reject) => {
|
||||
@@ -200,7 +193,7 @@ export async function executeBuild(
|
||||
buildProcess!.on('message', onMessage);
|
||||
});
|
||||
} else {
|
||||
buildResultOrOutputs = await builder.build(buildParams);
|
||||
buildResultOrOutputs = await builder.build(buildOptions);
|
||||
}
|
||||
|
||||
// Sort out build result to builder v2 shape
|
||||
@@ -374,6 +367,7 @@ export async function executeBuild(
|
||||
...asset.environment,
|
||||
...envConfigs.runEnv,
|
||||
NOW_REGION: 'dev1',
|
||||
VERCEL_REGION: 'dev1',
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -397,7 +391,6 @@ export async function executeBuild(
|
||||
export async function getBuildMatches(
|
||||
nowConfig: NowConfig,
|
||||
cwd: string,
|
||||
yarnDir: string,
|
||||
output: Output,
|
||||
devServer: DevServer,
|
||||
fileList: string[]
|
||||
@@ -449,7 +442,7 @@ export async function getBuildMatches(
|
||||
|
||||
for (const file of files) {
|
||||
src = relative(cwd, file);
|
||||
const builderWithPkg = await getBuilder(use, yarnDir, output);
|
||||
const builderWithPkg = await getBuilder(use, output);
|
||||
matches.push({
|
||||
...buildConfig,
|
||||
src,
|
||||
|
||||
@@ -4,7 +4,7 @@ import PCRE from 'pcre-to-regexp';
|
||||
import isURL from './is-url';
|
||||
import DevServer from './server';
|
||||
|
||||
import { HttpHeadersConfig, RouteConfig, RouteResult } from './types';
|
||||
import { HttpHeadersConfig, RouteResult } from './types';
|
||||
import { isHandler, Route, HandleValue } from '@vercel/routing-utils';
|
||||
|
||||
export function resolveRouteParameters(
|
||||
@@ -48,10 +48,10 @@ export function getRoutesTypes(routes: Route[] = []) {
|
||||
export async function devRouter(
|
||||
reqUrl: string = '/',
|
||||
reqMethod?: string,
|
||||
routes?: RouteConfig[],
|
||||
routes?: Route[],
|
||||
devServer?: DevServer,
|
||||
previousHeaders?: HttpHeadersConfig,
|
||||
missRoutes?: RouteConfig[],
|
||||
missRoutes?: Route[],
|
||||
phase?: HandleValue | null
|
||||
): Promise<RouteResult> {
|
||||
let result: RouteResult | undefined;
|
||||
@@ -79,7 +79,8 @@ export async function devRouter(
|
||||
}
|
||||
|
||||
const keys: string[] = [];
|
||||
const matcher = PCRE(`%${src}%i`, keys);
|
||||
const flags = devServer && devServer.isCaseSensitive() ? '' : 'i';
|
||||
const matcher = PCRE(`%${src}%${flags}`, keys);
|
||||
const match =
|
||||
matcher.exec(reqPathname) || matcher.exec(reqPathname.substring(1));
|
||||
|
||||
|
||||
@@ -12,36 +12,39 @@ import serveHandler from 'serve-handler';
|
||||
import { watch, FSWatcher } from 'chokidar';
|
||||
import { parse as parseDotenv } from 'dotenv';
|
||||
import { basename, dirname, extname, join } from 'path';
|
||||
import once from '@tootallnate/once';
|
||||
import directoryTemplate from 'serve-handler/src/directory';
|
||||
import getPort from 'get-port';
|
||||
import { ChildProcess } from 'child_process';
|
||||
import isPortReachable from 'is-port-reachable';
|
||||
import which from 'which';
|
||||
|
||||
import { getVercelIgnore } from '@vercel/client';
|
||||
import { getVercelIgnore, fileNameSymbol } from '@vercel/client';
|
||||
import {
|
||||
getTransformedRoutes,
|
||||
appendRoutesToPhase,
|
||||
HandleValue,
|
||||
Route,
|
||||
} from '@vercel/routing-utils';
|
||||
import {
|
||||
Builder,
|
||||
Env,
|
||||
StartDevServerResult,
|
||||
FileFsRef,
|
||||
PackageJson,
|
||||
detectBuilders,
|
||||
detectApiDirectory,
|
||||
detectApiExtensions,
|
||||
spawnCommand,
|
||||
isOfficialRuntime,
|
||||
} from '@vercel/build-utils';
|
||||
|
||||
import { once } from '../once';
|
||||
import link from '../output/link';
|
||||
import { Output } from '../output';
|
||||
import { relative } from '../path-helpers';
|
||||
import { getDistTag } from '../get-dist-tag';
|
||||
import getNowConfigPath from '../config/local-path';
|
||||
import { MissingDotenvVarsError } from '../errors-ts';
|
||||
import { isOfficialRuntime } from '../is-official-runtime';
|
||||
import { version as cliVersion } from '../../../package.json';
|
||||
import { staticFiles as getFiles, getAllProjectFiles } from '../get-files';
|
||||
import {
|
||||
@@ -57,10 +60,13 @@ import {
|
||||
|
||||
import { devRouter, getRoutesTypes } from './router';
|
||||
import getMimeType from './mime-type';
|
||||
import { getYarnPath } from './yarn-installer';
|
||||
import { executeBuild, getBuildMatches, shutdownBuilder } from './builder';
|
||||
import { generateErrorMessage, generateHttpStatusDescription } from './errors';
|
||||
import { installBuilders, updateBuilders } from './builder-cache';
|
||||
import {
|
||||
installBuilders,
|
||||
updateBuilders,
|
||||
builderDirPromise,
|
||||
} from './builder-cache';
|
||||
|
||||
// HTML templates
|
||||
import errorTemplate from './templates/error';
|
||||
@@ -70,7 +76,6 @@ import errorTemplate502 from './templates/error_502';
|
||||
import redirectTemplate from './templates/redirect';
|
||||
|
||||
import {
|
||||
EnvConfig,
|
||||
NowConfig,
|
||||
DevServerOptions,
|
||||
BuildMatch,
|
||||
@@ -81,7 +86,6 @@ import {
|
||||
InvokePayload,
|
||||
InvokeResult,
|
||||
ListenSpec,
|
||||
RouteConfig,
|
||||
RouteResult,
|
||||
HttpHeadersConfig,
|
||||
EnvConfigs,
|
||||
@@ -111,10 +115,10 @@ export default class DevServer {
|
||||
public envConfigs: EnvConfigs;
|
||||
public frameworkSlug: string | null;
|
||||
public files: BuilderInputs;
|
||||
public yarnPath: string;
|
||||
public address: string;
|
||||
|
||||
private cachedNowConfig: NowConfig | null;
|
||||
private caseSensitive: boolean;
|
||||
private apiDir: string | null;
|
||||
private apiExtensions: Set<string>;
|
||||
private server: http.Server;
|
||||
@@ -130,6 +134,7 @@ export default class DevServer {
|
||||
private devCommand?: string;
|
||||
private devProcess?: ChildProcess;
|
||||
private devProcessPort?: number;
|
||||
private devServerPids: Set<number>;
|
||||
|
||||
private getNowConfigPromise: Promise<NowConfig> | null;
|
||||
private blockingBuildsPromise: Promise<void> | null;
|
||||
@@ -144,13 +149,10 @@ export default class DevServer {
|
||||
this.address = '';
|
||||
this.devCommand = options.devCommand;
|
||||
this.frameworkSlug = options.frameworkSlug;
|
||||
|
||||
// This gets updated when `start()` is invoked
|
||||
this.yarnPath = '/';
|
||||
|
||||
this.cachedNowConfig = null;
|
||||
this.caseSensitive = false;
|
||||
this.apiDir = null;
|
||||
this.apiExtensions = new Set<string>();
|
||||
this.apiExtensions = new Set();
|
||||
this.server = http.createServer(this.devServerHandler);
|
||||
this.server.timeout = 0; // Disable timeout
|
||||
this.stopping = false;
|
||||
@@ -169,6 +171,8 @@ export default class DevServer {
|
||||
this.podId = Math.random()
|
||||
.toString(32)
|
||||
.slice(-5);
|
||||
|
||||
this.devServerPids = new Set();
|
||||
}
|
||||
|
||||
async exit(code = 1) {
|
||||
@@ -355,7 +359,6 @@ export default class DevServer {
|
||||
const matches = await getBuildMatches(
|
||||
nowConfig,
|
||||
this.cwd,
|
||||
this.yarnPath,
|
||||
this.output,
|
||||
this,
|
||||
fileList
|
||||
@@ -447,11 +450,11 @@ export default class DevServer {
|
||||
await this.updateBuildMatches(nowConfig);
|
||||
}
|
||||
|
||||
async getLocalEnv(fileName: string, base?: EnvConfig): Promise<EnvConfig> {
|
||||
async getLocalEnv(fileName: string, base?: Env): Promise<Env> {
|
||||
// TODO: use the file watcher to only invalidate the env `dotfile`
|
||||
// once a change to the `fileName` occurs
|
||||
const filePath = join(this.cwd, fileName);
|
||||
let env: EnvConfig = {};
|
||||
let env: Env = {};
|
||||
try {
|
||||
const dotenv = await fs.readFile(filePath, 'utf8');
|
||||
this.output.debug(`Using local env: ${filePath}`);
|
||||
@@ -495,7 +498,11 @@ export default class DevServer {
|
||||
|
||||
// The default empty `vercel.json` is used to serve all files as static
|
||||
// when no `vercel.json` is present
|
||||
let config: NowConfig = this.cachedNowConfig || { version: 2 };
|
||||
let configPath = 'vercel.json';
|
||||
let config: NowConfig = this.cachedNowConfig || {
|
||||
version: 2,
|
||||
[fileNameSymbol]: configPath,
|
||||
};
|
||||
|
||||
// We need to delete these properties for zero config to work
|
||||
// with file changes
|
||||
@@ -504,11 +511,11 @@ export default class DevServer {
|
||||
delete this.cachedNowConfig.routes;
|
||||
}
|
||||
|
||||
let configPath = 'vercel.json';
|
||||
try {
|
||||
configPath = getNowConfigPath(this.cwd);
|
||||
this.output.debug(`Reading ${configPath}`);
|
||||
config = JSON.parse(await fs.readFile(configPath, 'utf8'));
|
||||
config[fileNameSymbol] = configPath;
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
this.output.debug(err.toString());
|
||||
@@ -577,7 +584,7 @@ export default class DevServer {
|
||||
config.builds = config.builds || [];
|
||||
config.builds.push(...builders);
|
||||
|
||||
const routes: RouteConfig[] = [];
|
||||
const routes: Route[] = [];
|
||||
const { routes: nowConfigRoutes } = config;
|
||||
routes.push(...(redirectRoutes || []));
|
||||
routes.push(
|
||||
@@ -605,6 +612,7 @@ export default class DevServer {
|
||||
await this.validateNowConfig(config);
|
||||
|
||||
this.cachedNowConfig = config;
|
||||
this.caseSensitive = hasNewRoutingProperties(config);
|
||||
this.apiDir = detectApiDirectory(config.builds || []);
|
||||
this.apiExtensions = detectApiExtensions(config.builds || []);
|
||||
return config;
|
||||
@@ -649,6 +657,7 @@ export default class DevServer {
|
||||
if (config.version === 1) {
|
||||
this.output.error('Only `version: 2` is supported by `now dev`');
|
||||
await this.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.tryValidateOrExit(config, validateNowConfigBuilds);
|
||||
@@ -661,13 +670,8 @@ export default class DevServer {
|
||||
await this.tryValidateOrExit(config, validateNowConfigFunctions);
|
||||
}
|
||||
|
||||
validateEnvConfig(
|
||||
type: string,
|
||||
env: EnvConfig = {},
|
||||
localEnv: EnvConfig = {}
|
||||
): EnvConfig {
|
||||
validateEnvConfig(type: string, env: Env = {}, localEnv: Env = {}): Env {
|
||||
// Validate if there are any missing env vars defined in `vercel.json`,
|
||||
|
||||
// but not in the `.env` / `.build.env` file
|
||||
const missing: string[] = Object.entries(env)
|
||||
.filter(
|
||||
@@ -682,7 +686,7 @@ export default class DevServer {
|
||||
throw new MissingDotenvVarsError(type, missing);
|
||||
}
|
||||
|
||||
const merged: EnvConfig = { ...env, ...localEnv };
|
||||
const merged: Env = { ...env, ...localEnv };
|
||||
|
||||
// Validate that the env var name matches what AWS Lambda allows:
|
||||
// - https://docs.aws.amazon.com/lambda/latest/dg/env_variables.html
|
||||
@@ -730,7 +734,6 @@ export default class DevServer {
|
||||
}
|
||||
|
||||
const { ig } = await getVercelIgnore(this.cwd);
|
||||
this.yarnPath = await getYarnPath(this.output);
|
||||
this.filter = ig.createFilter();
|
||||
|
||||
// Retrieve the path of the native module
|
||||
@@ -763,16 +766,12 @@ export default class DevServer {
|
||||
.map((b: Builder) => b.use)
|
||||
);
|
||||
|
||||
await installBuilders(builders, this.yarnPath, this.output);
|
||||
await installBuilders(builders, this.output);
|
||||
await this.updateBuildMatches(nowConfig, true);
|
||||
|
||||
// Updating builders happens lazily, and any builders that were updated
|
||||
// get their "build matches" invalidated so that the new version is used.
|
||||
this.updateBuildersPromise = updateBuilders(
|
||||
builders,
|
||||
this.yarnPath,
|
||||
this.output
|
||||
)
|
||||
this.updateBuildersPromise = updateBuilders(builders, this.output)
|
||||
.then(updatedBuilders => {
|
||||
this.updateBuildersPromise = null;
|
||||
this.invalidateBuildMatches(nowConfig, updatedBuilders);
|
||||
@@ -868,6 +867,7 @@ export default class DevServer {
|
||||
*/
|
||||
async stop(exitCode?: number): Promise<void> {
|
||||
const { devProcess } = this;
|
||||
const { debug } = this.output;
|
||||
if (this.stopping) return;
|
||||
|
||||
this.stopping = true;
|
||||
@@ -898,15 +898,22 @@ export default class DevServer {
|
||||
ops.push(close(this.server));
|
||||
|
||||
if (this.watcher) {
|
||||
this.output.debug(`Closing file watcher`);
|
||||
debug(`Closing file watcher`);
|
||||
this.watcher.close();
|
||||
}
|
||||
|
||||
if (this.updateBuildersPromise) {
|
||||
this.output.debug(`Waiting for builders update to complete`);
|
||||
debug(`Waiting for builders update to complete`);
|
||||
ops.push(this.updateBuildersPromise);
|
||||
}
|
||||
|
||||
for (const pid of this.devServerPids) {
|
||||
ops.push(this.killBuilderDevServer(pid));
|
||||
}
|
||||
|
||||
// Ensure that the builders module cache is created
|
||||
ops.push(builderDirPromise);
|
||||
|
||||
try {
|
||||
await Promise.all(ops);
|
||||
} catch (err) {
|
||||
@@ -922,6 +929,18 @@ export default class DevServer {
|
||||
}
|
||||
}
|
||||
|
||||
async killBuilderDevServer(pid: number) {
|
||||
const { debug } = this.output;
|
||||
debug(`Killing builder dev server with PID ${pid}`);
|
||||
this.devServerPids.delete(pid);
|
||||
try {
|
||||
process.kill(pid, 'SIGTERM');
|
||||
debug(`Killed builder dev server with PID ${pid}`);
|
||||
} catch (err) {
|
||||
debug(`Failed to kill builder dev server with PID ${pid}: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async send404(
|
||||
req: http.IncomingMessage,
|
||||
res: http.ServerResponse,
|
||||
@@ -1215,9 +1234,11 @@ export default class DevServer {
|
||||
res: http.ServerResponse,
|
||||
nowRequestId: string,
|
||||
nowConfig: NowConfig,
|
||||
routes: RouteConfig[] | undefined = nowConfig.routes,
|
||||
routes: Route[] | undefined = nowConfig.routes,
|
||||
callLevel: number = 0
|
||||
) => {
|
||||
const { debug } = this.output;
|
||||
|
||||
// If there is a double-slash present in the URL,
|
||||
// then perform a redirect to make it "clean".
|
||||
const parsed = url.parse(req.url || '/');
|
||||
@@ -1234,16 +1255,16 @@ export default class DevServer {
|
||||
return;
|
||||
}
|
||||
|
||||
this.output.debug(`Rewriting URL from "${req.url}" to "${location}"`);
|
||||
debug(`Rewriting URL from "${req.url}" to "${location}"`);
|
||||
req.url = location;
|
||||
}
|
||||
|
||||
await this.updateBuildMatches(nowConfig);
|
||||
if (callLevel === 0) {
|
||||
await this.updateBuildMatches(nowConfig);
|
||||
}
|
||||
|
||||
if (this.blockingBuildsPromise) {
|
||||
this.output.debug(
|
||||
'Waiting for builds to complete before handling request'
|
||||
);
|
||||
debug('Waiting for builds to complete before handling request');
|
||||
await this.blockingBuildsPromise;
|
||||
}
|
||||
|
||||
@@ -1284,7 +1305,7 @@ export default class DevServer {
|
||||
Object.assign(destParsed.query, routeResult.uri_args);
|
||||
const destUrl = url.format(destParsed);
|
||||
|
||||
this.output.debug(`ProxyPass: ${destUrl}`);
|
||||
debug(`ProxyPass: ${destUrl}`);
|
||||
this.setResponseHeaders(res, nowRequestId);
|
||||
return proxyPass(req, res, destUrl, this.output);
|
||||
}
|
||||
@@ -1375,15 +1396,25 @@ export default class DevServer {
|
||||
});
|
||||
|
||||
if (statusCode) {
|
||||
res.statusCode = statusCode;
|
||||
// Set the `statusCode` as read-only so that `http-proxy`
|
||||
// is not able to modify the value in the future
|
||||
Object.defineProperty(res, 'statusCode', {
|
||||
get() {
|
||||
return statusCode;
|
||||
},
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
set(_: number) {
|
||||
/* ignore */
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const requestPath = dest.replace(/^\//, '');
|
||||
|
||||
if (!match) {
|
||||
// if the dev command is started, proxy to it
|
||||
// If the dev command is started, then proxy to it
|
||||
if (this.devProcessPort) {
|
||||
this.output.debug('Proxy to dev command server');
|
||||
debug('Proxying to frontend dev server');
|
||||
this.setResponseHeaders(res, nowRequestId);
|
||||
return proxyPass(
|
||||
req,
|
||||
@@ -1416,7 +1447,7 @@ export default class DevServer {
|
||||
origUrl.pathname = dest;
|
||||
Object.assign(origUrl.query, uri_args);
|
||||
const newUrl = url.format(origUrl);
|
||||
this.output.debug(
|
||||
debug(
|
||||
`Checking build result's ${buildResult.routes.length} \`routes\` to match ${newUrl}`
|
||||
);
|
||||
const matchedRoute = await devRouter(
|
||||
@@ -1426,9 +1457,7 @@ export default class DevServer {
|
||||
this
|
||||
);
|
||||
if (matchedRoute.found && callLevel === 0) {
|
||||
this.output.debug(
|
||||
`Found matching route ${matchedRoute.dest} for ${newUrl}`
|
||||
);
|
||||
debug(`Found matching route ${matchedRoute.dest} for ${newUrl}`);
|
||||
req.url = newUrl;
|
||||
await this.serveProjectAsNowV2(
|
||||
req,
|
||||
@@ -1442,7 +1471,76 @@ export default class DevServer {
|
||||
}
|
||||
}
|
||||
|
||||
// Before doing any asset matching, check if this builder supports the
|
||||
// `startDevServer()` "optimization". In this case, the now dev server invokes
|
||||
// `startDevServer()` on the builder for every HTTP request so that it boots
|
||||
// up a single-serve dev HTTP server that now dev will proxy this HTTP request
|
||||
// to. Once the proxied request is finished, now dev shuts down the dev
|
||||
// server child process.
|
||||
const { builder, package: builderPkg } = match.builderWithPkg;
|
||||
if (typeof builder.startDevServer === 'function') {
|
||||
let devServerResult: StartDevServerResult = null;
|
||||
try {
|
||||
devServerResult = await builder.startDevServer({
|
||||
entrypoint: match.entrypoint,
|
||||
workPath: this.cwd,
|
||||
config: match.config || {},
|
||||
env: this.envConfigs.runEnv || {},
|
||||
});
|
||||
} catch (err) {
|
||||
// `startDevServer()` threw an error. Most likely this means the dev
|
||||
// server process exited before sending the port information message
|
||||
// (missing dependency at runtime, for example).
|
||||
debug(`Error starting "${builderPkg.name}" dev server: ${err}`);
|
||||
await this.sendError(
|
||||
req,
|
||||
res,
|
||||
nowRequestId,
|
||||
'NO_STATUS_CODE_FROM_DEV_SERVER',
|
||||
502
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (devServerResult) {
|
||||
// When invoking lambda functions, the region where the lambda was invoked
|
||||
// is also included in the request ID. So use the same `dev1` fake region.
|
||||
nowRequestId = generateRequestId(this.podId, true);
|
||||
|
||||
const { port, pid } = devServerResult;
|
||||
this.devServerPids.add(pid);
|
||||
|
||||
res.once('close', () => {
|
||||
this.killBuilderDevServer(pid);
|
||||
});
|
||||
|
||||
debug(
|
||||
`Proxying to "${builderPkg.name}" dev server (port=${port}, pid=${pid})`
|
||||
);
|
||||
|
||||
// Mix in the routing based query parameters
|
||||
const parsed = url.parse(req.url || '/', true);
|
||||
Object.assign(parsed.query, uri_args);
|
||||
req.url = url.format({
|
||||
pathname: parsed.pathname,
|
||||
query: parsed.query,
|
||||
});
|
||||
|
||||
this.setResponseHeaders(res, nowRequestId);
|
||||
return proxyPass(
|
||||
req,
|
||||
res,
|
||||
`http://localhost:${port}`,
|
||||
this.output,
|
||||
false
|
||||
);
|
||||
} else {
|
||||
debug(`Skipping \`startDevServer()\` for ${match.entrypoint}`);
|
||||
}
|
||||
}
|
||||
|
||||
let foundAsset = findAsset(match, requestPath, nowConfig);
|
||||
|
||||
if (!foundAsset && callLevel === 0) {
|
||||
await this.triggerBuild(match, buildRequestPath, req);
|
||||
|
||||
@@ -1457,7 +1555,7 @@ export default class DevServer {
|
||||
this.devProcessPort &&
|
||||
(!foundAsset || (foundAsset && foundAsset.asset.type !== 'Lambda'))
|
||||
) {
|
||||
this.output.debug('Proxy to dev command server');
|
||||
debug('Proxying to frontend dev server');
|
||||
this.setResponseHeaders(res, nowRequestId);
|
||||
return proxyPass(
|
||||
req,
|
||||
@@ -1474,7 +1572,7 @@ export default class DevServer {
|
||||
}
|
||||
|
||||
const { asset, assetKey } = foundAsset;
|
||||
this.output.debug(
|
||||
debug(
|
||||
`Serving asset: [${asset.type}] ${assetKey} ${(asset as any)
|
||||
.contentType || ''}`
|
||||
);
|
||||
@@ -1543,7 +1641,7 @@ export default class DevServer {
|
||||
body: body.toString('base64'),
|
||||
};
|
||||
|
||||
this.output.debug(`Invoking lambda: "${assetKey}" with ${path}`);
|
||||
debug(`Invoking lambda: "${assetKey}" with ${path}`);
|
||||
|
||||
let result: InvokeResult;
|
||||
try {
|
||||
@@ -1675,6 +1773,10 @@ export default class DevServer {
|
||||
return false;
|
||||
}
|
||||
|
||||
isCaseSensitive(): boolean {
|
||||
return this.caseSensitive;
|
||||
}
|
||||
|
||||
async runDevCommand() {
|
||||
const { devCommand, cwd } = this;
|
||||
|
||||
@@ -1688,7 +1790,7 @@ export default class DevServer {
|
||||
|
||||
const port = await getPort();
|
||||
|
||||
const env: EnvConfig = {
|
||||
const env: Env = {
|
||||
// Because of child process 'pipe' below, isTTY will be false.
|
||||
// Most frameworks use `chalk`/`supports-color` so we enable it anyway.
|
||||
FORCE_COLOR: process.stdout.isTTY ? '1' : '0',
|
||||
@@ -1696,6 +1798,7 @@ export default class DevServer {
|
||||
...process.env,
|
||||
...this.envConfigs.allEnv,
|
||||
NOW_REGION: 'dev1',
|
||||
VERCEL_REGION: 'dev1',
|
||||
PORT: `${port}`,
|
||||
};
|
||||
|
||||
@@ -1900,7 +2003,7 @@ async function shouldServe(
|
||||
const shouldServe = await builder.shouldServe({
|
||||
entrypoint: src,
|
||||
files,
|
||||
config,
|
||||
config: config || {},
|
||||
requestPath,
|
||||
workPath: devServer.cwd,
|
||||
});
|
||||
@@ -2041,3 +2144,13 @@ function filterFrontendBuilds(build: Builder) {
|
||||
!isOfficialRuntime('next', build.use)
|
||||
);
|
||||
}
|
||||
|
||||
function hasNewRoutingProperties(nowConfig: NowConfig) {
|
||||
return (
|
||||
typeof nowConfig.cleanUrls !== undefined ||
|
||||
typeof nowConfig.headers !== undefined ||
|
||||
typeof nowConfig.redirects !== undefined ||
|
||||
typeof nowConfig.rewrites !== undefined ||
|
||||
typeof nowConfig.trailingSlash !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { basename, extname, join } from 'path';
|
||||
import { BuilderParams, BuildResult, ShouldServeParams } from './types';
|
||||
import {
|
||||
FileFsRef,
|
||||
BuildOptions,
|
||||
ShouldServeOptions,
|
||||
} from '@vercel/build-utils';
|
||||
import { BuildResult } from './types';
|
||||
|
||||
export const version = 2;
|
||||
|
||||
@@ -7,7 +12,7 @@ export function build({
|
||||
files,
|
||||
entrypoint,
|
||||
config,
|
||||
}: BuilderParams): BuildResult {
|
||||
}: BuildOptions): BuildResult {
|
||||
let path = entrypoint;
|
||||
const outputDir = config.zeroConfig ? config.outputDirectory : '';
|
||||
const outputMatch = outputDir + '/';
|
||||
@@ -16,7 +21,7 @@ export function build({
|
||||
path = path.slice(outputMatch.length);
|
||||
}
|
||||
const output = {
|
||||
[path]: files[entrypoint],
|
||||
[path]: files[entrypoint] as FileFsRef,
|
||||
};
|
||||
const watch = [path];
|
||||
|
||||
@@ -28,7 +33,7 @@ export function shouldServe({
|
||||
files,
|
||||
requestPath,
|
||||
config = {},
|
||||
}: ShouldServeParams) {
|
||||
}: ShouldServeOptions) {
|
||||
let outputPrefix = '';
|
||||
const outputDir = config.zeroConfig ? config.outputDirectory : '';
|
||||
const outputMatch = outputDir + '/';
|
||||
|
||||
@@ -3,11 +3,16 @@ import { ChildProcess } from 'child_process';
|
||||
import { Lambda as FunLambda } from '@zeit/fun';
|
||||
import {
|
||||
Builder as BuildConfig,
|
||||
BuildOptions,
|
||||
PrepareCacheOptions,
|
||||
ShouldServeOptions,
|
||||
StartDevServerOptions,
|
||||
StartDevServerResult,
|
||||
Env,
|
||||
FileBlob,
|
||||
FileFsRef,
|
||||
Lambda,
|
||||
PackageJson,
|
||||
Config,
|
||||
} from '@vercel/build-utils';
|
||||
import { NowConfig } from '@vercel/client';
|
||||
import { HandleValue, Route } from '@vercel/routing-utils';
|
||||
@@ -22,25 +27,21 @@ export interface DevServerOptions {
|
||||
frameworkSlug: string | null;
|
||||
}
|
||||
|
||||
export interface EnvConfig {
|
||||
[name: string]: string | undefined;
|
||||
}
|
||||
|
||||
export interface EnvConfigs {
|
||||
/**
|
||||
* environment variables from `.env.build` file (deprecated)
|
||||
*/
|
||||
buildEnv: EnvConfig;
|
||||
buildEnv: Env;
|
||||
|
||||
/**
|
||||
* environment variables from `.env` file
|
||||
*/
|
||||
runEnv: EnvConfig;
|
||||
runEnv: Env;
|
||||
|
||||
/**
|
||||
* environment variables from `.env` and `.env.build`
|
||||
*/
|
||||
allEnv: EnvConfig;
|
||||
allEnv: Env;
|
||||
}
|
||||
|
||||
export interface BuildMatch extends BuildConfig {
|
||||
@@ -52,8 +53,6 @@ export interface BuildMatch extends BuildConfig {
|
||||
buildProcess?: ChildProcess;
|
||||
}
|
||||
|
||||
export type RouteConfig = Route;
|
||||
|
||||
export interface HttpHandler {
|
||||
(req: http.IncomingMessage, res: http.ServerResponse): void;
|
||||
}
|
||||
@@ -62,9 +61,9 @@ export interface BuilderInputs {
|
||||
[path: string]: FileFsRef;
|
||||
}
|
||||
|
||||
export type BuiltLambda = Lambda & {
|
||||
export interface BuiltLambda extends Lambda {
|
||||
fn?: FunLambda;
|
||||
};
|
||||
}
|
||||
|
||||
export type BuilderOutput = BuiltLambda | FileFsRef | FileBlob;
|
||||
|
||||
@@ -78,28 +77,6 @@ export interface CacheOutputs {
|
||||
[path: string]: CacheOutput;
|
||||
}
|
||||
|
||||
export interface BuilderParamsBase {
|
||||
files: BuilderInputs;
|
||||
entrypoint: string;
|
||||
config: Config;
|
||||
meta?: {
|
||||
isDev?: boolean;
|
||||
requestPath?: string | null;
|
||||
filesChanged?: string[];
|
||||
filesRemoved?: string[];
|
||||
env?: EnvConfig;
|
||||
buildEnv?: EnvConfig;
|
||||
};
|
||||
}
|
||||
|
||||
export interface BuilderParams extends BuilderParamsBase {
|
||||
workPath: string;
|
||||
}
|
||||
|
||||
export interface PrepareCacheParams extends BuilderParams {
|
||||
cachePath: string;
|
||||
}
|
||||
|
||||
export interface BuilderConfigAttr {
|
||||
maxLambdaSize?: string | number;
|
||||
}
|
||||
@@ -108,49 +85,43 @@ export interface Builder {
|
||||
version?: 1 | 2 | 3 | 4;
|
||||
config?: BuilderConfigAttr;
|
||||
build(
|
||||
params: BuilderParams
|
||||
opts: BuildOptions
|
||||
):
|
||||
| BuilderOutputs
|
||||
| BuildResult
|
||||
| Promise<BuilderOutputs>
|
||||
| Promise<BuildResult>;
|
||||
shouldServe?(params: ShouldServeParams): boolean | Promise<boolean>;
|
||||
prepareCache?(
|
||||
params: PrepareCacheParams
|
||||
opts: PrepareCacheOptions
|
||||
): CacheOutputs | Promise<CacheOutputs>;
|
||||
shouldServe?(params: ShouldServeOptions): boolean | Promise<boolean>;
|
||||
startDevServer?(opts: StartDevServerOptions): Promise<StartDevServerResult>;
|
||||
}
|
||||
|
||||
export interface BuildResult {
|
||||
output: BuilderOutputs;
|
||||
routes: RouteConfig[];
|
||||
routes: Route[];
|
||||
watch: string[];
|
||||
distPath?: string;
|
||||
}
|
||||
|
||||
export interface BuildResultV3 {
|
||||
output: Lambda;
|
||||
routes: RouteConfig[];
|
||||
routes: Route[];
|
||||
watch: string[];
|
||||
distPath?: string;
|
||||
}
|
||||
|
||||
export interface BuildResultV4 {
|
||||
output: { [filePath: string]: Lambda };
|
||||
routes: RouteConfig[];
|
||||
routes: Route[];
|
||||
watch: string[];
|
||||
distPath?: string;
|
||||
}
|
||||
|
||||
export interface ShouldServeParams {
|
||||
files: BuilderInputs;
|
||||
entrypoint: string;
|
||||
config?: Config;
|
||||
requestPath: string;
|
||||
workPath: string;
|
||||
}
|
||||
|
||||
export interface BuilderWithPackage {
|
||||
runInProcess?: boolean;
|
||||
requirePath: string;
|
||||
builder: Readonly<Builder>;
|
||||
package: Readonly<PackageJson>;
|
||||
}
|
||||
@@ -173,7 +144,7 @@ export interface RouteResult {
|
||||
// "uri_args": <object (key=value) list of new uri args to be passed along to dest >
|
||||
uri_args?: { [key: string]: any };
|
||||
// "matched_route": <object of the route spec that matched>
|
||||
matched_route?: RouteConfig;
|
||||
matched_route?: Route;
|
||||
// "matched_route_idx": <integer of the index of the route matched>
|
||||
matched_route_idx?: number;
|
||||
// "userDest": <boolean in case the destination was user defined>
|
||||
|
||||
@@ -70,7 +70,9 @@ function validateKey(
|
||||
|
||||
const error = validate.errors[0];
|
||||
|
||||
return `Invalid \`${key}\` property: ${error.dataPath} ${error.message}`;
|
||||
return `Invalid \`${String(key)}\` property: ${error.dataPath} ${
|
||||
error.message
|
||||
}`;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
import { createHash } from 'crypto';
|
||||
import {
|
||||
mkdirp,
|
||||
createWriteStream,
|
||||
writeFile,
|
||||
statSync,
|
||||
chmodSync,
|
||||
createReadStream,
|
||||
} from 'fs-extra';
|
||||
import pipe from 'promisepipe';
|
||||
import { join } from 'path';
|
||||
import fetch from 'node-fetch';
|
||||
import { Output } from '../output/create-output';
|
||||
import { builderDirPromise } from './builder-cache';
|
||||
|
||||
const YARN_VERSION = '1.17.3';
|
||||
const YARN_SHA = '77f28b2793ca7d0ab5bd5da072afc423f7fdf733';
|
||||
const YARN_URL = `https://github.com/yarnpkg/yarn/releases/download/v${YARN_VERSION}/yarn-${YARN_VERSION}.js`;
|
||||
|
||||
function plusxSync(file: string): void {
|
||||
const s = statSync(file);
|
||||
const newMode = s.mode | 64 | 8 | 1;
|
||||
|
||||
if (s.mode === newMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const base8 = newMode.toString(8).slice(-3);
|
||||
chmodSync(file, base8);
|
||||
}
|
||||
|
||||
function getSha1(filePath: string): Promise<string | null> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const hash = createHash('sha1');
|
||||
const stream = createReadStream(filePath);
|
||||
stream.on('error', err => {
|
||||
if (err.code === 'ENOENT') {
|
||||
resolve(null);
|
||||
} else {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
stream.on('data', chunk => hash.update(chunk));
|
||||
stream.on('end', () => resolve(hash.digest('hex')));
|
||||
});
|
||||
}
|
||||
|
||||
async function installYarn(output: Output): Promise<string> {
|
||||
// Loosely based on https://yarnpkg.com/install.sh
|
||||
const dirName = await builderDirPromise;
|
||||
const yarnBin = join(dirName, 'yarn');
|
||||
const sha1 = await getSha1(yarnBin);
|
||||
|
||||
if (sha1 === YARN_SHA) {
|
||||
output.debug('The yarn executable is already cached, not re-downloading');
|
||||
return dirName;
|
||||
}
|
||||
|
||||
output.debug(`Creating directory ${dirName}`);
|
||||
await mkdirp(dirName);
|
||||
output.debug(`Finished creating ${dirName}`);
|
||||
|
||||
output.debug(`Downloading ${YARN_URL}`);
|
||||
const response = await fetch(YARN_URL, {
|
||||
compress: false,
|
||||
redirect: 'follow',
|
||||
});
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Received invalid response: ${await response.text()}`);
|
||||
}
|
||||
|
||||
const target = createWriteStream(yarnBin);
|
||||
await pipe(
|
||||
response.body,
|
||||
target
|
||||
);
|
||||
output.debug(`Finished downloading yarn ${yarnBin}`);
|
||||
|
||||
output.debug(`Making the yarn binary executable`);
|
||||
plusxSync(yarnBin);
|
||||
output.debug(`Finished making the yarn binary executable`);
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
// The `yarn.cmd` file is necessary for `yarn` to be executable
|
||||
// when running `now dev` through cmd.exe
|
||||
await writeFile(
|
||||
`${yarnBin}.cmd`,
|
||||
[
|
||||
'@echo off',
|
||||
'@SETLOCAL',
|
||||
'@SET PATHEXT=%PATHEXT:;.JS;=;%',
|
||||
'node "%~dp0\\yarn" %*',
|
||||
].join('\r\n')
|
||||
);
|
||||
}
|
||||
|
||||
return dirName;
|
||||
}
|
||||
|
||||
export async function getYarnPath(output: Output): Promise<string> {
|
||||
return installYarn(output);
|
||||
}
|
||||
@@ -1,25 +1,9 @@
|
||||
import getDNSRecords from './get-dns-records';
|
||||
import { DNSRecord } from '../../types';
|
||||
import { Output } from '../output';
|
||||
import Client from '../client';
|
||||
|
||||
type Result = {
|
||||
domainName: string,
|
||||
record: DNSRecord
|
||||
}
|
||||
|
||||
export default async function getDNSRecordById(
|
||||
output: Output,
|
||||
client: Client,
|
||||
contextName: string,
|
||||
id: string
|
||||
): Promise<Result | null> {
|
||||
const recordsByDomains = await getDNSRecords(output, client, contextName);
|
||||
return recordsByDomains.reduce((result: Result | null, { domainName, records }) => {
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
const record = records.find(record => record.id === id);
|
||||
return record ? { domainName, record } : null;
|
||||
}, null);
|
||||
): Promise<DNSRecord> {
|
||||
return client.fetch<DNSRecord>(`/v5/domains/records/${id}`);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import Client from '../client';
|
||||
import getDomainDNSRecords from './get-domain-dns-records';
|
||||
import getDomains from '../domains/get-domains';
|
||||
|
||||
type DomainRecordsItem = {
|
||||
export type DomainRecordsItem = {
|
||||
domainName: string;
|
||||
records: DNSRecord[];
|
||||
};
|
||||
@@ -13,16 +13,24 @@ type DomainRecordsItem = {
|
||||
export default async function getDNSRecords(
|
||||
output: Output,
|
||||
client: Client,
|
||||
contextName: string
|
||||
contextName: string,
|
||||
next?: number
|
||||
) {
|
||||
const domainNames = await getDomainNames(client, contextName);
|
||||
const { domainNames, pagination } = await getDomainNames(
|
||||
client,
|
||||
contextName,
|
||||
next
|
||||
);
|
||||
const domainsRecords = await Promise.all(
|
||||
domainNames.map(createGetDomainRecords(output, client))
|
||||
);
|
||||
const onlyRecords = domainsRecords.map(item =>
|
||||
item instanceof DomainNotFound ? [] : item
|
||||
) as DNSRecord[][];
|
||||
return onlyRecords.reduce(getAddDomainName(domainNames), []);
|
||||
return {
|
||||
records: onlyRecords.reduce(getAddDomainName(domainNames), []),
|
||||
pagination,
|
||||
};
|
||||
}
|
||||
|
||||
function createGetDomainRecords(output: Output, client: Client) {
|
||||
@@ -40,12 +48,16 @@ function getAddDomainName(domainNames: string[]) {
|
||||
...prev,
|
||||
{
|
||||
domainName: domainNames[idx],
|
||||
records: item.sort((a, b) => a.slug.localeCompare(b.slug)),
|
||||
records: item,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
async function getDomainNames(client: Client, contextName: string) {
|
||||
const { domains } = await getDomains(client, contextName);
|
||||
return domains.map(domain => domain.name).sort((a, b) => a.localeCompare(b));
|
||||
async function getDomainNames(
|
||||
client: Client,
|
||||
contextName: string,
|
||||
next?: number
|
||||
) {
|
||||
const { domains, pagination } = await getDomains(client, contextName, next);
|
||||
return { domainNames: domains.map(domain => domain.name), pagination };
|
||||
}
|
||||
|
||||
@@ -21,8 +21,7 @@ export default async function getEnvVariables(
|
||||
client: Client,
|
||||
projectId: string,
|
||||
apiVersion: 4,
|
||||
target?: ProjectEnvTarget,
|
||||
next?: number
|
||||
target?: ProjectEnvTarget
|
||||
): Promise<APIV4Response>;
|
||||
|
||||
export default async function getEnvVariables(
|
||||
|
||||
@@ -25,14 +25,25 @@ export default async function removeEnvRecord(
|
||||
if (env && env.value) {
|
||||
const idOrName = env.value.startsWith('@') ? env.value.slice(1) : env.value;
|
||||
const urlSecret = `/v2/now/secrets/${idOrName}`;
|
||||
const secret = await client.fetch<Secret>(urlSecret);
|
||||
let secret: Secret | undefined;
|
||||
|
||||
try {
|
||||
secret = await client.fetch<Secret>(urlSecret);
|
||||
} catch (error) {
|
||||
if (error && error.status === 404) {
|
||||
// User likely deleted the secret before the env var, so we can still report success
|
||||
output.debug(
|
||||
`Skipped ${env.key} because secret ${idOrName} was already deleted`
|
||||
);
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Since integrations add global secrets, we must only delete if the secret was
|
||||
// specifically added to this project
|
||||
if (secret && secret.projectId === projectId) {
|
||||
await client.fetch<Secret>(urlSecret, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
await client.fetch<Secret>(urlSecret, { method: 'DELETE' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import bytes from 'bytes';
|
||||
import { Response } from 'node-fetch';
|
||||
import { NowError } from './now-error';
|
||||
import param from './output/param';
|
||||
import cmd from './output/cmd';
|
||||
import code from './output/code';
|
||||
import { getPkgName } from './pkg-name';
|
||||
import { getCommandName } from './pkg-name';
|
||||
|
||||
/**
|
||||
* This error is thrown when there is an API error with a payload. The error
|
||||
@@ -52,8 +50,8 @@ export class TeamDeleted extends NowError<'TEAM_DELETED', {}> {
|
||||
constructor() {
|
||||
super({
|
||||
code: 'TEAM_DELETED',
|
||||
message: `Your team was deleted. You can switch to a different one using ${param(
|
||||
`${getPkgName()} switch`
|
||||
message: `Your team was deleted. You can switch to a different one using ${getCommandName(
|
||||
`switch`
|
||||
)}.`,
|
||||
meta: {},
|
||||
});
|
||||
@@ -143,8 +141,8 @@ export class SourceNotFound extends NowError<'SOURCE_NOT_FOUND', {}> {
|
||||
super({
|
||||
code: 'SOURCE_NOT_FOUND',
|
||||
meta: {},
|
||||
message: `Not able to purchase. Please add a payment method using ${cmd(
|
||||
`${getPkgName()} billing add`
|
||||
message: `Not able to purchase. Please add a payment method using ${getCommandName(
|
||||
`billing add`
|
||||
)}.`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import path from 'path';
|
||||
import { fileNameSymbol } from '@vercel/client';
|
||||
import {
|
||||
CantParseJSONFile,
|
||||
CantFindConfig,
|
||||
@@ -7,15 +8,15 @@ import {
|
||||
import humanizePath from './humanize-path';
|
||||
import readJSONFile from './read-json-file';
|
||||
import readPackage from './read-package';
|
||||
import { Config } from '../types';
|
||||
import { NowConfig } from './dev/types';
|
||||
import { Output } from './output';
|
||||
|
||||
let config: Config;
|
||||
let config: NowConfig;
|
||||
|
||||
export default async function getConfig(
|
||||
output: Output,
|
||||
configFile?: string
|
||||
): Promise<Config | Error> {
|
||||
): Promise<NowConfig | Error> {
|
||||
// If config was already read, just return it
|
||||
if (config) {
|
||||
return config;
|
||||
@@ -42,7 +43,8 @@ export default async function getConfig(
|
||||
return localConfig;
|
||||
}
|
||||
if (localConfig !== null) {
|
||||
config = localConfig;
|
||||
config = localConfig as NowConfig;
|
||||
config[fileNameSymbol] = configFile;
|
||||
return config;
|
||||
}
|
||||
}
|
||||
@@ -55,7 +57,8 @@ export default async function getConfig(
|
||||
}
|
||||
if (vercelConfig !== null) {
|
||||
output.debug(`Found config in file ${vercelFilePath}`);
|
||||
config = vercelConfig;
|
||||
config = vercelConfig as NowConfig;
|
||||
config[fileNameSymbol] = 'vercel.json';
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -67,7 +70,8 @@ export default async function getConfig(
|
||||
}
|
||||
if (mainConfig !== null) {
|
||||
output.debug(`Found config in file ${nowFilePath}`);
|
||||
config = mainConfig;
|
||||
config = mainConfig as NowConfig;
|
||||
config[fileNameSymbol] = 'now.json';
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -78,13 +82,16 @@ export default async function getConfig(
|
||||
return pkgConfig;
|
||||
}
|
||||
if (pkgConfig) {
|
||||
output.debug(`Found config in package ${nowFilePath}`);
|
||||
config = pkgConfig;
|
||||
output.debug(`Found config in package ${pkgFilePath}`);
|
||||
config = pkgConfig as NowConfig;
|
||||
config[fileNameSymbol] = 'package.json';
|
||||
return config;
|
||||
}
|
||||
|
||||
// If we couldn't find the config anywhere return error
|
||||
return new CantFindConfig([nowFilePath, pkgFilePath].map(humanizePath));
|
||||
return new CantFindConfig(
|
||||
[vercelFilePath, nowFilePath, pkgFilePath].map(humanizePath)
|
||||
);
|
||||
}
|
||||
|
||||
async function readConfigFromPackage(file: string) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Stats } from 'fs';
|
||||
import { sep, dirname, join, resolve } from 'path';
|
||||
import { readJSON, lstat, readlink, readFile, realpath } from 'fs-extra';
|
||||
import { isCanary } from './is-canary';
|
||||
import { getPkgName } from './pkg-name';
|
||||
|
||||
// `npm` tacks a bunch of extra properties on the `package.json` file,
|
||||
// so check for one of them to determine yarn vs. npm.
|
||||
@@ -93,12 +94,15 @@ async function isGlobal() {
|
||||
|
||||
export default async function getUpdateCommand(): Promise<string> {
|
||||
const tag = isCanary() ? 'canary' : 'latest';
|
||||
const pkgAndVersion = `${getPkgName()}@${tag}`;
|
||||
|
||||
if (await isGlobal()) {
|
||||
return (await isYarn())
|
||||
? `yarn global add now@${tag}`
|
||||
: `npm i -g now@${tag}`;
|
||||
? `yarn global add ${pkgAndVersion}`
|
||||
: `npm i -g ${pkgAndVersion}`;
|
||||
}
|
||||
|
||||
return (await isYarn()) ? `yarn add now@${tag}` : `npm i now@${tag}`;
|
||||
return (await isYarn())
|
||||
? `yarn add ${pkgAndVersion}`
|
||||
: `npm i ${pkgAndVersion}`;
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export const isOfficialRuntime = (desired: string, name?: string): boolean => {
|
||||
return (
|
||||
typeof name === 'string' &&
|
||||
(name.startsWith(`@vercel/${desired}`) ||
|
||||
name.startsWith(`@now/${desired}`))
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import crypto from 'crypto';
|
||||
import ua from 'universal-analytics';
|
||||
import { platform, release, userInfo } from 'os';
|
||||
import { getPlatformEnv } from '@vercel/build-utils';
|
||||
|
||||
import userAgent from './ua-browser';
|
||||
import { GA_TRACKING_ID } from './constants';
|
||||
@@ -10,7 +11,7 @@ const config: any = configFiles.getConfigFilePath();
|
||||
|
||||
export const shouldCollectMetrics =
|
||||
(config.collectMetrics === undefined || config.collectMetrics === true) &&
|
||||
process.env.NOW_CLI_COLLECT_METRICS !== '0' &&
|
||||
getPlatformEnv('CLI_COLLECT_METRICS') !== '0' &&
|
||||
GA_TRACKING_ID;
|
||||
|
||||
export const metrics = (): ua.Visitor => {
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
export function once<T>(emitter: EventEmitter, name: string): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
function cleanup() {
|
||||
emitter.removeListener(name, onEvent);
|
||||
emitter.removeListener('error', onError);
|
||||
}
|
||||
function onEvent(arg: T) {
|
||||
cleanup();
|
||||
resolve(arg);
|
||||
}
|
||||
function onError(err: Error) {
|
||||
cleanup();
|
||||
reject(err);
|
||||
}
|
||||
emitter.on(name, onEvent);
|
||||
emitter.on('error', onError);
|
||||
});
|
||||
}
|
||||
@@ -1,13 +1,36 @@
|
||||
// @ts-ignore
|
||||
import title from 'title';
|
||||
|
||||
import pkg from './pkg';
|
||||
import cmd from './output/cmd';
|
||||
|
||||
/**
|
||||
* Returns the package name such as `vercel` or `now`.
|
||||
*/
|
||||
export function getPkgName(): string {
|
||||
if (!pkg.name) {
|
||||
throw new Error('Expected package.json to have a `name` property.');
|
||||
throw new Error('Expected `package.json` to have a `name` property.');
|
||||
}
|
||||
return pkg.name;
|
||||
}
|
||||
|
||||
export function getCommandName() {
|
||||
return cmd(getPkgName());
|
||||
/**
|
||||
* Returns the package name with title-case
|
||||
* such as `Vercel` or `Now`.
|
||||
*/
|
||||
export function getTitleName(): string {
|
||||
const str = getPkgName();
|
||||
return title(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the package name with subcommand(s)
|
||||
* as a suffix such as `vercel env pull` or `now env pull`.
|
||||
*/
|
||||
export function getCommandName(subcommands?: string): string {
|
||||
let vercel = getPkgName();
|
||||
if (subcommands) {
|
||||
vercel = `${vercel} ${subcommands}`;
|
||||
}
|
||||
return cmd(vercel);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import path from 'path';
|
||||
import pkg from '../../package.json';
|
||||
import _pkg from '../../package.json';
|
||||
import { PackageJson } from '@vercel/build-utils';
|
||||
|
||||
try {
|
||||
const distDir = path.dirname(process.execPath);
|
||||
// @ts-ignore
|
||||
pkg._npmPkg = require(`${path.join(distDir, '../../package.json')}`);
|
||||
} catch (err) {
|
||||
// @ts-ignore
|
||||
pkg._npmPkg = null;
|
||||
}
|
||||
const pkg: PackageJson = _pkg;
|
||||
|
||||
export default pkg;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { exists } from 'fs-extra';
|
||||
import { PackageJson } from '@vercel/build-utils';
|
||||
|
||||
import Client from './client';
|
||||
import { Config } from '../types';
|
||||
import { NowConfig } from './dev/types';
|
||||
import { CantParseJSONFile, ProjectNotFound } from './errors-ts';
|
||||
import getProjectByIdOrName from './projects/get-project-by-id-or-name';
|
||||
|
||||
@@ -33,7 +33,7 @@ export default async function preferV2Deployment({
|
||||
hasDockerfile: boolean;
|
||||
hasServerfile: boolean;
|
||||
pkg: PackageJson | CantParseJSONFile | null;
|
||||
localConfig: Config | undefined;
|
||||
localConfig: NowConfig | undefined;
|
||||
projectName?: string;
|
||||
}): Promise<null | string> {
|
||||
if (localConfig && localConfig.version) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import chalk from 'chalk';
|
||||
import { prependEmoji, emoji } from '../emoji';
|
||||
import AJV from 'ajv';
|
||||
import { isDirectory } from '../config/global-path';
|
||||
import { getPlatformEnv } from '@vercel/build-utils';
|
||||
|
||||
const readFile = promisify(fs.readFile);
|
||||
const writeFile = promisify(fs.writeFile);
|
||||
@@ -38,11 +39,17 @@ const linkSchema = {
|
||||
},
|
||||
};
|
||||
|
||||
async function getLink(path?: string): Promise<ProjectLink | null> {
|
||||
const cwd = path || process.cwd();
|
||||
/**
|
||||
* Returns the `<cwd>/.vercel` directory for the current project
|
||||
* with a fallback to <cwd>/.now` if it exists.
|
||||
*/
|
||||
export function getVercelDirectory(cwd: string = process.cwd()) {
|
||||
const possibleDirs = [join(cwd, VERCEL_DIR), join(cwd, VERCEL_DIR_FALLBACK)];
|
||||
return possibleDirs.find(d => isDirectory(d)) || possibleDirs[0];
|
||||
}
|
||||
|
||||
const dir = possibleDirs.find(d => isDirectory(d)) || possibleDirs[0];
|
||||
async function getLink(path?: string): Promise<ProjectLink | null> {
|
||||
const dir = getVercelDirectory(path);
|
||||
return getLinkFromDir(dir);
|
||||
}
|
||||
|
||||
@@ -98,7 +105,8 @@ export async function getLinkedProject(
|
||||
| { status: 'not_linked'; org: null; project: null }
|
||||
| { status: 'error'; exitCode: number }
|
||||
> {
|
||||
const { VERCEL_ORG_ID, VERCEL_PROJECT_ID } = getFlags();
|
||||
const VERCEL_ORG_ID = getPlatformEnv('ORG_ID');
|
||||
const VERCEL_PROJECT_ID = getPlatformEnv('PROJECT_ID');
|
||||
const shouldUseEnv = Boolean(VERCEL_ORG_ID && VERCEL_PROJECT_ID);
|
||||
|
||||
if ((VERCEL_ORG_ID || VERCEL_PROJECT_ID) && !shouldUseEnv) {
|
||||
@@ -157,20 +165,6 @@ export async function getLinkedProject(
|
||||
return { status: 'linked', org, project };
|
||||
}
|
||||
|
||||
export function getFlags() {
|
||||
let {
|
||||
VERCEL_ORG_ID,
|
||||
VERCEL_PROJECT_ID,
|
||||
NOW_ORG_ID,
|
||||
NOW_PROJECT_ID,
|
||||
} = process.env;
|
||||
|
||||
// Fallback to old NOW env vars if available
|
||||
VERCEL_ORG_ID = VERCEL_ORG_ID || NOW_ORG_ID;
|
||||
VERCEL_PROJECT_ID = VERCEL_PROJECT_ID || NOW_PROJECT_ID;
|
||||
return { VERCEL_ORG_ID, VERCEL_PROJECT_ID };
|
||||
}
|
||||
|
||||
export async function linkFolderToProject(
|
||||
output: Output,
|
||||
path: string,
|
||||
@@ -178,7 +172,8 @@ export async function linkFolderToProject(
|
||||
projectName: string,
|
||||
orgSlug: string
|
||||
) {
|
||||
const { VERCEL_ORG_ID, VERCEL_PROJECT_ID } = getFlags();
|
||||
const VERCEL_ORG_ID = getPlatformEnv('ORG_ID');
|
||||
const VERCEL_PROJECT_ID = getPlatformEnv('PROJECT_ID');
|
||||
|
||||
// if defined, skip linking
|
||||
if (VERCEL_ORG_ID || VERCEL_PROJECT_ID) {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import path from 'path';
|
||||
import { CantParseJSONFile } from './errors-ts';
|
||||
import readJSONFile from './read-json-file';
|
||||
import { Config } from '../types';
|
||||
import { NowConfig } from './dev/types';
|
||||
import { PackageJson } from '@vercel/build-utils';
|
||||
|
||||
interface CustomPackage extends PackageJson {
|
||||
now?: Config;
|
||||
now?: NowConfig;
|
||||
}
|
||||
|
||||
export default async function readPackage(file?: string) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Now from './index';
|
||||
import { URLSearchParams } from 'url';
|
||||
|
||||
export default class Teams extends Now {
|
||||
async create({ slug }) {
|
||||
@@ -101,9 +102,16 @@ export default class Teams extends Now {
|
||||
});
|
||||
}
|
||||
|
||||
async ls() {
|
||||
async ls({ next, apiVersion = 1 } = {}) {
|
||||
return this.retry(async bail => {
|
||||
const res = await this._fetch(`/teams`);
|
||||
const query = new URLSearchParams();
|
||||
|
||||
if (next) {
|
||||
query.set('limit', 20);
|
||||
query.set('until', next);
|
||||
}
|
||||
|
||||
const res = await this._fetch(`/v${apiVersion}/teams?${query}`);
|
||||
|
||||
if (res.status === 403) {
|
||||
const error = new Error('Unauthorized');
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user