Compare commits

..

8 Commits

Author SHA1 Message Date
Andy Bitz
9f05a1865c Publish Stable
- @vercel/frameworks@0.0.17
 - @vercel/build-utils@2.4.2
 - vercel@19.2.0
 - @vercel/client@8.2.1
 - @vercel/go@1.1.4
 - @vercel/next@2.6.13
 - @vercel/node@1.7.3
 - @vercel/static-build@0.17.6
2020-07-21 14:58:27 +02:00
Andy
8d1afc026f Revert "[go] Implement startDevServer() function (#4662)" (#4899)
This reverts commit 04bea1e3cd.
2020-07-21 14:55:59 +02:00
Nathan Rajlich
130f36aad6 Publish Canary
- vercel@19.1.3-canary.6
2020-07-20 23:11:46 -07:00
Nathan Rajlich
dd87c9b0c6 Publish Canary
- vercel@19.1.3-canary.5
 - @vercel/next@2.6.13-canary.2
2020-07-20 19:51:15 -07:00
Max Leiter
f813b3340b [cli] Fetch environment variables from Project Settings if .env file is not present (#4562)
Previously, users would have to run `vc env pull` to fetch cloud environment
variables into `.env`. After this PR, if no `.env` or `.build.env` file is present,
environment variables will be pulled by `vc dev` from your Vercel Environment
Variables settings, no file necessary.
2020-07-20 19:47:13 -07:00
Joe Haddad
976b02e895 [next] Add tests for trailing slash behavior (#4894) 2020-07-20 22:29:09 -04:00
Nathan Rajlich
843be9658c [cli] Use update-notifier instead of update-check (#4896)
This makes the updating logic be asynchronous instead of synchronous, and as such will make boot-up of CLI be faster.

The actual update notification display is identical to previous, we are not using `update-notifier`'s default boxen rendering.
2020-07-20 22:31:15 +00:00
dependabot[bot]
ad501a4cd0 Bump lodash from 4.17.15 to 4.17.19 (#4877)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-07-20 14:00:32 -04:00
88 changed files with 709 additions and 663 deletions

View File

@@ -19,11 +19,6 @@ indent_style = space
[*.py]
indent_size = 4
[*.go]
indent_style = tab
indent_size = 4
tab_width = 4
[*.asm]
indent_size = 8

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/frameworks",
"version": "0.0.17-canary.2",
"version": "0.0.17",
"main": "frameworks.json",
"license": "UNLICENSED",
"scripts": {

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "2.4.2-canary.1",
"version": "2.4.2",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "19.1.3-canary.4",
"version": "19.2.0",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -62,13 +62,14 @@
"node": ">= 10"
},
"dependencies": {
"@vercel/build-utils": "2.4.2-canary.1",
"@vercel/go": "1.1.4-canary.0",
"@vercel/next": "2.6.13-canary.1",
"@vercel/node": "1.7.3-canary.0",
"@vercel/build-utils": "2.4.2",
"@vercel/go": "1.1.4",
"@vercel/next": "2.6.13",
"@vercel/node": "1.7.3",
"@vercel/python": "1.2.2",
"@vercel/ruby": "1.2.3",
"@vercel/static-build": "0.17.6-canary.1"
"@vercel/static-build": "0.17.6",
"update-notifier": "4.1.0"
},
"devDependencies": {
"@sentry/node": "5.5.0",
@@ -185,7 +186,6 @@
"ts-node": "8.3.0",
"typescript": "3.9.3",
"universal-analytics": "0.4.20",
"update-check": "1.5.3",
"utility-types": "2.1.0",
"which": "2.0.2",
"which-promise": "1.0.0",

View File

@@ -49,7 +49,13 @@ async function main() {
// Do the initial `ncc` build
console.log();
const src = join(dirRoot, 'src');
const args = ['@zeit/ncc', 'build', '--source-map'];
const args = [
'@zeit/ncc',
'build',
'--source-map',
'--external',
'update-notifier',
];
if (!isDev) {
args.push('--minify');
}
@@ -86,7 +92,7 @@ async function main() {
// A bunch of source `.ts` files from CLI's `util` directory
await remove(join(dirRoot, 'dist', 'util'));
console.log('Finished building `now-cli`');
console.log('Finished building Vercel CLI');
}
process.on('unhandledRejection', (reason: any, promise: Promise<any>) => {

View File

@@ -9,7 +9,9 @@ import { getLinkedProject } from '../../util/projects/link';
import { getFrameworks } from '../../util/get-frameworks';
import { isSettingValue } from '../../util/is-setting-value';
import { getCommandName } from '../../util/pkg-name';
import { ProjectSettings } from '../../types';
import { ProjectSettings, ProjectEnvTarget } from '../../types';
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
import { Env } from '@vercel/build-utils';
type Options = {
'--debug'?: boolean;
@@ -54,6 +56,7 @@ export default async function dev(
let devCommand: string | undefined;
let frameworkSlug: string | undefined;
let projectSettings: ProjectSettings | undefined;
let environmentVars: Env | undefined;
if (link.status === 'linked') {
const { project, org } = link;
client.currentTeam = org.type === 'team' ? org.id : undefined;
@@ -80,6 +83,13 @@ export default async function dev(
if (project.rootDirectory) {
cwd = join(cwd, project.rootDirectory);
}
environmentVars = await getDecryptedEnvRecords(
output,
client,
project,
ProjectEnvTarget.Development
);
}
const devServer = new DevServer(cwd, {
@@ -88,6 +98,7 @@ export default async function dev(
devCommand,
frameworkSlug,
projectSettings,
environmentVars,
});
process.once('SIGINT', () => devServer.stop());

View File

@@ -4,8 +4,7 @@ import { Output } from '../../util/output';
import promptBool from '../../util/prompt-bool';
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 getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
import param from '../../util/output/param';
import withSpinner from '../../util/with-spinner';
import { join } from 'path';
@@ -13,6 +12,7 @@ import { promises, openSync, closeSync, readSync } from 'fs';
import { emoji, prependEmoji } from '../../util/emoji';
import { getCommandName } from '../../util/pkg-name';
const { writeFile } = promises;
import { Env } from '@vercel/build-utils';
const CONTENTS_PREFIX = '# Created by Vercel CLI\n';
@@ -84,45 +84,21 @@ export default async function pull(
);
const pullStamp = stamp();
const records = await withSpinner('Downloading', async () => {
const dev = ProjectEnvTarget.Development;
const envs = await getEnvVariables(output, client, project.id, 4, dev);
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; 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 records: Env = await withSpinner(
'Downloading',
async () =>
await getDecryptedEnvRecords(
output,
client,
project,
ProjectEnvTarget.Development
)
);
const contents =
CONTENTS_PREFIX +
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)}"`)
Object.entries(records)
.map(([key, value]) => `${key}="${escapeValue(value)}"`)
.join('\n') +
'\n';
@@ -139,8 +115,10 @@ export default async function pull(
return 0;
}
function escapeValue(value: string) {
function escapeValue(value: string | undefined) {
return value
.replace(new RegExp('\n', 'g'), '\\n') // combine newlines (unix) into one line
.replace(new RegExp('\r', 'g'), '\\r'); // combine newlines (windows) into one line
? value
.replace(new RegExp('\n', 'g'), '\\n') // combine newlines (unix) into one line
.replace(new RegExp('\r', 'g'), '\\r') // combine newlines (windows) into one line
: '';
}

View File

@@ -20,8 +20,7 @@ import sourceMap from '@zeit/source-map-support';
import { mkdirp } from 'fs-extra';
import chalk from 'chalk';
import epipebomb from 'epipebomb';
import checkForUpdate from 'update-check';
import ms from 'ms';
import updateNotifier from 'update-notifier';
import { URL } from 'url';
import * as Sentry from '@sentry/node';
import { NowBuildError } from '@vercel/build-utils';
@@ -52,6 +51,14 @@ import getUpdateCommand from './util/get-update-command';
import { metrics, shouldCollectMetrics } from './util/metrics.ts';
import { getCommandName, getTitleName } from './util/pkg-name.ts';
const isCanary = pkg.version.includes('canary');
// Checks for available update and returns an instance
const notifier = updateNotifier({
pkg,
distTag: isCanary ? 'canary' : 'latest',
});
const VERCEL_DIR = getGlobalPathConfig();
const VERCEL_CONFIG_PATH = configFiles.getConfigFilePath();
const VERCEL_AUTH_CONFIG_PATH = configFiles.getAuthConfigFilePath();
@@ -66,7 +73,7 @@ sourceMap.install();
Sentry.init({
dsn: SENTRY_DSN,
release: `vercel-cli@${pkg.version}`,
environment: pkg.version.includes('canary') ? 'canary' : 'stable',
environment: isCanary ? 'canary' : 'stable',
});
let debug = () => {};
@@ -128,38 +135,20 @@ const main = async argv_ => {
// (as in: `vercel ls`)
const targetOrSubcommand = argv._[2];
let update = null;
try {
if (targetOrSubcommand !== 'update') {
update = await checkForUpdate(pkg, {
interval: ms('1d'),
distTag: pkg.version.includes('canary') ? 'canary' : 'latest',
});
}
} catch (err) {
console.error(
error(`Checking for updates failed${isDebugging ? ':' : ''}`)
);
if (isDebugging) {
console.error(err);
}
}
if (update && isTTY) {
if (notifier.update && isTTY) {
const { latest } = notifier.update;
console.log(
info(
`${chalk.bgRed('UPDATE AVAILABLE')} ` +
`Run ${cmd(
await getUpdateCommand()
)} to install ${getTitleName()} CLI ${update.latest}`
)} to install ${getTitleName()} CLI ${latest}`
)
);
console.log(
info(
`Changelog: https://github.com/vercel/vercel/releases/tag/vercel@${update.latest}`
`Changelog: https://github.com/vercel/vercel/releases/tag/vercel@${latest}`
)
);
}
@@ -169,7 +158,7 @@ const main = async argv_ => {
`${getTitleName()} CLI ${pkg.version}${
targetOrSubcommand === 'dev' ? ' dev (beta)' : ''
}${
pkg.version.includes('canary') || targetOrSubcommand === 'dev'
isCanary || targetOrSubcommand === 'dev'
? ' — https://vercel.com/feedback'
: ''
}`
@@ -191,9 +180,7 @@ const main = async argv_ => {
} catch (err) {
console.error(
error(
`An unexpected error occurred while trying to find the global directory: ${
err.message
}`
`An unexpected error occurred while trying to find the global directory: ${err.message}`
)
);

View File

@@ -140,12 +140,16 @@ export default class DevServer {
private blockingBuildsPromise: Promise<void> | null;
private updateBuildersPromise: Promise<void> | null;
private updateBuildersTimeout: NodeJS.Timeout | undefined;
private startPromise: Promise<void> | null;
private environmentVars: Env | undefined;
constructor(cwd: string, options: DevServerOptions) {
this.cwd = cwd;
this.debug = options.debug;
this.output = options.output;
this.envConfigs = { buildEnv: {}, runEnv: {}, allEnv: {} };
this.environmentVars = options.environmentVars;
this.files = {};
this.address = '';
this.devCommand = options.devCommand;
@@ -169,6 +173,7 @@ export default class DevServer {
this.getNowConfigPromise = null;
this.blockingBuildsPromise = null;
this.updateBuildersPromise = null;
this.startPromise = null;
this.watchAggregationId = null;
this.watchAggregationEvents = [];
@@ -479,22 +484,15 @@ export default class DevServer {
const dotenv = await fs.readFile(filePath, 'utf8');
this.output.debug(`Using local env: ${filePath}`);
env = parseDotenv(dotenv);
env = this.populateVercelEnvVars(env);
} catch (err) {
if (err.code !== 'ENOENT') {
throw err;
}
}
try {
let host = '';
if (this.address) {
host = new URL(this.address).host;
}
return {
...this.validateEnvConfig(fileName, base || {}, env),
NOW_REGION: 'dev1',
NOW_URL: host,
VERCEL_REGION: 'dev1',
VERCEL_URL: host,
};
} catch (err) {
if (err instanceof MissingDotenvVarsError) {
@@ -630,13 +628,20 @@ export default class DevServer {
this.apiExtensions = detectApiExtensions(config.builds || []);
// Update the env vars configuration
const [runEnv, buildEnv] = await Promise.all([
let [runEnv, buildEnv] = await Promise.all([
this.getLocalEnv('.env', config.env),
this.getLocalEnv('.env.build', config.build?.env),
]);
const allEnv = { ...buildEnv, ...runEnv };
this.envConfigs = { buildEnv, runEnv, allEnv };
let allEnv = { ...buildEnv, ...runEnv };
// If no .env/.build.env is present, fetch and use cloud environment variables
if (Object.keys(allEnv).length === 0) {
const cloudEnv = this.populateVercelEnvVars(this.environmentVars);
allEnv = runEnv = buildEnv = cloudEnv;
}
this.envConfigs = { buildEnv, runEnv, allEnv };
return config;
}
@@ -741,6 +746,26 @@ export default class DevServer {
return merged;
}
populateVercelEnvVars(env: Env | undefined): Env {
if (!env) {
return {};
}
for (const name of Object.keys(env)) {
if (name === 'VERCEL_URL') {
const host = new URL(this.address).host;
env['VERCEL_URL'] = host;
} else if (name === 'VERCEL_REGION') {
env['VERCEL_REGION'] = 'dev1';
}
}
// Always set NOW_REGION to match production
env['NOW_REGION'] = 'dev1';
return env;
}
/**
* Create an array of from builder inputs
* and filter them
@@ -749,10 +774,20 @@ export default class DevServer {
return Object.keys(files).filter(this.filter);
}
start(...listenSpec: ListenSpec): Promise<void> {
if (!this.startPromise) {
this.startPromise = this._start(...listenSpec).catch(err => {
this.stop();
throw err;
});
}
return this.startPromise;
}
/**
* Launches the `vercel dev` server.
*/
async start(...listenSpec: ListenSpec): Promise<void> {
async _start(...listenSpec: ListenSpec): Promise<void> {
if (!fs.existsSync(this.cwd)) {
throw new Error(`${chalk.bold(this.cwd)} doesn't exist`);
}
@@ -764,7 +799,39 @@ export default class DevServer {
const { ig } = await getVercelIgnore(this.cwd);
this.filter = ig.createFilter();
let address: string | null = null;
while (typeof address !== 'string') {
try {
address = await listen(this.server, ...listenSpec);
} catch (err) {
this.output.debug(`Got listen error: ${err.code}`);
if (err.code === 'EADDRINUSE') {
if (typeof listenSpec[0] === 'number') {
// Increase port and try again
this.output.note(
`Requested port ${chalk.yellow(
String(listenSpec[0])
)} is already in use`
);
listenSpec[0]++;
} else {
this.output.error(
`Requested socket ${chalk.cyan(listenSpec[0])} is already in use`
);
process.exit(1);
}
} else {
throw err;
}
}
}
this.address = address
.replace('[::]', 'localhost')
.replace('127.0.0.1', 'localhost');
const nowConfig = await this.getNowConfig();
const devCommandPromise = this.runDevCommand();
const opts = { output: this.output, isBuilds: true };
const files = await getFiles(this.cwd, nowConfig, opts);
@@ -856,39 +923,6 @@ export default class DevServer {
this.proxy.ws(req, socket, head, { target });
});
const devCommandPromise = this.runDevCommand();
let address: string | null = null;
while (typeof address !== 'string') {
try {
address = await listen(this.server, ...listenSpec);
} catch (err) {
this.output.debug(`Got listen error: ${err.code}`);
if (err.code === 'EADDRINUSE') {
if (typeof listenSpec[0] === 'number') {
// Increase port and try again
this.output.note(
`Requested port ${chalk.yellow(
String(listenSpec[0])
)} is already in use`
);
listenSpec[0]++;
} else {
this.output.error(
`Requested socket ${chalk.cyan(listenSpec[0])} is already in use`
);
process.exit(1);
}
} else {
throw err;
}
}
}
this.address = address
.replace('[::]', 'localhost')
.replace('127.0.0.1', 'localhost');
await devCommandPromise;
this.output.ready(`Available at ${link(this.address)}`);
@@ -1210,6 +1244,8 @@ export default class DevServer {
req: http.IncomingMessage,
res: http.ServerResponse
) => {
await this.startPromise;
let nowRequestId = generateRequestId(this.podId);
if (this.stopping) {

View File

@@ -27,11 +27,12 @@ export interface DevServerOptions {
devCommand?: string;
frameworkSlug?: string;
projectSettings?: ProjectSettings;
environmentVars?: Env;
}
export interface EnvConfigs {
/**
* environment variables from `.env.build` file (deprecated)
* environment variables from `.env.build` file
*/
buildEnv: Env;

View File

@@ -0,0 +1,46 @@
import getEnvVariables from './env/get-env-records';
import getDecryptedSecret from './env/get-decrypted-secret';
import Client from './client';
import { Output } from './output/create-output';
import { ProjectEnvTarget, Project } from '../types';
import { Env } from '@vercel/build-utils';
export default async function getDecryptedEnvRecords(
output: Output,
client: Client,
project: Project,
target: ProjectEnvTarget
): Promise<Env> {
const envs = await getEnvVariables(output, client, project.id, 4, target);
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: Env = {};
for (let i = 0; i < decryptedValues.length; i++) {
const { key } = envs[i];
const { value, found } = decryptedValues[i];
if (!found) {
output.print('');
output.warn(
`Unable to download variable ${key} because associated secret was deleted`
);
continue;
}
results[key] = value ? value : '';
}
return results;
}

View File

@@ -1,7 +1,7 @@
{
"functions": {
"api/user.sh": {
"runtime": "vercel-bash@3.0.7"
"runtime": "vercel-bash@3.0.8"
}
}
}

View File

@@ -1 +0,0 @@
.vercel

View File

@@ -1,10 +0,0 @@
package handler
import (
"fmt"
"net/http"
)
func Handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Req Path: %s", r.URL.Path)
}

View File

@@ -1,10 +0,0 @@
package another
import (
"fmt"
"net/http"
)
func Another(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "This is another page")
}

View File

@@ -1,10 +0,0 @@
package handler
import (
"fmt"
"net/http"
)
func Handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "This is the index page")
}

View File

@@ -0,0 +1,3 @@
# Created by Vercel CLI
VERCEL_REGION=""
VERCEL_URL=""

View File

@@ -2,7 +2,6 @@ import ms from 'ms';
import os from 'os';
import fs from 'fs-extra';
import test from 'ava';
import { isIP } from 'net';
import { join, resolve, delimiter } from 'path';
import _execa from 'execa';
import fetch from 'node-fetch';
@@ -1601,49 +1600,3 @@ test(
await testPath(200, '/index.css', 'This is index.css');
})
);
test(
'[vercel dev] Should support `*.go` API serverless functions',
testFixtureStdio('go', async testPath => {
await testPath(200, `/api`, 'This is the index page');
await testPath(200, `/api/index`, 'This is the index page');
await testPath(200, `/api/index.go`, 'This is the index page');
await testPath(200, `/api/another`, 'This is another page');
await testPath(200, '/api/another.go', 'This is another page');
await testPath(200, `/api/foo`, 'Req Path: /api/foo');
await testPath(200, `/api/bar`, 'Req Path: /api/bar');
})
);
test(
'[vercel dev] Should set the `ts-node` "target" to match Node.js version',
testFixtureStdio('node-ts-node-target', async testPath => {
await testPath(200, `/api/subclass`, '{"ok":true}');
await testPath(
200,
`/api/array`,
'{"months":[1,2,3,4,5,6,7,8,9,10,11,12]}'
);
await testPath(200, `/api/dump`, (t, body, res, isDev) => {
const { host } = new URL(res.url);
const { env, headers } = JSON.parse(body);
// Test that the API endpoint receives the Vercel proxy request headers
t.is(headers['x-forwarded-host'], host);
t.is(headers['x-vercel-deployment-url'], host);
t.truthy(isIP(headers['x-real-ip']));
t.truthy(isIP(headers['x-forwarded-for']));
t.truthy(isIP(headers['x-vercel-forwarded-for']));
// Test that the API endpoint has the Vercel platform env vars defined.
t.regex(env.NOW_REGION, /^[a-z]{3}\d$/);
if (isDev) {
// Only dev is tested because in production these are opt-in.
t.is(env.NOW_URL, host);
t.is(env.VERCEL_URL, host);
t.is(env.VERCEL_REGION, 'dev1');
}
});
})
);

View File

@@ -365,7 +365,7 @@ CMD ["node", "index.js"]`,
'package.json': JSON.stringify({
private: true,
scripts: {
build: 'mkdir public && node print.js > public/index.json',
build: 'mkdir -p public && node print.js > public/index.json',
},
}),
},

View File

@@ -593,7 +593,86 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
t.is(homeRes.status, 200, formatOutput({ stderr, stdout }));
const homeJson = await homeRes.json();
t.is(homeJson['MY_ENV_VAR'], 'MY_VALUE');
t.is(apiJson['VERCEL_URL'], host);
t.is(homeJson['VERCEL_URL'], host);
}
async function nowDevWithEnv() {
const vc = execa(binaryPath, ['dev', ...defaultArgs], {
reject: false,
cwd: target,
});
let localhost = undefined;
await waitForPrompt(vc, chunk => {
if (chunk.includes('Ready! Available at')) {
localhost = /(https?:[^\s]+)/g.exec(chunk);
return true;
}
return false;
});
const localhostNoProtocol = localhost[0].slice('http://'.length);
const apiUrl = `${localhost[0]}/api/get-env`;
const apiRes = await fetch(apiUrl);
t.is(apiRes.status, 200);
const apiJson = await apiRes.json();
t.is(apiJson['MY_ENV_VAR'], 'MY_VALUE');
t.is(apiJson['VERCEL_URL'], localhostNoProtocol);
const homeUrl = localhost[0];
const homeRes = await fetch(homeUrl);
const homeJson = await homeRes.json();
t.is(homeJson['MY_ENV_VAR'], 'MY_VALUE');
t.is(homeJson['VERCEL_URL'], localhostNoProtocol);
vc.kill('SIGTERM', { forceKillAfterTimeout: 2000 });
const { exitCode, stderr, stdout } = await vc;
t.is(exitCode, 0, formatOutput({ stderr, stdout }));
}
async function nowDevAndFetchCloudVars() {
const vc = execa(binaryPath, ['dev', ...defaultArgs], {
reject: false,
cwd: target,
});
let localhost = undefined;
await waitForPrompt(vc, chunk => {
if (chunk.includes('Ready! Available at')) {
localhost = /(https?:[^\s]+)/g.exec(chunk);
return true;
}
return false;
});
const apiUrl = `${localhost[0]}/api/get-env`;
const apiRes = await fetch(apiUrl);
const localhostNoProtocol = localhost[0].slice('http://'.length);
t.is(apiRes.status, 200);
const apiJson = await apiRes.json();
t.is(apiJson['VERCEL_URL'], localhostNoProtocol);
t.is(apiJson['MY_ENV_VAR'], 'MY_VALUE');
const homeUrl = localhost[0];
const homeRes = await fetch(homeUrl);
const homeJson = await homeRes.json();
t.is(homeJson['MY_ENV_VAR'], 'MY_VALUE');
t.is(homeJson['VERCEL_URL'], localhostNoProtocol);
vc.kill('SIGTERM', { forceKillAfterTimeout: 2000 });
const { exitCode, stderr, stdout } = await vc;
t.is(exitCode, 0, formatOutput({ stderr, stdout }));
}
async function nowEnvRemove() {
@@ -662,11 +741,13 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
await nowEnvPullOverwrite();
await nowEnvPullConfirm();
await nowDeployWithVar();
await nowDevWithEnv();
fs.unlinkSync(path.join(target, '.env'));
await nowDevAndFetchCloudVars();
await nowEnvRemove();
await nowEnvRemoveWithArgs();
await nowEnvRemoveWithNameOnly();
await nowEnvLsIsEmpty();
fs.unlinkSync(path.join(target, '.env'));
});
test('deploy with metadata containing "=" in the value', async t => {

View File

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

View File

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

View File

@@ -3,10 +3,14 @@ import execa from 'execa';
import fetch from 'node-fetch';
import { mkdirp, pathExists } from 'fs-extra';
import { dirname, join } from 'path';
import { homedir } from 'os';
import buildUtils from './build-utils';
import stringArgv from 'string-argv';
const { debug } = buildUtils;
const archMap = new Map([['x64', 'amd64'], ['x86', '386']]);
const archMap = new Map([
['x64', 'amd64'],
['x86', '386'],
]);
const platformMap = new Map([['win32', 'windows']]);
// Location where the `go` binary will be installed after `postinstall`
@@ -126,35 +130,50 @@ export async function downloadGo(
platform = process.platform,
arch = process.arch
) {
// Check if `go` is already installed in user's `$PATH`
const { failed, stdout } = await execa('go', ['version'], { reject: false });
// Check default `Go` in user machine
const isUserGo = await pathExists(join(homedir(), 'go'));
if (!failed && parseInt(stdout.split('.')[1]) >= 11) {
debug('Using system installed version of `go`: %o', stdout.trim());
return createGo(dir, platform, arch);
}
// If we found GOPATH in ENV, or default `Go` path exists
// asssume that user have `Go` installed
if (isUserGo || process.env.GOPATH !== undefined) {
const { stdout } = await execa('go', ['version']);
// Check `go` bin in builder CWD
const isGoExist = await pathExists(join(dir, 'bin'));
if (!isGoExist) {
debug('Installing `go` v%s to %o for %s %s', version, dir, platform, arch);
const url = getGoUrl(version, platform, arch);
debug('Downloading `go` URL: %o', url);
const res = await fetch(url);
if (!res.ok) {
throw new Error(`Failed to download: ${url} (${res.status})`);
if (parseInt(stdout.split('.')[1]) >= 11) {
return createGo(dir, platform, arch);
}
// TODO: use a zip extractor when `ext === "zip"`
await mkdirp(dir);
await new Promise((resolve, reject) => {
res.body
.on('error', reject)
.pipe(tar.extract({ cwd: dir, strip: 1 }))
.on('error', reject)
.on('finish', resolve);
});
throw new Error(
`Your current ${stdout} doesn't support Go Modules. Please update.`
);
} else {
// Check `Go` bin in builder CWD
const isGoExist = await pathExists(join(dir, 'bin'));
if (!isGoExist) {
debug(
'Installing `go` v%s to %o for %s %s',
version,
dir,
platform,
arch
);
const url = getGoUrl(version, platform, arch);
debug('Downloading `go` URL: %o', url);
const res = await fetch(url);
if (!res.ok) {
throw new Error(`Failed to download: ${url} (${res.status})`);
}
// TODO: use a zip extractor when `ext === "zip"`
await mkdirp(dir);
await new Promise((resolve, reject) => {
res.body
.on('error', reject)
.pipe(tar.extract({ cwd: dir, strip: 1 }))
.on('error', reject)
.on('finish', resolve);
});
}
return createGo(dir, platform, arch);
}
return createGo(dir, platform, arch);
}

View File

@@ -1,25 +1,8 @@
import { join, sep, dirname, basename, normalize } from 'path';
import { readFile, writeFile, pathExists, move } from 'fs-extra';
import { homedir } from 'os';
import execa from 'execa';
import retry from 'async-retry';
import { homedir, tmpdir } from 'os';
import { spawn } from 'child_process';
import { Readable } from 'stream';
import once from '@tootallnate/once';
import { join, dirname, basename, normalize, sep } from 'path';
import {
readFile,
writeFile,
pathExists,
mkdirp,
move,
remove,
} from 'fs-extra';
import {
BuildOptions,
Meta,
Files,
StartDevServerOptions,
StartDevServerResult,
} from '@vercel/build-utils';
import { BuildOptions, Meta, Files, shouldServe } from '@vercel/build-utils';
import buildUtils from './build-utils';
const {
@@ -27,17 +10,12 @@ const {
download,
createLambda,
getWriteableDirectory,
shouldServe,
debug,
} = buildUtils;
const TMP = tmpdir();
import { createGo, getAnalyzedEntrypoint, OUT_EXTENSION } from './go-helpers';
const handlerFileName = `handler${OUT_EXTENSION}`;
export { shouldServe };
interface Analyzed {
found?: boolean;
packageName: string;
@@ -45,22 +23,16 @@ interface Analyzed {
watch: string[];
}
interface PortInfo {
port: number;
}
// Initialize private git repo for Go Modules
async function initPrivateGit(credentials: string) {
const gitCredentialsPath = join(homedir(), '.git-credentials');
await execa('git', [
'config',
'--global',
'credential.helper',
`store --file ${gitCredentialsPath}`,
`store --file ${join(homedir(), '.git-credentials')}`,
]);
await writeFile(gitCredentialsPath, credentials);
await writeFile(join(homedir(), '.git-credentials'), credentials);
}
/**
@@ -463,160 +435,4 @@ Learn more: https://vercel.com/docs/runtimes#official-runtimes/go
};
}
function isPortInfo(v: any): v is PortInfo {
return v && typeof v.port === 'number';
}
function isReadable(v: any): v is Readable {
return v && v.readable === true;
}
async function copyEntrypoint(entrypoint: string, dest: string): Promise<void> {
const data = await readFile(entrypoint, 'utf8');
// Modify package to `package main`
const patched = data.replace(/\bpackage\W+\S+\b/, 'package main');
await writeFile(join(dest, 'entrypoint.go'), patched);
}
async function copyDevServer(
functionName: string,
dest: string
): Promise<void> {
const data = await readFile(join(__dirname, 'dev-server.go'), 'utf8');
// Populate the handler function name
const patched = data.replace('__HANDLER_FUNC_NAME', functionName);
await writeFile(join(dest, 'vercel-dev-server-main.go'), patched);
}
export async function startDevServer(
opts: StartDevServerOptions
): Promise<StartDevServerResult> {
const { entrypoint, workPath, meta = {} } = opts;
const { devCacheDir = join(workPath, '.vercel', 'cache') } = meta;
const entrypointDir = dirname(entrypoint);
// For some reason, if `entrypoint` is a path segment (filename contains `[]`
// brackets) then the `.go` suffix on the entrypoint is missing. Fix that here…
let entrypointWithExt = entrypoint;
if (!entrypoint.endsWith('.go')) {
entrypointWithExt += '.go';
}
const tmp = join(devCacheDir, 'go', Math.random().toString(32).substring(2));
const tmpPackage = join(tmp, entrypointDir);
await mkdirp(tmpPackage);
let goModAbsPathDir = '';
if (await pathExists(join(workPath, 'go.mod'))) {
goModAbsPathDir = workPath;
}
const analyzedRaw = await getAnalyzedEntrypoint(
entrypointWithExt,
goModAbsPathDir
);
if (!analyzedRaw) {
throw new Error(
`Could not find an exported function in "${entrypointWithExt}"
Learn more: https://vercel.com/docs/runtimes#official-runtimes/go`
);
}
const analyzed: Analyzed = JSON.parse(analyzedRaw);
await Promise.all([
copyEntrypoint(entrypointWithExt, tmpPackage),
copyDevServer(analyzed.functionName, tmpPackage),
]);
const portFile = join(
TMP,
`vercel-dev-port-${Math.random().toString(32).substring(2)}`
);
const env: typeof process.env = {
...process.env,
...meta.env,
VERCEL_DEV_PORT_FILE: portFile,
};
const tmpRelative = `.${sep}${entrypointDir}`;
const child = spawn('go', ['run', tmpRelative], {
cwd: tmp,
env,
stdio: ['ignore', 'inherit', 'inherit', 'pipe'],
});
child.once('exit', () => {
retry(() => remove(tmp)).catch((err: Error) => {
console.error('Could not delete tmp directory: %j: %s', tmp, err);
});
});
const portPipe = child.stdio[3];
if (!isReadable(portPipe)) {
throw new Error('File descriptor 3 is not readable');
}
// `dev-server.go` writes the ephemeral port number to FD 3 to be consumed here
const onPort = new Promise<PortInfo>(resolve => {
portPipe.setEncoding('utf8');
portPipe.once('data', d => {
resolve({ port: Number(d) });
});
});
const onPortFile = waitForPortFile(portFile);
const onExit = once.spread<[number, string | null]>(child, 'exit');
const result = await Promise.race([onPort, onPortFile, onExit]);
onExit.cancel();
onPortFile.cancel();
if (isPortInfo(result)) {
return {
port: result.port,
pid: child.pid,
};
} else if (Array.isArray(result)) {
// Got "exit" event from child process
throw new Error(
`Failed to start dev server for "${entrypointWithExt}" (code=${result[0]}, signal=${result[1]})`
);
} else {
throw new Error(`Unexpected result type: ${typeof result}`);
}
}
export interface CancelablePromise<T> extends Promise<T> {
cancel: () => void;
}
function waitForPortFile(portFile: string) {
const opts = { portFile, canceled: false };
const promise = waitForPortFile_(opts) as CancelablePromise<PortInfo | void>;
promise.cancel = () => {
opts.canceled = true;
};
return promise;
}
async function waitForPortFile_(opts: {
portFile: string;
canceled: boolean;
}): Promise<PortInfo | void> {
while (!opts.canceled) {
await new Promise(resolve => setTimeout(resolve, 100));
try {
const port = Number(await readFile(opts.portFile, 'ascii'));
retry(() => remove(opts.portFile)).catch((err: Error) => {
console.error('Could not delete port file: %j: %s', opts.portFile, err);
});
return { port };
} catch (err) {
if (err.code !== 'ENOENT') {
throw err;
}
}
}
}
export { shouldServe };

View File

@@ -1,8 +1,8 @@
package main
import (
vc "github.com/vercel/go-bridge/go/bridge"
"net/http"
vc "github.com/vercel/go-bridge/go/bridge"
)
func main() {

View File

@@ -1,12 +1,12 @@
package main
import (
"__NOW_HANDLER_PACKAGE_NAME"
"net/http"
"net/http"
"__NOW_HANDLER_PACKAGE_NAME"
vc "github.com/vercel/go-bridge/go/bridge"
vc "github.com/vercel/go-bridge/go/bridge"
)
func main() {
vc.Start(http.HandlerFunc(__NOW_HANDLER_FUNC_NAME))
vc.Start(http.HandlerFunc(__NOW_HANDLER_FUNC_NAME))
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/go",
"version": "1.1.4-canary.0",
"version": "1.1.4",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
@@ -19,8 +19,6 @@
"dist"
],
"devDependencies": {
"@tootallnate/once": "1.1.2",
"@types/async-retry": "1.4.2",
"@types/execa": "^0.9.0",
"@types/fs-extra": "^5.0.5",
"@types/node-fetch": "^2.3.0",

View File

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

View File

@@ -579,8 +579,6 @@ export const build = async ({
return {
output,
routes: [
// TODO: low priority: handle trailingSlash
// User headers
...headers,

View File

@@ -1 +1 @@
export default () => 'hello from /[teamSlug]/[project]/[id]'
export default () => 'hello from /[teamSlug]/[project]/[id]';

View File

@@ -1,18 +1,17 @@
import { useRouter } from 'next/router'
import { useRouter } from 'next/router';
export const getStaticProps = ({ params }) => {
return {
props: {
id: params.id
}
}
}
id: params.id,
},
};
};
export const getStaticPaths = () => ({
paths: ['first', 'second'].map(id => ({ params: { id }})),
fallback: true
})
paths: ['first', 'second'].map(id => ({ params: { id } })),
fallback: true,
});
export default ({ id }) => useRouter().isFallback
? `loading...`
: `hello from /groups/[id] ${id}`
export default ({ id }) =>
useRouter().isFallback ? `loading...` : `hello from /groups/[id] ${id}`;

View File

@@ -1,9 +1,9 @@
export const getServerSideProps = ({ params }) => {
return {
props: {
code: params.inviteCode
}
}
}
code: params.inviteCode,
},
};
};
export default ({ code }) => `hello from /teams/invite/[inviteCode] ${code}`
export default ({ code }) => `hello from /teams/invite/[inviteCode] ${code}`;

View File

@@ -0,0 +1 @@
module.exports = { trailingSlash: true };

View File

@@ -0,0 +1,38 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@vercel/next" }],
"probes": [
{ "path": "/foo/", "status": 200, "mustContain": "foo page" },
{
"fetchOptions": { "redirect": "manual" },
"path": "/foo",
"status": 308,
"responseHeaders": {
"refresh": "/url=/foo/$/"
}
},
{ "path": "/abc/def/", "status": 200, "mustContain": "nested page" },
{
"fetchOptions": { "redirect": "manual" },
"path": "/abc/def",
"status": 308,
"responseHeaders": {
"refresh": "/url=/abc/def/$/"
}
},
{
"fetchOptions": { "redirect": "manual" },
"path": "/test.txt/",
"status": 308,
"responseHeaders": {
"refresh": "/url=/test\\.txt$/"
}
},
{
"fetchOptions": { "redirect": "manual" },
"path": "/test.txt",
"status": 200,
"mustContain": "this is a file"
}
]
}

View File

@@ -0,0 +1,7 @@
{
"dependencies": {
"next": "canary",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <p>nested page</p>;
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <p>foo page</p>;
}

View File

@@ -0,0 +1 @@
this is a file

View File

@@ -0,0 +1 @@
module.exports = { trailingSlash: false };

View File

@@ -0,0 +1,38 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@vercel/next" }],
"probes": [
{ "path": "/foo", "status": 200, "mustContain": "foo page" },
{
"fetchOptions": { "redirect": "manual" },
"path": "/foo/",
"status": 308,
"responseHeaders": {
"refresh": "/url=/foo$/"
}
},
{ "path": "/abc/def", "status": 200, "mustContain": "nested page" },
{
"fetchOptions": { "redirect": "manual" },
"path": "/abc/def/",
"status": 308,
"responseHeaders": {
"refresh": "/url=/abc/def$/"
}
},
{
"fetchOptions": { "redirect": "manual" },
"path": "/test.txt/",
"status": 308,
"responseHeaders": {
"refresh": "/url=/test\\.txt$/"
}
},
{
"fetchOptions": { "redirect": "manual" },
"path": "/test.txt",
"status": 200,
"mustContain": "this is a file"
}
]
}

View File

@@ -0,0 +1,7 @@
{
"dependencies": {
"next": "canary",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <p>nested page</p>;
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <p>foo page</p>;
}

View File

@@ -0,0 +1 @@
this is a file

View File

@@ -1 +1 @@
export default () => 'hi from final route'
export default () => 'hi from final route';

View File

@@ -1 +1 @@
export default () => 'hi from another route'
export default () => 'hi from another route';

View File

@@ -1 +1 @@
export default () => 'hi from deployment route'
export default () => 'hi from deployment route';

View File

@@ -1 +1 @@
export default () => 'hi from project route'
export default () => 'hi from project route';

View File

@@ -1 +1 @@
export default () => 'hi from team route'
export default () => 'hi from team route';

View File

@@ -1,14 +1,14 @@
import React from 'react'
import React from 'react';
// eslint-disable-next-line camelcase
export async function unstable_getStaticProps () {
export async function unstable_getStaticProps() {
return {
props: {
world: 'world',
time: new Date().getTime()
time: new Date().getTime(),
},
revalidate: 5
}
revalidate: 5,
};
}
export default ({ world, time }) => {
@@ -17,5 +17,5 @@ export default ({ world, time }) => {
<p>hello: {world}</p>
<span>time: {time}</span>
</>
)
}
);
};

View File

@@ -1,14 +1,14 @@
import React from 'react'
import React from 'react';
// eslint-disable-next-line camelcase
export async function unstable_getStaticProps () {
export async function unstable_getStaticProps() {
return {
props: {
world: 'world',
time: new Date().getTime()
time: new Date().getTime(),
},
revalidate: 5
}
revalidate: 5,
};
}
export default ({ world, time }) => {
@@ -17,5 +17,5 @@ export default ({ world, time }) => {
<p>hello: {world}</p>
<span>time: {time}</span>
</>
)
}
);
};

View File

@@ -1,7 +1,7 @@
import React from 'react';
// eslint-disable-next-line camelcase
export async function unstable_getStaticPaths () {
export async function unstable_getStaticPaths() {
return [
'/blog/post-1/comment-1',
{ params: { post: 'post-2', comment: 'comment-2' } },
@@ -10,7 +10,7 @@ export async function unstable_getStaticPaths () {
}
// eslint-disable-next-line camelcase
export async function unstable_getStaticProps ({ params }) {
export async function unstable_getStaticProps({ params }) {
return {
props: {
post: params.post,

View File

@@ -1,29 +1,25 @@
import React from 'react'
import React from 'react';
// eslint-disable-next-line camelcase
export async function unstable_getStaticPaths () {
return [
'/blog/post-1',
{ params: { post: 'post-2' } },
]
export async function unstable_getStaticPaths() {
return ['/blog/post-1', { params: { post: 'post-2' } }];
}
// eslint-disable-next-line camelcase
export async function unstable_getStaticProps ({ params }) {
export async function unstable_getStaticProps({ params }) {
if (params.post === 'post-10') {
await new Promise(resolve => {
setTimeout(() => resolve(), 1000)
})
setTimeout(() => resolve(), 1000);
});
}
return {
props: {
post: params.post,
time: (await import('perf_hooks')).performance.now()
time: (await import('perf_hooks')).performance.now(),
},
revalidate: 10
}
revalidate: 10,
};
}
export default ({ post, time }) => {
@@ -32,5 +28,5 @@ export default ({ post, time }) => {
<p>Post: {post}</p>
<span>time: {time}</span>
</>
)
}
);
};

View File

@@ -1,14 +1,14 @@
import React from 'react'
import React from 'react';
// eslint-disable-next-line camelcase
export async function unstable_getStaticProps () {
export async function unstable_getStaticProps() {
return {
props: {
world: 'world',
time: new Date().getTime()
time: new Date().getTime(),
},
revalidate: false
}
revalidate: false,
};
}
export default ({ world, time }) => {
@@ -17,5 +17,5 @@ export default ({ world, time }) => {
<p>hello: {world}</p>
<span>time: {time}</span>
</>
)
}
);
};

View File

@@ -1,5 +1,5 @@
const Page = ({ data }) => <p>{data} world</p>
const Page = ({ data }) => <p>{data} world</p>;
Page.getInitialProps = () => ({ data: 'hello' })
Page.getInitialProps = () => ({ data: 'hello' });
export default Page
export default Page;

View File

@@ -1,3 +1,3 @@
export default function(req, res) {
export default function (req, res) {
res.end(`${process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE}`);
}

View File

@@ -1 +1 @@
export default () => 'hello world!'
export default () => 'hello world!';

View File

@@ -1,10 +1,10 @@
import { useRouter } from 'next/router'
import { useRouter } from 'next/router';
const Page = () => {
const { query } = useRouter()
return <p>{JSON.stringify(query)}</p>
}
const { query } = useRouter();
return <p>{JSON.stringify(query)}</p>;
};
Page.getInitialProps = () => ({ a: 'b' })
Page.getInitialProps = () => ({ a: 'b' });
export default Page
export default Page;

View File

@@ -1,5 +1,5 @@
const Page = () => 'hello world'
const Page = () => 'hello world';
Page.getInitialProps = () => ({ hello: 'world' })
Page.getInitialProps = () => ({ hello: 'world' });
export default Page
export default Page;

View File

@@ -1,10 +1,10 @@
import { useRouter } from 'next/router'
import { useRouter } from 'next/router';
const Page = () => {
const { query } = useRouter()
return <p>{JSON.stringify(query)}</p>
}
const { query } = useRouter();
return <p>{JSON.stringify(query)}</p>;
};
Page.getInitialProps = () => ({ a: 'b' })
Page.getInitialProps = () => ({ a: 'b' });
export default Page
export default Page;

View File

@@ -1 +1 @@
export default (req, res) => res.end(`another slug: ${req.query.slug}`)
export default (req, res) => res.end(`another slug: ${req.query.slug}`);

View File

@@ -1 +1 @@
export default (req, res) => res.end(`index slug: ${req.query.slug}`)
export default (req, res) => res.end(`index slug: ${req.query.slug}`);

View File

@@ -1,9 +1,9 @@
export const getServerSideProps = ({ params }) => ({
props: {
post: params.post
}
})
post: params.post,
},
});
export default function Comment({ post }) {
return `comments post: ${post}`
}
return `comments post: ${post}`;
}

View File

@@ -1,9 +1,9 @@
export const getServerSideProps = ({ params }) => ({
props: {
post: params.post
}
})
post: params.post,
},
});
export default function Post({ post }) {
return `index post: ${post}`
}
return `index post: ${post}`;
}

View File

@@ -1,18 +1,18 @@
import React from 'react';
// eslint-disable-next-line camelcase
export async function unstable_getStaticPaths () {
export async function unstable_getStaticPaths() {
return {
paths: [
'/blog/post-1/comment-1',
{ params: { post: 'post-2', comment: 'comment-2' } },
'/blog/post-1337/comment-1337',
]
}
],
};
}
// eslint-disable-next-line camelcase
export async function unstable_getStaticProps ({ params }) {
export async function unstable_getStaticProps({ params }) {
return {
props: {
post: params.post,
@@ -24,7 +24,7 @@ export async function unstable_getStaticProps ({ params }) {
}
export default ({ post, comment, time }) => {
if (!post) return <p>loading...</p>
if (!post) return <p>loading...</p>;
return (
<>

View File

@@ -1,40 +1,36 @@
import React from 'react'
import React from 'react';
// eslint-disable-next-line camelcase
export async function unstable_getStaticPaths () {
export async function unstable_getStaticPaths() {
return {
paths: [
'/blog/post-1',
{ params: { post: 'post-2' } },
]
}
paths: ['/blog/post-1', { params: { post: 'post-2' } }],
};
}
// eslint-disable-next-line camelcase
export async function unstable_getStaticProps ({ params }) {
export async function unstable_getStaticProps({ params }) {
if (params.post === 'post-10') {
await new Promise(resolve => {
setTimeout(() => resolve(), 1000)
})
setTimeout(() => resolve(), 1000);
});
}
return {
props: {
post: params.post,
time: (await import('perf_hooks')).performance.now()
time: (await import('perf_hooks')).performance.now(),
},
revalidate: 10
}
revalidate: 10,
};
}
export default ({ post, time }) => {
if (!post) return <p>loading...</p>
if (!post) return <p>loading...</p>;
return (
<>
<p>Post: {post}</p>
<span>time: {time}</span>
</>
)
}
);
};

View File

@@ -3,7 +3,7 @@ import React from 'react';
// eslint-disable-next-line camelcase
export async function getServerSideProps({ params }) {
if (params.post === 'post-10') {
await new Promise((resolve) => {
await new Promise(resolve => {
setTimeout(() => resolve(), 1000);
});
}

View File

@@ -1,5 +1,5 @@
import { useRouter } from 'next/router'
import Error from 'next/error'
import { useRouter } from 'next/router';
import Error from 'next/error';
function loadArticle() {
return {
@@ -10,21 +10,22 @@ function loadArticle() {
},
{
type: 'paragraph',
content: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend.'
}
]
}
content:
'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend.',
},
],
};
}
const Page = ({ path, article }) => {
const router = useRouter()
const router = useRouter();
if (router.isFallback) {
return <div>Loading...</div>
return <div>Loading...</div>;
}
if (!article.content) {
return <Error statusCode={404}/>
return <Error statusCode={404} />;
}
const [header, ...body] = article.content;
@@ -34,13 +35,15 @@ const Page = ({ path, article }) => {
<header>{header.content}</header>
<small>path: {path.join('/')}</small>
<main>
{body.map(({ content }) => <p>{content}</p>)}
{body.map(({ content }) => (
<p>{content}</p>
))}
</main>
</article>
)
}
);
};
export default Page
export default Page;
export async function getStaticProps({ params }) {
const { path } = params;
@@ -50,13 +53,13 @@ export async function getStaticProps({ params }) {
props: {
article,
path,
}
}
},
};
}
export async function getStaticPaths() {
return {
paths: [],
fallback: true,
}
};
}

View File

@@ -2,7 +2,7 @@
const fetch = require('node-fetch');
const cheerio = require('cheerio');
module.exports = function(ctx) {
module.exports = function (ctx) {
it('should revalidate content properly from pathname', async () => {
const res = await fetch(`${ctx.deploymentUrl}/another`);
expect(res.status).toBe(200);

View File

@@ -8,7 +8,7 @@ export async function getStaticProps() {
random: Math.random(),
time: new Date().getTime(),
},
unstable_revalidate: 1,
revalidate: 1,
};
}

View File

@@ -7,7 +7,7 @@ export async function getStaticProps() {
world: 'world',
time: new Date().getTime(),
},
unstable_revalidate: 5,
revalidate: 5,
};
}

View File

@@ -7,7 +7,7 @@ export async function getStaticPaths() {
'/blog/post-1/comment-1',
{ params: { post: 'post-2', comment: 'comment-2' } },
'/blog/post-1337/comment-1337',
'/blog/post-123/comment-321'
'/blog/post-123/comment-321',
],
fallback: true,
};
@@ -22,7 +22,7 @@ export async function getStaticProps({ params }) {
comment: params.comment,
time: new Date().getTime(),
},
unstable_revalidate: 1,
revalidate: 1,
};
}
@@ -31,10 +31,10 @@ export default ({ post, comment, time, random }) => {
return (
<>
<p id='post'>Post: {post}</p>
<p id='comment'>Comment: {comment}</p>
<span id='time'>time: {time}</span>
<span id='random'>random: {random}</span>
<p id="post">Post: {post}</p>
<p id="comment">Comment: {comment}</p>
<span id="time">time: {time}</span>
<span id="random">random: {random}</span>
</>
);
};

View File

@@ -3,11 +3,7 @@ import React from 'react';
// eslint-disable-next-line camelcase
export async function getStaticPaths() {
return {
paths: [
'/blog/post-1',
{ params: { post: 'post-2' } },
'/blog/post-123',
],
paths: ['/blog/post-1', { params: { post: 'post-2' } }, '/blog/post-123'],
fallback: true,
};
}
@@ -26,7 +22,7 @@ export async function getStaticProps({ params }) {
random: Math.random(),
time: (await import('perf_hooks')).performance.now(),
},
unstable_revalidate: 1,
revalidate: 1,
};
}
@@ -35,9 +31,9 @@ export default ({ post, time, random }) => {
return (
<>
<p id='post'>Post: {post}</p>
<span id='time'>time: {time}</span>
<span id='random'>random: {random}</span>
<p id="post">Post: {post}</p>
<span id="time">time: {time}</span>
<span id="random">random: {random}</span>
</>
);
};

View File

@@ -7,7 +7,7 @@ export async function getStaticProps() {
world: 'world',
time: new Date().getTime(),
},
unstable_revalidate: false,
revalidate: false,
};
}

View File

@@ -15,7 +15,7 @@ export async function getStaticProps({ params }) {
slug: params.slug,
time: (await import('perf_hooks')).performance.now(),
},
unstable_revalidate: 10,
revalidate: 10,
};
}

View File

@@ -1 +1 @@
export default () => 'hi'
export default () => 'hi';

View File

@@ -1 +1 @@
export default async (req, res) => res.json({ query: req.query })
export default async (req, res) => res.json({ query: req.query });

View File

@@ -1,11 +1,11 @@
import { useRouter } from 'next/router'
import { useRouter } from 'next/router';
const Page = () => (
<>
<p>post: {useRouter().query.post}</p>
</>
)
);
Page.getInitialProps = () => ({ hello: 'world' })
Page.getInitialProps = () => ({ hello: 'world' });
export default Page
export default Page;

View File

@@ -1,17 +1,15 @@
import { useRouter } from 'next/router'
import { useRouter } from 'next/router';
const Page = () => {
return (
<p>path: {useRouter().query['hello-world']?.join('/')}</p>
)
}
return <p>path: {useRouter().query['hello-world']?.join('/')}</p>;
};
export default Page
export default Page;
export const getServerSideProps = () => {
return {
props: {
hello: 'world'
}
}
}
hello: 'world',
},
};
};

View File

@@ -1 +1 @@
export default () => 'hi'
export default () => 'hi';

View File

@@ -1,3 +1,3 @@
export default function() {
export default function () {
return <div>hello world</div>;
}

View File

@@ -14,10 +14,10 @@ module.exports = function (ctx) {
setCookieParser.splitCookiesString(res.headers.get('set-cookie'))
);
const bypassCookie = cookies.find(
(cookie) => cookie.name === '__prerender_bypass'
cookie => cookie.name === '__prerender_bypass'
);
const previewDataCookie = cookies.find(
(cookie) => cookie.name === '__next_preview_data'
cookie => cookie.name === '__next_preview_data'
);
expect(bypassCookie).toBeDefined();
@@ -38,10 +38,10 @@ module.exports = function (ctx) {
setCookieParser.splitCookiesString(res.headers.get('set-cookie'))
);
const bypassCookie = cookies.find(
(cookie) => cookie.name === '__prerender_bypass'
cookie => cookie.name === '__prerender_bypass'
);
const previewDataCookie = cookies.find(
(cookie) => cookie.name === '__next_preview_data'
cookie => cookie.name === '__next_preview_data'
);
expect(bypassCookie).toBeDefined();

View File

@@ -1,4 +1,4 @@
export const getStaticProps = (ctx) => {
export const getStaticProps = ctx => {
console.log('previewData', ctx.previewData);
return {
@@ -12,7 +12,7 @@ export const getStaticProps = (ctx) => {
export const getStaticPaths = () => {
return {
paths: [['first'], ['second'], ['another', 'one']].map((rest) => ({
paths: [['first'], ['second'], ['another', 'one']].map(rest => ({
params: { rest },
})),
fallback: false,

View File

@@ -1,4 +1,4 @@
export const getStaticProps = (ctx) => {
export const getStaticProps = ctx => {
console.log('previewData', ctx.previewData);
return {

View File

@@ -3,5 +3,5 @@ export default function Page() {
}
export function getStaticProps() {
return { props: {}, unstable_revalidate: 10 };
return { props: {}, revalidate: 10 };
}

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/static-build",
"version": "0.17.6-canary.1",
"version": "0.17.6",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/static-builds",

122
yarn.lock
View File

@@ -3029,7 +3029,7 @@ boolbase@~1.0.0:
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
boxen@4.2.0:
boxen@4.2.0, boxen@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64"
integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==
@@ -3751,6 +3751,18 @@ configstore@^4.0.0:
write-file-atomic "^2.0.0"
xdg-basedir "^3.0.0"
configstore@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96"
integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==
dependencies:
dot-prop "^5.2.0"
graceful-fs "^4.1.2"
make-dir "^3.0.0"
unique-string "^2.0.0"
write-file-atomic "^3.0.0"
xdg-basedir "^4.0.0"
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
@@ -3986,6 +3998,11 @@ crypto-random-string@^1.0.0:
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=
crypto-random-string@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
css-select@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
@@ -4466,7 +4483,7 @@ dot-prop@^4.1.0, dot-prop@^4.2.0:
dependencies:
is-obj "^1.0.0"
dot-prop@^5.1.0:
dot-prop@^5.1.0, dot-prop@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb"
integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==
@@ -4682,6 +4699,11 @@ es6-promisify@^5.0.0:
dependencies:
es6-promise "^4.0.3"
escape-goat@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675"
integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==
escape-html@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
@@ -5705,6 +5727,13 @@ global-dirs@^0.1.0:
dependencies:
ini "^1.3.4"
global-dirs@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201"
integrity sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==
dependencies:
ini "^1.3.5"
globals@^11.1.0, globals@^11.7.0:
version "11.12.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
@@ -6232,7 +6261,7 @@ ini@1.3.4:
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
integrity sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=
ini@^1.3.2, ini@^1.3.4, ini@~1.3.0:
ini@^1.3.2, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
@@ -6487,6 +6516,14 @@ is-installed-globally@^0.1.0:
global-dirs "^0.1.0"
is-path-inside "^1.0.0"
is-installed-globally@^0.3.1:
version "0.3.2"
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141"
integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==
dependencies:
global-dirs "^2.0.1"
is-path-inside "^3.0.1"
is-natural-number@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
@@ -6497,6 +6534,11 @@ is-npm@^3.0.0:
resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-3.0.0.tgz#ec9147bfb629c43f494cf67936a961edec7e8053"
integrity sha512-wsigDr1Kkschp2opC4G3yA6r9EgVA6NjRpWzIi9axXqeIaAATPRJc4uLujXe3Nd9uO8KoDyA4MD6aZSeXTADhA==
is-npm@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d"
integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==
is-number@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
@@ -7632,9 +7674,9 @@ lodash.uniq@^4.5.0:
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.2.1:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
version "4.17.19"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
log-symbols@^1.0.2:
version "1.0.2"
@@ -9374,6 +9416,13 @@ punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
pupa@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.0.1.tgz#dbdc9ff48ffbea4a26a069b6f9f7abb051008726"
integrity sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==
dependencies:
escape-goat "^2.0.0"
q@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
@@ -9429,7 +9478,7 @@ raw-body@2.4.1:
iconv-lite "0.4.24"
unpipe "1.0.0"
rc@^1.0.1, rc@^1.1.6, rc@^1.2.7, rc@^1.2.8:
rc@^1.2.7, rc@^1.2.8:
version "1.2.8"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
@@ -9683,14 +9732,6 @@ regexpu-core@^4.7.0:
unicode-match-property-ecmascript "^1.0.4"
unicode-match-property-value-ecmascript "^1.2.0"
registry-auth-token@3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20"
integrity sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==
dependencies:
rc "^1.1.6"
safe-buffer "^5.0.1"
registry-auth-token@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.1.1.tgz#40a33be1e82539460f94328b0f7f0f84c16d9479"
@@ -9698,13 +9739,6 @@ registry-auth-token@^4.0.0:
dependencies:
rc "^1.2.8"
registry-url@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942"
integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI=
dependencies:
rc "^1.0.1"
registry-url@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009"
@@ -10060,6 +10094,13 @@ semver-diff@^2.0.0:
dependencies:
semver "^5.0.3"
semver-diff@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b"
integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==
dependencies:
semver "^6.3.0"
"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.0.3, semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
@@ -10075,7 +10116,7 @@ semver@6.1.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b"
integrity sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==
semver@^6.0.0, semver@^6.1.2, semver@^6.2.0:
semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
@@ -11333,6 +11374,13 @@ unique-string@^1.0.0:
dependencies:
crypto-random-string "^1.0.0"
unique-string@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d"
integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==
dependencies:
crypto-random-string "^2.0.0"
unique-temp-dir@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unique-temp-dir/-/unique-temp-dir-1.0.0.tgz#6dce95b2681ca003eebfb304a415f9cbabcc5385"
@@ -11383,13 +11431,24 @@ unset-value@^1.0.0:
has-value "^0.3.1"
isobject "^3.0.0"
update-check@1.5.3:
version "1.5.3"
resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.3.tgz#45240fcfb8755a7c7fa68bbdd9eda026a41639ed"
integrity sha512-6KLU4/dd0Tg/l0xwL+f9V7kEIPSL1vOIbnNnhSLiRDlj4AVG6Ks9Zoc9Jgt9kIgWFPZ/wp2AHgmG7xNf15TJOA==
update-notifier@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.0.tgz#4866b98c3bc5b5473c020b1250583628f9a328f3"
integrity sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==
dependencies:
registry-auth-token "3.3.2"
registry-url "3.1.0"
boxen "^4.2.0"
chalk "^3.0.0"
configstore "^5.0.1"
has-yarn "^2.1.0"
import-lazy "^2.1.0"
is-ci "^2.0.0"
is-installed-globally "^0.3.1"
is-npm "^4.0.0"
is-yarn-global "^0.3.0"
latest-version "^5.0.0"
pupa "^2.0.1"
semver-diff "^3.1.1"
xdg-basedir "^4.0.0"
update-notifier@^3.0.1:
version "3.0.1"
@@ -11781,6 +11840,11 @@ xdg-basedir@^3.0.0:
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
xdg-basedir@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
xdg-portable@^7.0.0:
version "7.2.1"
resolved "https://registry.yarnpkg.com/xdg-portable/-/xdg-portable-7.2.1.tgz#4301ba0868b2cbc9de0c53b3699906adcc9d2560"