Compare commits

..

12 Commits

Author SHA1 Message Date
Nathan Rajlich
547e88228e Publish Stable
- @vercel/build-utils@4.2.1
 - vercel@25.2.3
 - @vercel/client@12.0.3
 - @vercel/go@2.0.3
 - @vercel/next@3.1.2
 - @vercel/node@2.3.3
 - @vercel/python@3.0.3
 - @vercel/redwood@1.0.4
 - @vercel/remix@1.0.4
 - @vercel/ruby@1.3.11
 - @vercel/static-build@1.0.3
2022-06-30 12:24:13 -07:00
Luc Leray
9bfb5dd535 [build-utils] Handle npm bin exit code 7 (#8058)
In some rare cases, `npm bin` exits with code 7, but still outputs the right bin path.

To reproduce, try:
```
npm init -y
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
vc
# enter "echo build" for the build command, leave the other configuration as default
```

The build will fail with `Error: Command exited with 7` because `npm bin` fails with code 7, for some reason.

In this PR, we do 2 things:
(1) Ignore exit codes from `npm bin`. It still outputs the right path when it exits with code 7 so we just read the output and check if it's a valid path.
(2) Throw a more specific error message when `npm bin` fails to give us the bin path. The current error was hard to debug because it looked like it was coming from the install commmand. We can do better by emitting a custom error.

Alternative considered for (2): Do not throw errors. If `npm bin` fails, emit a warning and let the build continue.

Related Issues:
- https://github.com/vercel/customer-issues/issues/585 (internal)
2022-06-30 17:27:52 +00:00
Nathan Rajlich
81ea84fae8 [cli] Fix vc build lambda serialization when there's a broken symlink (#8050)
There's some cleanup directory walking logic that was choking when
a Lambda outputs a file with a broken symlink. We shouldn't need to
traverse into those directories in the case of a symlink anyways, so use
`lstat()` instead of `stat()` to prevent that filesystem call from
throwing an error.
2022-06-29 16:04:31 -07:00
Nathan Rajlich
fa8bf07be4 [cli] Add Client#stdin / Client#stdout (#8039)
This will allow for mockability of the input streams (i.e. prompts)
for CLI commands in unit tests.

**Example:**

```typescript
import confirm from '../../src/util/input/confirm';
import { client } from '../mocks/client';

describe('MockClient', () => {
  it('should mock `confirm()`', async () => {
    const confirmedPromise = confirm(client, 'Do the thing?', false);

    client.stdin.write('yes\n');

    const confirmed = await confirmedPromise;
    expect(confirmed).toEqual(true);
  });
});
```
2022-06-29 16:03:56 -07:00
JJ Kasper
cc9dce73ad [next] Ensure uncompressed limit is correct (#8049)
* Revert "Revert "[next] Update max size warning to handle initial layer better" (#8047)"

This reverts commit 8c62de16ce.

* Ensure uncompressed limit is correct

* apply suggestion
2022-06-29 15:13:15 -05:00
Sean Massa
bba7cbd411 [cli][dev] fix: creating "api/some-func.js" after "vc dev" now works (#8041)
If there is no `api` directory, then you run `vc dev`, then you create a new function `api/some-func.js`, then this file would not be served as a new function.

This was being caused by incomplete "new file" handling logic. This PR ensures that the proper detection is done in each new file (`getVercelConfig`) that populates key properties (`apiDir`, `apiExtensions`, and extensionless `files`) for determining when a file is used to serve a request.
2022-06-29 18:37:48 +00:00
Steven
9a3739bebd Publish Stable
- vercel@25.2.2
 - @vercel/next@3.1.1
 - @vercel/node@2.3.2
 - @vercel/redwood@1.0.3
 - @vercel/remix@1.0.3
2022-06-29 09:27:34 -04:00
Gal Schlezinger
8c62de16ce Revert "[next] Update max size warning to handle initial layer better" (#8047)
Revert "[next] Update max size warning to handle initial layer better (#8013)"

This reverts commit f20703b15d.
2022-06-29 09:26:29 -04:00
Steven
e9333988d7 [next][node][redwood][remix] Bump @vercel/nft to 0.20.1 (#8042)
- https://github.com/vercel/nft/releases/tag/0.20.0
- https://github.com/vercel/nft/releases/tag/0.20.1
2022-06-29 00:21:14 +00:00
Nathan Rajlich
fb001ce7eb [tests] Remove TODO comments from Middleware matchers vc dev test (#8037) 2022-06-28 18:26:56 +00:00
Sean Massa
b399fe7037 Publish Stable
- vercel@25.2.1
 - @vercel/node@2.3.1
2022-06-28 12:04:57 -05:00
Sean Massa
88385b3c84 [cli][node] switch to esbuild for compiling edge functions (#8032) 2022-06-28 11:27:56 -05:00
67 changed files with 723 additions and 367 deletions

View File

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

View File

@@ -61,6 +61,13 @@ export interface SpawnOptionsExtended extends SpawnOptions {
* Pretty formatted command that is being spawned for logging purposes. * Pretty formatted command that is being spawned for logging purposes.
*/ */
prettyCommand?: string; prettyCommand?: string;
/**
* Returns instead of throwing an error when the process exits with a
* non-0 exit code. When relevant, the returned object will include
* the error code, stdout and stderr.
*/
ignoreNon0Exit?: boolean;
} }
export function spawnAsync( export function spawnAsync(
@@ -79,7 +86,7 @@ export function spawnAsync(
child.on('error', reject); child.on('error', reject);
child.on('close', (code, signal) => { child.on('close', (code, signal) => {
if (code === 0) { if (code === 0 || opts.ignoreNon0Exit) {
return resolve(); return resolve();
} }
@@ -123,24 +130,24 @@ export function execAsync(
child.on('error', reject); child.on('error', reject);
child.on('close', (code, signal) => { child.on('close', (code, signal) => {
if (code !== 0) { if (code === 0 || opts.ignoreNon0Exit) {
const cmd = opts.prettyCommand return resolve({
? `Command "${opts.prettyCommand}"` code,
: 'Command'; stdout: Buffer.concat(stdoutList).toString(),
stderr: Buffer.concat(stderrList).toString(),
return reject( });
new NowBuildError({
code: `BUILD_UTILS_EXEC_${code || signal}`,
message: `${cmd} exited with ${code || signal}`,
})
);
} }
return resolve({ const cmd = opts.prettyCommand
code, ? `Command "${opts.prettyCommand}"`
stdout: Buffer.concat(stdoutList).toString(), : 'Command';
stderr: Buffer.concat(stderrList).toString(),
}); return reject(
new NowBuildError({
code: `BUILD_UTILS_EXEC_${code || signal}`,
message: `${cmd} exited with ${code || signal}`,
})
);
}); });
} }
); );
@@ -166,9 +173,30 @@ export async function execCommand(command: string, options: SpawnOptions = {}) {
return true; return true;
} }
export async function getNodeBinPath({ cwd }: { cwd: string }) { export async function getNodeBinPath({
const { stdout } = await execAsync('npm', ['bin'], { cwd }); cwd,
return stdout.trim(); }: {
cwd: string;
}): Promise<string | undefined> {
const { code, stdout, stderr } = await execAsync('npm', ['bin'], {
cwd,
prettyCommand: 'npm bin',
// in some rare cases, we saw `npm bin` exit with a non-0 code, but still
// output the right bin path, so we ignore the exit code
ignoreNon0Exit: true,
});
const nodeBinPath = stdout.trim();
if (path.isAbsolute(nodeBinPath)) {
return nodeBinPath;
}
throw new NowBuildError({
code: `BUILD_UTILS_GET_NODE_BIN_PATH`,
message: `Running \`npm bin\` failed to return a valid bin path (code=${code}, stdout=${stdout}, stderr=${stderr})`,
});
} }
async function chmodPlusX(fsPath: string) { async function chmodPlusX(fsPath: string) {

View File

@@ -0,0 +1,29 @@
import { execAsync, NowBuildError } from '../src';
it('should execute a command', async () => {
const { code, stdout, stderr } = await execAsync('echo', ['hello']);
expect(code).toBe(0);
expect(stdout).toContain('hello');
expect(stderr).toBe('');
});
it('should throw if the command exits with non-0 code', async () => {
await expect(execAsync('find', ['unknown-file'])).rejects.toBeInstanceOf(
NowBuildError
);
});
it('should return if the command exits with non-0 code and ignoreNon0Exit=true', async () => {
const { code, stdout, stderr } = await execAsync('find', ['unknown-file'], {
ignoreNon0Exit: true,
});
expect(code).toBe(process.platform === 'win32' ? 2 : 1);
expect(stdout).toBe('');
expect(stderr).toContain(
process.platform === 'win32'
? 'Parameter format not correct'
: 'No such file or directory'
);
});

View File

@@ -0,0 +1,21 @@
import { spawnAsync, NowBuildError } from '../src';
it('should execute a command', async () => {
// should resolve (it doesn't return anything, so it resolves with "undefined")
await expect(spawnAsync('echo', ['hello'])).resolves.toBeUndefined();
});
it('should throw if the command exits with non-0 code', async () => {
await expect(spawnAsync('find', ['unknown-file'])).rejects.toBeInstanceOf(
NowBuildError
);
});
it('should return if the command exits with non-0 code and ignoreNon0Exit=true', async () => {
// should resolve (it doesn't return anything, so it resolves with "undefined")
await expect(
spawnAsync('find', ['unknown-file'], {
ignoreNon0Exit: true,
})
).resolves.toBeUndefined();
});

View File

@@ -1,6 +1,6 @@
{ {
"name": "vercel", "name": "vercel",
"version": "25.2.0", "version": "25.2.3",
"preferGlobal": true, "preferGlobal": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "The command-line interface for Vercel", "description": "The command-line interface for Vercel",
@@ -42,15 +42,15 @@
"node": ">= 14" "node": ">= 14"
}, },
"dependencies": { "dependencies": {
"@vercel/build-utils": "4.2.0", "@vercel/build-utils": "4.2.1",
"@vercel/go": "2.0.2", "@vercel/go": "2.0.3",
"@vercel/next": "3.1.0", "@vercel/next": "3.1.2",
"@vercel/node": "2.3.0", "@vercel/node": "2.3.3",
"@vercel/python": "3.0.2", "@vercel/python": "3.0.3",
"@vercel/redwood": "1.0.2", "@vercel/redwood": "1.0.4",
"@vercel/remix": "1.0.2", "@vercel/remix": "1.0.4",
"@vercel/ruby": "1.3.10", "@vercel/ruby": "1.3.11",
"@vercel/static-build": "1.0.2", "@vercel/static-build": "1.0.3",
"update-notifier": "5.1.0" "update-notifier": "5.1.0"
}, },
"devDependencies": { "devDependencies": {
@@ -95,7 +95,7 @@
"@types/which": "1.3.2", "@types/which": "1.3.2",
"@types/write-json-file": "2.2.1", "@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0", "@types/yauzl-promise": "2.1.0",
"@vercel/client": "12.0.2", "@vercel/client": "12.0.3",
"@vercel/frameworks": "1.0.2", "@vercel/frameworks": "1.0.2",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"@zeit/fun": "0.11.2", "@zeit/fun": "0.11.2",

View File

@@ -10,7 +10,6 @@ import confirm from '../../util/input/confirm';
import findAliasByAliasOrId from '../../util/alias/find-alias-by-alias-or-id'; import findAliasByAliasOrId from '../../util/alias/find-alias-by-alias-or-id';
import { Alias } from '../../types'; import { Alias } from '../../types';
import { Output } from '../../util/output';
import { isValidName } from '../../util/is-valid-name'; import { isValidName } from '../../util/is-valid-name';
import { getCommandName } from '../../util/pkg-name'; import { getCommandName } from '../../util/pkg-name';
@@ -71,7 +70,7 @@ export default async function rm(
} }
const removeStamp = stamp(); const removeStamp = stamp();
if (!opts['--yes'] && !(await confirmAliasRemove(output, alias))) { if (!opts['--yes'] && !(await confirmAliasRemove(client, alias))) {
output.log('Aborted'); output.log('Aborted');
return 0; return 0;
} }
@@ -85,7 +84,7 @@ export default async function rm(
return 0; return 0;
} }
async function confirmAliasRemove(output: Output, alias: Alias) { async function confirmAliasRemove(client: Client, alias: Alias) {
const srcUrl = alias.deployment const srcUrl = alias.deployment
? chalk.underline(alias.deployment.url) ? chalk.underline(alias.deployment.url)
: null; : null;
@@ -104,7 +103,7 @@ async function confirmAliasRemove(output: Output, alias: Alias) {
} }
); );
output.log(`The following alias will be removed permanently`); client.output.log(`The following alias will be removed permanently`);
output.print(` ${tbl}\n`); client.output.print(` ${tbl}\n`);
return confirm(chalk.red('Are you sure?'), false); return confirm(client, chalk.red('Are you sure?'), false);
} }

View File

@@ -187,6 +187,7 @@ export default async client => {
if (cardId) { if (cardId) {
const label = `Are you sure that you to set this card as the default?`; const label = `Are you sure that you to set this card as the default?`;
const confirmation = await promptBool(label, { const confirmation = await promptBool(label, {
...client,
trailing: '\n', trailing: '\n',
}); });
@@ -262,7 +263,7 @@ export default async client => {
// typed `vercel billing rm <some-id>`) is valid // typed `vercel billing rm <some-id>`) is valid
if (cardId) { if (cardId) {
const label = `Are you sure that you want to remove this card?`; const label = `Are you sure that you want to remove this card?`;
const confirmation = await promptBool(label); const confirmation = await promptBool(label, client);
if (!confirmation) { if (!confirmation) {
console.log('Aborted'); console.log('Aborted');
break; break;

View File

@@ -140,6 +140,7 @@ export default async function main(client: Client): Promise<number> {
} }
confirmed = await confirm( confirmed = await confirm(
client,
`No Project Settings found locally. Run ${cli.getCommandName( `No Project Settings found locally. Run ${cli.getCommandName(
'pull' 'pull'
)} for retrieving them?`, )} for retrieving them?`,

View File

@@ -165,7 +165,7 @@ export default async (client: Client) => {
const quiet = !isTTY; const quiet = !isTTY;
// check paths // check paths
const pathValidation = await validatePaths(output, paths); const pathValidation = await validatePaths(client, paths);
if (!pathValidation.valid) { if (!pathValidation.valid) {
return pathValidation.exitCode; return pathValidation.exitCode;
@@ -243,6 +243,7 @@ export default async (client: Client) => {
const shouldStartSetup = const shouldStartSetup =
autoConfirm || autoConfirm ||
(await confirm( (await confirm(
client,
`Set up and deploy ${chalk.cyan(`${toHumanPath(path)}`)}?`, `Set up and deploy ${chalk.cyan(`${toHumanPath(path)}`)}?`,
true true
)); ));
@@ -287,7 +288,7 @@ export default async (client: Client) => {
if (typeof projectOrNewProjectName === 'string') { if (typeof projectOrNewProjectName === 'string') {
newProjectName = projectOrNewProjectName; newProjectName = projectOrNewProjectName;
rootDirectory = await inputRootDirectory(path, output, autoConfirm); rootDirectory = await inputRootDirectory(client, path, autoConfirm);
} else { } else {
project = projectOrNewProjectName; project = projectOrNewProjectName;
rootDirectory = project.rootDirectory; rootDirectory = project.rootDirectory;
@@ -521,7 +522,7 @@ export default async (client: Client) => {
} }
const settings = await editProjectSettings( const settings = await editProjectSettings(
output, client,
projectSettings, projectSettings,
framework, framework,
false, false,

View File

@@ -46,7 +46,7 @@ export default async function add(
const addStamp = stamp(); const addStamp = stamp();
const { domain, data: argData } = parsedParams; const { domain, data: argData } = parsedParams;
const data = await getDNSData(output, argData); const data = await getDNSData(client, argData);
if (!data) { if (!data) {
output.log(`Aborted`); output.log(`Aborted`);
return 1; return 1;

View File

@@ -87,7 +87,8 @@ export default async function buy(
!(await promptBool( !(await promptBool(
`Buy now for ${chalk.bold(`$${price}`)} (${`${period}yr${ `Buy now for ${chalk.bold(`$${price}`)} (${`${period}yr${
period > 1 ? 's' : '' period > 1 ? 's' : ''
}`})?` }`})?`,
client
)) ))
) { ) {
return 0; return 0;
@@ -99,7 +100,7 @@ export default async function buy(
: `Auto renew every ${renewalPrice.period} years for ${chalk.bold( : `Auto renew every ${renewalPrice.period} years for ${chalk.bold(
`$${price}` `$${price}`
)}?`, )}?`,
{ defaultValue: true } { ...client, defaultValue: true }
); );
let buyResult; let buyResult;

View File

@@ -77,7 +77,8 @@ export default async function move(
!(await promptBool( !(await promptBool(
`Are you sure you want to move ${param(domainName)} to ${param( `Are you sure you want to move ${param(domainName)} to ${param(
destination destination
)}?` )}?`,
client
)) ))
) { ) {
output.log('Aborted'); output.log('Aborted');
@@ -95,7 +96,8 @@ export default async function move(
); );
if ( if (
!(await promptBool( !(await promptBool(
`Are you sure you want to move ${param(domainName)}?` `Are you sure you want to move ${param(domainName)}?`,
client
)) ))
) { ) {
output.log('Aborted'); output.log('Aborted');

View File

@@ -92,7 +92,10 @@ export default async function rm(
const skipConfirmation = opts['--yes'] || false; const skipConfirmation = opts['--yes'] || false;
if ( if (
!skipConfirmation && !skipConfirmation &&
!(await promptBool(`Are you sure you want to remove ${param(domainName)}?`)) !(await promptBool(
`Are you sure you want to remove ${param(domainName)}?`,
client
))
) { ) {
output.log('Aborted'); output.log('Aborted');
return 0; return 0;
@@ -230,7 +233,7 @@ async function removeDomain(
if ( if (
!skipConfirmation && !skipConfirmation &&
!(await promptBool(`Remove conflicts associated with domain?`)) !(await promptBool(`Remove conflicts associated with domain?`, client))
) { ) {
output.log('Aborted'); output.log('Aborted');
return 0; return 0;

View File

@@ -81,7 +81,8 @@ export default async function transferIn(
const shouldTransfer = await promptBool( const shouldTransfer = await promptBool(
transferPolicy === 'no-change' transferPolicy === 'no-change'
? `Transfer now for ${chalk.bold(`$${price}`)}?` ? `Transfer now for ${chalk.bold(`$${price}`)}?`
: `Transfer now with 1yr renewal for ${chalk.bold(`$${price}`)}?` : `Transfer now with 1yr renewal for ${chalk.bold(`$${price}`)}?`,
client
); );
if (!shouldTransfer) { if (!shouldTransfer) {
return 0; return 0;

View File

@@ -31,7 +31,7 @@ export default async function add(
// improve the way we show inquirer prompts // improve the way we show inquirer prompts
require('../../util/input/patch-inquirer'); require('../../util/input/patch-inquirer');
const stdInput = await readStandardInput(); const stdInput = await readStandardInput(client.stdin);
let [envName, envTargetArg, envGitBranch] = args; let [envName, envTargetArg, envGitBranch] = args;
if (args.length > 3) { if (args.length > 3) {

View File

@@ -74,6 +74,7 @@ export default async function pull(
exists && exists &&
!skipConfirmation && !skipConfirmation &&
!(await confirm( !(await confirm(
client,
`Found existing file ${param(filename)}. Do you want to overwrite?`, `Found existing file ${param(filename)}. Do you want to overwrite?`,
false false
)) ))

View File

@@ -104,6 +104,7 @@ export default async function rm(
if ( if (
!skipConfirmation && !skipConfirmation &&
!(await confirm( !(await confirm(
client,
`Removing Environment Variable ${param(env.key)} from ${formatEnvTarget( `Removing Environment Variable ${param(env.key)} from ${formatEnvTarget(
env env
)} in Project ${chalk.bold(project.name)}. Are you sure?`, )} in Project ${chalk.bold(project.name)}. Are you sure?`,

View File

@@ -65,7 +65,7 @@ export default async function init(
return extractExample(client, name, dir, force, 'v1'); return extractExample(client, name, dir, force, 'v1');
} }
const found = await guess(exampleList, name); const found = await guess(client, exampleList, name);
if (typeof found === 'string') { if (typeof found === 'string') {
return extractExample(client, found, dir, force); return extractExample(client, found, dir, force);
@@ -194,7 +194,7 @@ function prepareFolder(cwd: string, folder: string, force?: boolean) {
/** /**
* Guess which example user try to init * Guess which example user try to init
*/ */
async function guess(exampleList: string[], name: string) { async function guess(client: Client, exampleList: string[], name: string) {
const GuessError = new Error( const GuessError = new Error(
`No example found for ${chalk.bold(name)}, run ${getCommandName( `No example found for ${chalk.bold(name)}, run ${getCommandName(
`init` `init`
@@ -208,7 +208,7 @@ async function guess(exampleList: string[], name: string) {
const found = didYouMean(name, exampleList, 0.7); const found = didYouMean(name, exampleList, 0.7);
if (typeof found === 'string') { if (typeof found === 'string') {
if (await promptBool(`Did you mean ${chalk.bold(found)}?`)) { if (await promptBool(`Did you mean ${chalk.bold(found)}?`, client)) {
return found; return found;
} }
} else { } else {

View File

@@ -387,6 +387,8 @@ const main = async () => {
// Shared API `Client` instance for all sub-commands to utilize // Shared API `Client` instance for all sub-commands to utilize
client = new Client({ client = new Client({
apiUrl, apiUrl,
stdin: process.stdin,
stdout: process.stdout,
output, output,
config, config,
authConfig, authConfig,

View File

@@ -385,8 +385,14 @@ export async function* findDirs(
} }
for (const path of paths) { for (const path of paths) {
const abs = join(dir, path); const abs = join(dir, path);
const s = await fs.stat(abs); let stat: fs.Stats;
if (s.isDirectory()) { try {
stat = await fs.lstat(abs);
} catch (err: any) {
if (err.code === 'ENOENT') continue;
throw err;
}
if (stat.isDirectory()) {
if (path === name) { if (path === name) {
yield relative(root, abs); yield relative(root, abs);
} else { } else {

View File

@@ -32,6 +32,8 @@ export interface ClientOptions {
argv: string[]; argv: string[];
apiUrl: string; apiUrl: string;
authConfig: AuthConfig; authConfig: AuthConfig;
stdin: NodeJS.ReadStream;
stdout: NodeJS.WriteStream;
output: Output; output: Output;
config: GlobalConfig; config: GlobalConfig;
localConfig?: VercelConfig; localConfig?: VercelConfig;
@@ -45,6 +47,8 @@ export default class Client extends EventEmitter {
argv: string[]; argv: string[];
apiUrl: string; apiUrl: string;
authConfig: AuthConfig; authConfig: AuthConfig;
stdin: NodeJS.ReadStream;
stdout: NodeJS.WriteStream;
output: Output; output: Output;
config: GlobalConfig; config: GlobalConfig;
localConfig?: VercelConfig; localConfig?: VercelConfig;
@@ -55,6 +59,8 @@ export default class Client extends EventEmitter {
this.argv = opts.argv; this.argv = opts.argv;
this.apiUrl = opts.apiUrl; this.apiUrl = opts.apiUrl;
this.authConfig = opts.authConfig; this.authConfig = opts.authConfig;
this.stdin = opts.stdin;
this.stdout = opts.stdout;
this.output = opts.output; this.output = opts.output;
this.config = opts.config; this.config = opts.config;
this.localConfig = opts.localConfig; this.localConfig = opts.localConfig;

View File

@@ -329,6 +329,8 @@ export default class DevServer {
): Promise<void> { ): Promise<void> {
const name = relative(this.cwd, fsPath); const name = relative(this.cwd, fsPath);
try { try {
await this.getVercelConfig();
this.files[name] = await FileFsRef.fromFsPath({ fsPath }); this.files[name] = await FileFsRef.fromFsPath({ fsPath });
const extensionless = this.getExtensionlessFile(name); const extensionless = this.getExtensionlessFile(name);
if (extensionless) { if (extensionless) {

View File

@@ -2,26 +2,29 @@ import chalk from 'chalk';
import { DNSRecordData } from '../../types'; import { DNSRecordData } from '../../types';
import textInput from '../input/text'; import textInput from '../input/text';
import promptBool from '../input/prompt-bool'; import promptBool from '../input/prompt-bool';
import { Output } from '../output'; import Client from '../client';
const RECORD_TYPES = ['A', 'AAAA', 'ALIAS', 'CAA', 'CNAME', 'MX', 'SRV', 'TXT']; const RECORD_TYPES = ['A', 'AAAA', 'ALIAS', 'CAA', 'CNAME', 'MX', 'SRV', 'TXT'];
export default async function getDNSData( export default async function getDNSData(
output: Output, client: Client,
data: null | DNSRecordData data: null | DNSRecordData
): Promise<DNSRecordData | null> { ): Promise<DNSRecordData | null> {
if (data) { if (data) {
return data; return data;
} }
const { output } = client;
try { try {
// first ask for type, branch from there // first ask for type, branch from there
const possibleTypes = new Set(RECORD_TYPES); const possibleTypes = new Set(RECORD_TYPES);
const type = (await textInput({ const type = (
label: `- Record type (${RECORD_TYPES.join(', ')}): `, await textInput({
validateValue: (v: string) => label: `- Record type (${RECORD_TYPES.join(', ')}): `,
Boolean(v && possibleTypes.has(v.trim().toUpperCase())) validateValue: (v: string) =>
})) Boolean(v && possibleTypes.has(v.trim().toUpperCase())),
})
)
.trim() .trim()
.toUpperCase(); .toUpperCase();
@@ -39,7 +42,7 @@ export default async function getDNSData(
target target
)}.` )}.`
); );
return (await verifyData()) return (await verifyData(client))
? { ? {
name, name,
type, type,
@@ -47,8 +50,8 @@ export default async function getDNSData(
priority, priority,
weight, weight,
port, port,
target target,
} },
} }
: null; : null;
} }
@@ -61,23 +64,23 @@ export default async function getDNSData(
`${mxPriority}` `${mxPriority}`
)} ${chalk.cyan(value)}` )} ${chalk.cyan(value)}`
); );
return (await verifyData()) return (await verifyData(client))
? { ? {
name, name,
type, type,
value, value,
mxPriority mxPriority,
} }
: null; : null;
} }
const value = await getTrimmedString(`- ${type} value: `); const value = await getTrimmedString(`- ${type} value: `);
output.log(`${chalk.cyan(name)} ${chalk.bold(type)} ${chalk.cyan(value)}`); output.log(`${chalk.cyan(name)} ${chalk.bold(type)} ${chalk.cyan(value)}`);
return (await verifyData()) return (await verifyData(client))
? { ? {
name, name,
type, type,
value value,
} }
: null; : null;
} catch (error) { } catch (error) {
@@ -85,13 +88,13 @@ export default async function getDNSData(
} }
} }
async function verifyData() { async function verifyData(client: Client) {
return promptBool('Is this correct?'); return promptBool('Is this correct?', client);
} }
async function getRecordName(type: string) { async function getRecordName(type: string) {
const input = await textInput({ const input = await textInput({
label: `- ${type} name: ` label: `- ${type} name: `,
}); });
return input === '@' ? '' : input; return input === '@' ? '' : input;
} }
@@ -100,14 +103,14 @@ async function getNumber(label: string) {
return Number( return Number(
await textInput({ await textInput({
label, label,
validateValue: v => Boolean(v && Number(v)) validateValue: v => Boolean(v && Number(v)),
}) })
); );
} }
async function getTrimmedString(label: string) { async function getTrimmedString(label: string) {
const res = await textInput({ const res = await textInput({
label, label,
validateValue: v => Boolean(v && v.trim().length > 0) validateValue: v => Boolean(v && v.trim().length > 0),
}); });
return res.trim(); return res.trim();
} }

View File

@@ -54,7 +54,8 @@ export default async function purchaseDomainIfAvailable(
!(await promptBool( !(await promptBool(
`Buy ${chalk.underline(domain)} for ${chalk.bold( `Buy ${chalk.underline(domain)} for ${chalk.bold(
`$${price}` `$${price}`
)} (${plural('yr', period, true)})?` )} (${plural('yr', period, true)})?`,
client
)) ))
) { ) {
output.print(eraseLines(1)); output.print(eraseLines(1));

View File

@@ -1,12 +1,19 @@
import inquirer from 'inquirer'; import inquirer from 'inquirer';
import Client from '../client';
export default async function confirm( export default async function confirm(
client: Client,
message: string, message: string,
preferred: boolean preferred: boolean
): Promise<boolean> { ): Promise<boolean> {
require('./patch-inquirer'); require('./patch-inquirer');
const answers = await inquirer.prompt({ const prompt = inquirer.createPromptModule({
input: client.stdin,
output: client.stdout,
});
const answers = await prompt({
type: 'confirm', type: 'confirm',
name: 'value', name: 'value',
message, message,

View File

@@ -1,8 +1,8 @@
import inquirer from 'inquirer'; import inquirer from 'inquirer';
import confirm from './confirm'; import confirm from './confirm';
import chalk from 'chalk'; import chalk from 'chalk';
import { Output } from '../output';
import frameworkList, { Framework } from '@vercel/frameworks'; import frameworkList, { Framework } from '@vercel/frameworks';
import Client from '../client';
import { isSettingValue } from '../is-setting-value'; import { isSettingValue } from '../is-setting-value';
import { ProjectSettings } from '../../types'; import { ProjectSettings } from '../../types';
@@ -22,12 +22,14 @@ const settingKeys = Object.keys(settingMap).sort() as unknown as readonly [
export type PartialProjectSettings = Pick<ProjectSettings, ConfigKeys>; export type PartialProjectSettings = Pick<ProjectSettings, ConfigKeys>;
export default async function editProjectSettings( export default async function editProjectSettings(
output: Output, client: Client,
projectSettings: PartialProjectSettings | null, projectSettings: PartialProjectSettings | null,
framework: Framework | null, framework: Framework | null,
autoConfirm: boolean, autoConfirm: boolean,
localConfigurationOverrides: PartialProjectSettings | null localConfigurationOverrides: PartialProjectSettings | null
): Promise<ProjectSettings> { ): Promise<ProjectSettings> {
const { output } = client;
// Create initial settings object defaulting everything to `null` and assigning what may exist in `projectSettings` // Create initial settings object defaulting everything to `null` and assigning what may exist in `projectSettings`
const settings: ProjectSettings = Object.assign( const settings: ProjectSettings = Object.assign(
{ {
@@ -118,7 +120,7 @@ export default async function editProjectSettings(
// Prompt the user if they want to modify any settings not defined by local configuration. // Prompt the user if they want to modify any settings not defined by local configuration.
if ( if (
autoConfirm || autoConfirm ||
!(await confirm('Want to modify these settings?', false)) !(await confirm(client, 'Want to modify these settings?', false))
) { ) {
return settings; return settings;
} }

View File

@@ -47,11 +47,16 @@ export default async function inputProject(
if (!detectedProject) { if (!detectedProject) {
// did not auto-detect a project to link // did not auto-detect a project to link
shouldLinkProject = await confirm(`Link to existing project?`, false); shouldLinkProject = await confirm(
client,
`Link to existing project?`,
false
);
} else { } else {
// auto-detected a project to link // auto-detected a project to link
if ( if (
await confirm( await confirm(
client,
`Found project ${chalk.cyan( `Found project ${chalk.cyan(
`${org.slug}/${detectedProject.name}` `${org.slug}/${detectedProject.name}`
)}. Link to it?`, )}. Link to it?`,
@@ -63,6 +68,7 @@ export default async function inputProject(
// user doesn't want to link the auto-detected project // user doesn't want to link the auto-detected project
shouldLinkProject = await confirm( shouldLinkProject = await confirm(
client,
`Link to different existing project?`, `Link to different existing project?`,
true true
); );
@@ -73,7 +79,11 @@ export default async function inputProject(
let project: Project | ProjectNotFound | null = null; let project: Project | ProjectNotFound | null = null;
while (!project || project instanceof ProjectNotFound) { while (!project || project instanceof ProjectNotFound) {
const answers = await inquirer.prompt({ const prompt = inquirer.createPromptModule({
input: client.stdin,
output: client.stdout,
});
const answers = await prompt({
type: 'input', type: 'input',
name: 'existingProjectName', name: 'existingProjectName',
message: `Whats the name of your existing project?`, message: `Whats the name of your existing project?`,

View File

@@ -1,12 +1,12 @@
import path from 'path'; import path from 'path';
import chalk from 'chalk'; import chalk from 'chalk';
import inquirer from 'inquirer'; import inquirer from 'inquirer';
import { Output } from '../output';
import { validateRootDirectory } from '../validate-paths'; import { validateRootDirectory } from '../validate-paths';
import Client from '../client';
export async function inputRootDirectory( export async function inputRootDirectory(
client: Client,
cwd: string, cwd: string,
output: Output,
autoConfirm = false autoConfirm = false
) { ) {
if (autoConfirm) { if (autoConfirm) {
@@ -15,7 +15,11 @@ export async function inputRootDirectory(
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while (true) { while (true) {
const { rootDirectory } = await inquirer.prompt({ const prompt = inquirer.createPromptModule({
input: client.stdin,
output: client.stdout,
});
const { rootDirectory } = await prompt({
type: 'input', type: 'input',
name: 'rootDirectory', name: 'rootDirectory',
message: `In which directory is your code located?`, message: `In which directory is your code located?`,
@@ -38,7 +42,7 @@ export async function inputRootDirectory(
if ( if (
(await validateRootDirectory( (await validateRootDirectory(
output, client.output,
cwd, cwd,
fullPath, fullPath,
'Please choose a different one.' 'Please choose a different one.'

View File

@@ -5,21 +5,21 @@ type Options = {
defaultValue?: boolean; defaultValue?: boolean;
noChar?: string; noChar?: string;
resolveChars?: Set<string>; resolveChars?: Set<string>;
stdin?: NodeJS.ReadStream; stdin: NodeJS.ReadStream;
stdout?: NodeJS.WriteStream; stdout: NodeJS.WriteStream;
trailing?: string; trailing?: string;
yesChar?: string; yesChar?: string;
}; };
export default async function promptBool(label: string, options: Options = {}) { export default async function promptBool(label: string, options: Options) {
const { const {
stdin,
stdout,
defaultValue = false, defaultValue = false,
abortSequences = new Set(['\u0003']), abortSequences = new Set(['\u0003']),
resolveChars = new Set(['\r']), resolveChars = new Set(['\r']),
yesChar = 'y', yesChar = 'y',
noChar = 'n', noChar = 'n',
stdin = process.stdin,
stdout = process.stdout,
trailing = '', trailing = '',
} = options; } = options;

View File

@@ -1,13 +1,15 @@
export default async function readStandardInput(): Promise<string> { export default async function readStandardInput(
stdin: NodeJS.ReadStream
): Promise<string> {
return new Promise<string>(resolve => { return new Promise<string>(resolve => {
setTimeout(() => resolve(''), 500); setTimeout(() => resolve(''), 500);
if (process.stdin.isTTY) { if (stdin.isTTY) {
// found tty so we know there is nothing piped to stdin // found tty so we know there is nothing piped to stdin
resolve(''); resolve('');
} else { } else {
process.stdin.setEncoding('utf8'); stdin.setEncoding('utf8');
process.stdin.once('data', resolve); stdin.once('data', resolve);
} }
}); });
} }

View File

@@ -80,6 +80,7 @@ export default async function setupAndLink(
const shouldStartSetup = const shouldStartSetup =
autoConfirm || autoConfirm ||
(await confirm( (await confirm(
client,
`${setupMsg} ${chalk.cyan(`${toHumanPath(path)}`)}?`, `${setupMsg} ${chalk.cyan(`${toHumanPath(path)}`)}?`,
true true
)); ));
@@ -120,7 +121,7 @@ export default async function setupAndLink(
if (typeof projectOrNewProjectName === 'string') { if (typeof projectOrNewProjectName === 'string') {
newProjectName = projectOrNewProjectName; newProjectName = projectOrNewProjectName;
rootDirectory = await inputRootDirectory(path, output, autoConfirm); rootDirectory = await inputRootDirectory(client, path, autoConfirm);
} else { } else {
const project = projectOrNewProjectName; const project = projectOrNewProjectName;
@@ -224,7 +225,7 @@ export default async function setupAndLink(
const { projectSettings, framework } = deployment; const { projectSettings, framework } = deployment;
settings = await editProjectSettings( settings = await editProjectSettings(
output, client,
projectSettings, projectSettings,
framework, framework,
autoConfirm, autoConfirm,

View File

@@ -14,7 +14,7 @@ export default async function reauthenticate(
client.output.log( client.output.log(
`You must re-authenticate with SAML to use ${bold(error.scope)} scope.` `You must re-authenticate with SAML to use ${bold(error.scope)} scope.`
); );
if (await confirm(`Log in with SAML?`, true)) { if (await confirm(client, `Log in with SAML?`, true)) {
return doSamlLogin(client, error.teamId); return doSamlLogin(client, error.teamId);
} }
} else { } else {

View File

@@ -5,6 +5,7 @@ import chalk from 'chalk';
import { homedir } from 'os'; import { homedir } from 'os';
import confirm from './input/confirm'; import confirm from './input/confirm';
import toHumanPath from './humanize-path'; import toHumanPath from './humanize-path';
import Client from './client';
const stat = promisify(lstatRaw); const stat = promisify(lstatRaw);
@@ -51,9 +52,11 @@ export async function validateRootDirectory(
} }
export default async function validatePaths( export default async function validatePaths(
output: Output, client: Client,
paths: string[] paths: string[]
): Promise<{ valid: true; path: string } | { valid: false; exitCode: number }> { ): Promise<{ valid: true; path: string } | { valid: false; exitCode: number }> {
const { output } = client;
// can't deploy more than 1 path // can't deploy more than 1 path
if (paths.length > 1) { if (paths.length > 1) {
output.print(`${chalk.red('Error!')} Can't deploy more than one path.\n`); output.print(`${chalk.red('Error!')} Can't deploy more than one path.\n`);
@@ -85,6 +88,7 @@ export default async function validatePaths(
// ask confirmation if the directory is home // ask confirmation if the directory is home
if (path === homedir()) { if (path === homedir()) {
const shouldDeployHomeDirectory = await confirm( const shouldDeployHomeDirectory = await confirm(
client,
`You are deploying your home directory. Do you want to continue?`, `You are deploying your home directory. Do you want to continue?`,
false false
); );

View File

@@ -2,7 +2,7 @@ export const config = {
runtime: 'experimental-edge' runtime: 'experimental-edge'
} }
export default async function edge(request: Request, event: Event) { export default async function edge(request, event) {
return new Response('some response body'); return new Response('some response body');
// intentional missing closing bracket to produce syntax error // intentional missing closing bracket to produce syntax error

View File

@@ -0,0 +1,3 @@
{
"version": 2
}

View File

@@ -161,14 +161,13 @@ test('[vercel dev] should handle startup errors thrown in edge functions', async
}); });
validateResponseHeaders(res); validateResponseHeaders(res);
const { stdout, stderr } = await dev.kill('SIGTERM'); const { stderr } = await dev.kill('SIGTERM');
expect(await res.text()).toMatch( expect(await res.text()).toMatch(
/<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g /<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g
); );
expect(stdout).toMatch( expect(stderr).toMatch(/Failed to instantiate edge runtime./g);
/Failed to instantiate edge runtime: intentional startup error/g expect(stderr).toMatch(/intentional startup error/g);
);
expect(stderr).toMatch( expect(stderr).toMatch(
/Failed to complete request to \/api\/edge-error-startup: Error: socket hang up/g /Failed to complete request to \/api\/edge-error-startup: Error: socket hang up/g
); );
@@ -193,14 +192,13 @@ test('[vercel dev] should handle syntax errors thrown in edge functions', async
}); });
validateResponseHeaders(res); validateResponseHeaders(res);
const { stdout, stderr } = await dev.kill('SIGTERM'); const { stderr } = await dev.kill('SIGTERM');
expect(await res.text()).toMatch( expect(await res.text()).toMatch(
/<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g /<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g
); );
expect(stdout).toMatch( expect(stderr).toMatch(/Failed to instantiate edge runtime./g);
/Failed to instantiate edge runtime: Module parse failed: Unexpected token/g expect(stderr).toMatch(/Unexpected end of file/g);
);
expect(stderr).toMatch( expect(stderr).toMatch(
/Failed to complete request to \/api\/edge-error-syntax: Error: socket hang up/g /Failed to complete request to \/api\/edge-error-syntax: Error: socket hang up/g
); );
@@ -228,13 +226,13 @@ test('[vercel dev] should handle import errors thrown in edge functions', async
); );
validateResponseHeaders(res); validateResponseHeaders(res);
const { stdout, stderr } = await dev.kill('SIGTERM'); const { stderr } = await dev.kill('SIGTERM');
expect(await res.text()).toMatch( expect(await res.text()).toMatch(
/<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g /<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g
); );
expect(stdout).toMatch( expect(stderr).toMatch(
/Failed to instantiate edge runtime: Code generation from strings disallowed for this context/g /Could not resolve "unknown-module-893427589372458934795843"/g
); );
expect(stderr).toMatch( expect(stderr).toMatch(
/Failed to complete request to \/api\/edge-error-unknown-import: Error: socket hang up/g /Failed to complete request to \/api\/edge-error-unknown-import: Error: socket hang up/g
@@ -244,7 +242,7 @@ test('[vercel dev] should handle import errors thrown in edge functions', async
} }
}); });
test('[vercel dev] should handle import errors thrown in edge functions', async () => { test('[vercel dev] should handle missing handler errors thrown in edge functions', async () => {
const dir = fixture('edge-function-error'); const dir = fixture('edge-function-error');
const { dev, port, readyResolver } = await testFixture(dir); const { dev, port, readyResolver } = await testFixture(dir);

View File

@@ -1,7 +1,17 @@
// eslint-disable-next-line // eslint-disable-next-line
import path from 'path'; import { join } from 'path';
import ms from 'ms';
import fs, { mkdirp } from 'fs-extra';
const { exec, fixture, testFixture, testFixtureStdio } = require('./utils.js'); const {
exec,
fetch,
fixture,
sleep,
testFixture,
testFixtureStdio,
validateResponseHeaders,
} = require('./utils.js');
test('[vercel dev] validate redirects', async () => { test('[vercel dev] validate redirects', async () => {
const directory = fixture('invalid-redirects'); const directory = fixture('invalid-redirects');
@@ -334,3 +344,44 @@ test(
await testPath(200, '/', /A simple deployment with the Vercel API!/m); await testPath(200, '/', /A simple deployment with the Vercel API!/m);
}) })
); );
test(
'[vercel dev] add a `api/fn.ts` when `api` does not exist at startup`',
testFixtureStdio('no-api', async (_testPath: any, port: any) => {
const directory = fixture('no-api');
const apiDir = join(directory, 'api');
try {
{
const response = await fetch(`http://localhost:${port}/api/new-file`);
validateResponseHeaders(response);
expect(response.status).toBe(404);
}
const fileContents = `
export const config = {
runtime: 'experimental-edge'
}
export default async function edge(request, event) {
return new Response('from new file');
}
`;
await mkdirp(apiDir);
await fs.writeFile(join(apiDir, 'new-file.js'), fileContents);
// Wait until file events have been processed
await sleep(ms('1s'));
{
const response = await fetch(`http://localhost:${port}/api/new-file`);
validateResponseHeaders(response);
const body = await response.text();
expect(body.trim()).toBe('from new file');
}
} finally {
await fs.remove(apiDir);
}
})
);

View File

@@ -508,18 +508,10 @@ test(
test( test(
'[vercel dev] Middleware with `matchers` config', '[vercel dev] Middleware with `matchers` config',
testFixtureStdio( testFixtureStdio('middleware-matchers', async (testPath: any) => {
'middleware-matchers', await testPath(404, '/');
async (testPath: any) => { await testPath(404, '/another');
// TODO: remove once latest `@vercel/node` is shipped to stable with `matchers` support (fails because `directoryListing`) await testPath(200, '/about/page', 'middleware response');
//await testPath(404, '/'); await testPath(200, '/dashboard/home', 'middleware response');
await testPath(404, '/another'); })
await testPath(200, '/about/page', 'middleware response');
await testPath(200, '/dashboard/home', 'middleware response');
},
{
// TODO: remove once latest `@vercel/node` is shipped to stable with `matchers` support
skipDeploy: true,
}
)
); );

View File

@@ -1,4 +1,5 @@
import chalk from 'chalk'; import chalk from 'chalk';
import { PassThrough } from 'stream';
import { createServer, Server } from 'http'; import { createServer, Server } from 'http';
import express, { Express, Router } from 'express'; import express, { Express, Router } from 'express';
import listen from 'async-listen'; import listen from 'async-listen';
@@ -23,10 +24,13 @@ export class MockClient extends Client {
// Gets populated in `startMockServer()` // Gets populated in `startMockServer()`
apiUrl: '', apiUrl: '',
authConfig: {}, authConfig: {},
stdin: new PassThrough(),
stdout: new PassThrough(),
output: new Output(), output: new Output(),
config: {}, config: {},
localConfig: {}, localConfig: {},
}); });
this.mockOutput = jest.fn(); this.mockOutput = jest.fn();
this.app = express(); this.app = express();
@@ -53,6 +57,12 @@ export class MockClient extends Client {
} }
reset() { reset() {
this.stdin = new PassThrough();
this.stdin.isTTY = true;
this.stdout = new PassThrough();
this.stdout.isTTY = true;
this.output = new Output(); this.output = new Output();
this.mockOutput = jest.fn(); this.mockOutput = jest.fn();
this.output.print = s => { this.output.print = s => {

View File

@@ -0,0 +1,25 @@
import confirm from '../../src/util/input/confirm';
import { client } from '../mocks/client';
describe('MockClient', () => {
it('should mock `confirm()`', async () => {
// true
let confirmedPromise = confirm(client, 'Do the thing?', false);
client.stdin.write('yes\n');
client.stdout.setEncoding('utf8');
client.stdout.on('data', d => console.log({ d }));
let confirmed = await confirmedPromise;
expect(confirmed).toEqual(true);
// false
confirmedPromise = confirm(client, 'Do the thing?', false);
client.stdin.write('no\n');
confirmed = await confirmedPromise;
expect(confirmed).toEqual(false);
});
});

View File

@@ -1,13 +1,6 @@
import { Framework, frameworks } from '@vercel/frameworks'; import { Framework, frameworks } from '@vercel/frameworks';
import editProjectSettings from '../../../../src/util/input/edit-project-settings'; import editProjectSettings from '../../../../src/util/input/edit-project-settings';
import { Output } from '../../../../src/util/output'; import { client } from '../../../mocks/client';
let output: Output;
beforeEach(() => {
output = new Output();
output.print = jest.fn();
});
const otherFramework = frameworks.find( const otherFramework = frameworks.find(
fwk => fwk.name === 'Other' fwk => fwk.name === 'Other'
@@ -20,7 +13,7 @@ describe('editProjectSettings', () => {
describe('with no settings, "Other" framework, and no overrides provided', () => { describe('with no settings, "Other" framework, and no overrides provided', () => {
test('should default all settings to `null` and print user default framework settings', async () => { test('should default all settings to `null` and print user default framework settings', async () => {
const settings = await editProjectSettings( const settings = await editProjectSettings(
output, client,
null, null,
otherFramework, otherFramework,
true, true,
@@ -34,22 +27,14 @@ describe('editProjectSettings', () => {
installCommand: null, installCommand: null,
outputDirectory: null, outputDirectory: null,
}); });
expect((output.print as jest.Mock).mock.calls.length).toBe(5); expect(client.mockOutput.mock.calls.length).toBe(5);
expect((output.print as jest.Mock).mock.calls[0][0]).toMatch( expect(client.mockOutput.mock.calls[0][0]).toMatch(
/No framework detected. Default Project Settings:/ /No framework detected. Default Project Settings:/
); );
expect((output.print as jest.Mock).mock.calls[1][0]).toMatch( expect(client.mockOutput.mock.calls[1][0]).toMatch(/Build Command/);
/Build Command/ expect(client.mockOutput.mock.calls[2][0]).toMatch(/Development Command/);
); expect(client.mockOutput.mock.calls[3][0]).toMatch(/Install Command/);
expect((output.print as jest.Mock).mock.calls[2][0]).toMatch( expect(client.mockOutput.mock.calls[4][0]).toMatch(/Output Directory/);
/Development Command/
);
expect((output.print as jest.Mock).mock.calls[3][0]).toMatch(
/Install Command/
);
expect((output.print as jest.Mock).mock.calls[4][0]).toMatch(
/Output Directory/
);
}); });
}); });
@@ -63,29 +48,21 @@ describe('editProjectSettings', () => {
outputDirectory: 'OUTPUT_DIRECTORY', outputDirectory: 'OUTPUT_DIRECTORY',
}; };
const settings = await editProjectSettings( const settings = await editProjectSettings(
output, client,
projectSettings, projectSettings,
otherFramework, otherFramework,
true, true,
null null
); );
expect(settings).toStrictEqual({ ...projectSettings, framework: null }); expect(settings).toStrictEqual({ ...projectSettings, framework: null });
expect((output.print as jest.Mock).mock.calls.length).toBe(5); expect(client.mockOutput.mock.calls.length).toBe(5);
expect((output.print as jest.Mock).mock.calls[0][0]).toMatch( expect(client.mockOutput.mock.calls[0][0]).toMatch(
/No framework detected. Default Project Settings:/ /No framework detected. Default Project Settings:/
); );
expect((output.print as jest.Mock).mock.calls[1][0]).toMatch( expect(client.mockOutput.mock.calls[1][0]).toMatch(/Build Command/);
/Build Command/ expect(client.mockOutput.mock.calls[2][0]).toMatch(/Development Command/);
); expect(client.mockOutput.mock.calls[3][0]).toMatch(/Install Command/);
expect((output.print as jest.Mock).mock.calls[2][0]).toMatch( expect(client.mockOutput.mock.calls[4][0]).toMatch(/Output Directory/);
/Development Command/
);
expect((output.print as jest.Mock).mock.calls[3][0]).toMatch(
/Install Command/
);
expect((output.print as jest.Mock).mock.calls[4][0]).toMatch(
/Output Directory/
);
}); });
}); });
@@ -99,28 +76,20 @@ describe('editProjectSettings', () => {
outputDirectory: 'OUTPUT_DIRECTORY', outputDirectory: 'OUTPUT_DIRECTORY',
}; };
const settings = await editProjectSettings( const settings = await editProjectSettings(
output, client,
projectSettings, projectSettings,
nextJSFramework, nextJSFramework,
true, true,
null null
); );
expect((output.print as jest.Mock).mock.calls.length).toBe(5); expect(client.mockOutput.mock.calls.length).toBe(5);
expect((output.print as jest.Mock).mock.calls[0][0]).toMatch( expect(client.mockOutput.mock.calls[0][0]).toMatch(
/Auto-detected Project Settings/ /Auto-detected Project Settings/
); );
expect((output.print as jest.Mock).mock.calls[1][0]).toMatch( expect(client.mockOutput.mock.calls[1][0]).toMatch(/Build Command/);
/Build Command/ expect(client.mockOutput.mock.calls[2][0]).toMatch(/Development Command/);
); expect(client.mockOutput.mock.calls[3][0]).toMatch(/Install Command/);
expect((output.print as jest.Mock).mock.calls[2][0]).toMatch( expect(client.mockOutput.mock.calls[4][0]).toMatch(/Output Directory/);
/Development Command/
);
expect((output.print as jest.Mock).mock.calls[3][0]).toMatch(
/Install Command/
);
expect((output.print as jest.Mock).mock.calls[4][0]).toMatch(
/Output Directory/
);
expect(settings).toStrictEqual({ expect(settings).toStrictEqual({
...projectSettings, ...projectSettings,
framework: nextJSFramework.slug, framework: nextJSFramework.slug,
@@ -146,38 +115,28 @@ describe('editProjectSettings', () => {
outputDirectory: 'OUTPUT_DIRECTORY', outputDirectory: 'OUTPUT_DIRECTORY',
}; };
const settings = await editProjectSettings( const settings = await editProjectSettings(
output, client,
projectSettings, projectSettings,
nextJSFramework, nextJSFramework,
true, true,
overrides overrides
); );
expect((output.print as jest.Mock).mock.calls.length).toBe(9); expect(client.mockOutput.mock.calls.length).toBe(9);
expect((output.print as jest.Mock).mock.calls[0][0]).toMatch( expect(client.mockOutput.mock.calls[0][0]).toMatch(
/Local settings detected in vercel.json:/ /Local settings detected in vercel.json:/
); );
expect((output.print as jest.Mock).mock.calls[1][0]).toMatch( expect(client.mockOutput.mock.calls[1][0]).toMatch(/Build Command:/);
/Build Command:/ expect(client.mockOutput.mock.calls[2][0]).toMatch(/Ignore Command:/);
); expect(client.mockOutput.mock.calls[3][0]).toMatch(
expect((output.print as jest.Mock).mock.calls[2][0]).toMatch(
/Ignore Command:/
);
expect((output.print as jest.Mock).mock.calls[3][0]).toMatch(
/Development Command:/ /Development Command:/
); );
expect((output.print as jest.Mock).mock.calls[4][0]).toMatch( expect(client.mockOutput.mock.calls[4][0]).toMatch(/Framework:/);
/Framework:/ expect(client.mockOutput.mock.calls[5][0]).toMatch(/Install Command:/);
); expect(client.mockOutput.mock.calls[6][0]).toMatch(/Output Directory:/);
expect((output.print as jest.Mock).mock.calls[5][0]).toMatch( expect(client.mockOutput.mock.calls[7][0]).toMatch(
/Install Command:/
);
expect((output.print as jest.Mock).mock.calls[6][0]).toMatch(
/Output Directory:/
);
expect((output.print as jest.Mock).mock.calls[7][0]).toMatch(
/Merging default Project Settings for Svelte. Previously listed overrides are prioritized./ /Merging default Project Settings for Svelte. Previously listed overrides are prioritized./
); );
expect((output.print as jest.Mock).mock.calls[8][0]).toMatch( expect(client.mockOutput.mock.calls[8][0]).toMatch(
/Auto-detected Project Settings/ /Auto-detected Project Settings/
); );
@@ -196,38 +155,28 @@ describe('editProjectSettings', () => {
outputDirectory: 'OUTPUT_DIRECTORY', outputDirectory: 'OUTPUT_DIRECTORY',
}; };
const settings = await editProjectSettings( const settings = await editProjectSettings(
output, client,
null, null,
nextJSFramework, nextJSFramework,
true, true,
overrides overrides
); );
expect((output.print as jest.Mock).mock.calls.length).toBe(9); expect(client.mockOutput.mock.calls.length).toBe(9);
expect((output.print as jest.Mock).mock.calls[0][0]).toMatch( expect(client.mockOutput.mock.calls[0][0]).toMatch(
/Local settings detected in vercel.json:/ /Local settings detected in vercel.json:/
); );
expect((output.print as jest.Mock).mock.calls[1][0]).toMatch( expect(client.mockOutput.mock.calls[1][0]).toMatch(/Build Command:/);
/Build Command:/ expect(client.mockOutput.mock.calls[2][0]).toMatch(/Ignore Command:/);
); expect(client.mockOutput.mock.calls[3][0]).toMatch(
expect((output.print as jest.Mock).mock.calls[2][0]).toMatch(
/Ignore Command:/
);
expect((output.print as jest.Mock).mock.calls[3][0]).toMatch(
/Development Command:/ /Development Command:/
); );
expect((output.print as jest.Mock).mock.calls[4][0]).toMatch( expect(client.mockOutput.mock.calls[4][0]).toMatch(/Framework:/);
/Framework:/ expect(client.mockOutput.mock.calls[5][0]).toMatch(/Install Command:/);
); expect(client.mockOutput.mock.calls[6][0]).toMatch(/Output Directory:/);
expect((output.print as jest.Mock).mock.calls[5][0]).toMatch( expect(client.mockOutput.mock.calls[7][0]).toMatch(
/Install Command:/
);
expect((output.print as jest.Mock).mock.calls[6][0]).toMatch(
/Output Directory:/
);
expect((output.print as jest.Mock).mock.calls[7][0]).toMatch(
/Merging default Project Settings for Svelte. Previously listed overrides are prioritized./ /Merging default Project Settings for Svelte. Previously listed overrides are prioritized./
); );
expect((output.print as jest.Mock).mock.calls[8][0]).toMatch( expect(client.mockOutput.mock.calls[8][0]).toMatch(
/Auto-detected Project Settings/ /Auto-detected Project Settings/
); );
expect(settings).toStrictEqual(overrides); expect(settings).toStrictEqual(overrides);
@@ -245,38 +194,28 @@ describe('editProjectSettings', () => {
outputDirectory: 'OUTPUT_DIRECTORY', outputDirectory: 'OUTPUT_DIRECTORY',
}; };
const settings = await editProjectSettings( const settings = await editProjectSettings(
output, client,
null, null,
null, null,
true, true,
overrides overrides
); );
expect((output.print as jest.Mock).mock.calls.length).toBe(9); expect(client.mockOutput.mock.calls.length).toBe(9);
expect((output.print as jest.Mock).mock.calls[0][0]).toMatch( expect(client.mockOutput.mock.calls[0][0]).toMatch(
/Local settings detected in vercel.json:/ /Local settings detected in vercel.json:/
); );
expect((output.print as jest.Mock).mock.calls[1][0]).toMatch( expect(client.mockOutput.mock.calls[1][0]).toMatch(/Build Command:/);
/Build Command:/ expect(client.mockOutput.mock.calls[2][0]).toMatch(/Ignore Command:/);
); expect(client.mockOutput.mock.calls[3][0]).toMatch(
expect((output.print as jest.Mock).mock.calls[2][0]).toMatch(
/Ignore Command:/
);
expect((output.print as jest.Mock).mock.calls[3][0]).toMatch(
/Development Command:/ /Development Command:/
); );
expect((output.print as jest.Mock).mock.calls[4][0]).toMatch( expect(client.mockOutput.mock.calls[4][0]).toMatch(/Framework:/);
/Framework:/ expect(client.mockOutput.mock.calls[5][0]).toMatch(/Install Command:/);
); expect(client.mockOutput.mock.calls[6][0]).toMatch(/Output Directory:/);
expect((output.print as jest.Mock).mock.calls[5][0]).toMatch( expect(client.mockOutput.mock.calls[7][0]).toMatch(
/Install Command:/
);
expect((output.print as jest.Mock).mock.calls[6][0]).toMatch(
/Output Directory:/
);
expect((output.print as jest.Mock).mock.calls[7][0]).toMatch(
/Merging default Project Settings for Svelte. Previously listed overrides are prioritized./ /Merging default Project Settings for Svelte. Previously listed overrides are prioritized./
); );
expect((output.print as jest.Mock).mock.calls[8][0]).toMatch( expect(client.mockOutput.mock.calls[8][0]).toMatch(
/Auto-detected Project Settings/ /Auto-detected Project Settings/
); );

View File

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

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/go", "name": "@vercel/go",
"version": "2.0.2", "version": "2.0.3",
"license": "MIT", "license": "MIT",
"main": "./dist/index", "main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go", "homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
@@ -25,7 +25,7 @@
"@types/fs-extra": "^5.0.5", "@types/fs-extra": "^5.0.5",
"@types/node-fetch": "^2.3.0", "@types/node-fetch": "^2.3.0",
"@types/tar": "^4.0.0", "@types/tar": "^4.0.0",
"@vercel/build-utils": "4.2.0", "@vercel/build-utils": "4.2.1",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"async-retry": "1.3.1", "async-retry": "1.3.1",
"execa": "^1.0.0", "execa": "^1.0.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/next", "name": "@vercel/next",
"version": "3.1.0", "version": "3.1.2",
"license": "MIT", "license": "MIT",
"main": "./dist/index", "main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js", "homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
@@ -45,8 +45,8 @@
"@types/semver": "6.0.0", "@types/semver": "6.0.0",
"@types/text-table": "0.2.1", "@types/text-table": "0.2.1",
"@types/webpack-sources": "3.2.0", "@types/webpack-sources": "3.2.0",
"@vercel/build-utils": "4.2.0", "@vercel/build-utils": "4.2.1",
"@vercel/nft": "0.19.1", "@vercel/nft": "0.20.1",
"@vercel/routing-utils": "1.13.5", "@vercel/routing-utils": "1.13.5",
"async-sema": "3.0.1", "async-sema": "3.0.1",
"buffer-crc32": "0.2.13", "buffer-crc32": "0.2.13",

View File

@@ -1520,35 +1520,35 @@ export const build: BuildV2 = async ({
const pageLambdaGroups: Array<LambdaGroup> = []; const pageLambdaGroups: Array<LambdaGroup> = [];
if (isSharedLambdas) { if (isSharedLambdas) {
const initialPageLambdaGroups = await getPageLambdaGroups( const initialPageLambdaGroups = await getPageLambdaGroups({
entryPath, entryPath,
config, config,
nonApiPages, pages: nonApiPages,
new Set(), prerenderRoutes: new Set(),
pageTraces, pageTraces,
compressedPages, compressedPages,
tracedPseudoLayer?.pseudoLayer || {}, tracedPseudoLayer: tracedPseudoLayer?.pseudoLayer || {},
{ pseudoLayer: {}, pseudoLayerBytes: 0 }, initialPseudoLayer: { pseudoLayer: {}, pseudoLayerBytes: 0 },
0, initialPseudoLayerUncompressed: 0,
lambdaCompressedByteLimit, lambdaCompressedByteLimit,
// internal pages are already referenced in traces for serverless // internal pages are already referenced in traces for serverless
// like builds // like builds
[] internalPages: [],
); });
const initialApiLambdaGroups = await getPageLambdaGroups( const initialApiLambdaGroups = await getPageLambdaGroups({
entryPath, entryPath,
config, config,
apiPages, pages: apiPages,
new Set(), prerenderRoutes: new Set(),
pageTraces, pageTraces,
compressedPages, compressedPages,
tracedPseudoLayer?.pseudoLayer || {}, tracedPseudoLayer: tracedPseudoLayer?.pseudoLayer || {},
{ pseudoLayer: {}, pseudoLayerBytes: 0 }, initialPseudoLayer: { pseudoLayer: {}, pseudoLayerBytes: 0 },
0, initialPseudoLayerUncompressed: 0,
lambdaCompressedByteLimit, lambdaCompressedByteLimit,
[] internalPages: [],
); });
debug( debug(
JSON.stringify( JSON.stringify(

View File

@@ -610,34 +610,34 @@ export async function serverBuild({
const pageExtensions = requiredServerFilesManifest.config?.pageExtensions; const pageExtensions = requiredServerFilesManifest.config?.pageExtensions;
const pageLambdaGroups = await getPageLambdaGroups( const pageLambdaGroups = await getPageLambdaGroups({
requiredServerFilesManifest.appDir || entryPath, entryPath: requiredServerFilesManifest.appDir || entryPath,
config, config,
nonApiPages, pages: nonApiPages,
prerenderRoutes, prerenderRoutes,
pageTraces, pageTraces,
compressedPages, compressedPages,
tracedPseudoLayer.pseudoLayer, tracedPseudoLayer: tracedPseudoLayer.pseudoLayer,
initialPseudoLayer, initialPseudoLayer,
lambdaCompressedByteLimit, lambdaCompressedByteLimit,
uncompressedInitialSize, initialPseudoLayerUncompressed: uncompressedInitialSize,
internalPages, internalPages,
pageExtensions pageExtensions,
); });
const apiLambdaGroups = await getPageLambdaGroups( const apiLambdaGroups = await getPageLambdaGroups({
requiredServerFilesManifest.appDir || entryPath, entryPath: requiredServerFilesManifest.appDir || entryPath,
config, config,
apiPages, pages: apiPages,
prerenderRoutes, prerenderRoutes,
pageTraces, pageTraces,
compressedPages, compressedPages,
tracedPseudoLayer.pseudoLayer, tracedPseudoLayer: tracedPseudoLayer.pseudoLayer,
initialPseudoLayer, initialPseudoLayer,
uncompressedInitialSize, initialPseudoLayerUncompressed: uncompressedInitialSize,
lambdaCompressedByteLimit, lambdaCompressedByteLimit,
internalPages internalPages,
); });
debug( debug(
JSON.stringify( JSON.stringify(

View File

@@ -1275,26 +1275,39 @@ export const MAX_UNCOMPRESSED_LAMBDA_SIZE = 250 * 1000 * 1000; // 250MB
const LAMBDA_RESERVED_UNCOMPRESSED_SIZE = 2.5 * 1000 * 1000; // 2.5MB const LAMBDA_RESERVED_UNCOMPRESSED_SIZE = 2.5 * 1000 * 1000; // 2.5MB
const LAMBDA_RESERVED_COMPRESSED_SIZE = 250 * 1000; // 250KB const LAMBDA_RESERVED_COMPRESSED_SIZE = 250 * 1000; // 250KB
export async function getPageLambdaGroups( export async function getPageLambdaGroups({
entryPath: string, entryPath,
config: Config, config,
pages: string[], pages,
prerenderRoutes: Set<string>, prerenderRoutes,
pageTraces,
compressedPages,
tracedPseudoLayer,
initialPseudoLayer,
initialPseudoLayerUncompressed,
lambdaCompressedByteLimit,
internalPages,
pageExtensions,
}: {
entryPath: string;
config: Config;
pages: string[];
prerenderRoutes: Set<string>;
pageTraces: { pageTraces: {
[page: string]: { [page: string]: {
[key: string]: FileFsRef; [key: string]: FileFsRef;
}; };
}, };
compressedPages: { compressedPages: {
[page: string]: PseudoFile; [page: string]: PseudoFile;
}, };
tracedPseudoLayer: PseudoLayer, tracedPseudoLayer: PseudoLayer;
initialPseudoLayer: PseudoLayerResult, initialPseudoLayer: PseudoLayerResult;
initialPseudoLayerUncompressed: number, initialPseudoLayerUncompressed: number;
lambdaCompressedByteLimit: number, lambdaCompressedByteLimit: number;
internalPages: string[], internalPages: string[];
pageExtensions?: string[] pageExtensions?: string[];
) { }) {
const groups: Array<LambdaGroup> = []; const groups: Array<LambdaGroup> = [];
for (const page of pages) { for (const page of pages) {

View File

@@ -105,6 +105,21 @@ it('should build using server build', async () => {
log.includes('WARNING: Unable to find source file for page') log.includes('WARNING: Unable to find source file for page')
) )
).toBeFalsy(); ).toBeFalsy();
const lambdas = new Set();
let totalLambdas = 0;
for (const item of Object.values(output)) {
if (item.type === 'Lambda') {
totalLambdas += 1;
lambdas.add(item);
} else if (item.type === 'Prerender') {
lambdas.add(item.lambda);
totalLambdas += 1;
}
}
expect(lambdas.size).toBe(5);
expect(lambdas.size).toBeLessThan(totalLambdas);
}); });
it('should build custom error lambda correctly', async () => { it('should build custom error lambda correctly', async () => {
@@ -835,3 +850,66 @@ it('Should provide lambda info when limit is hit (uncompressed)', async () => {
expect(logs).toMatch(/data\.txt/); expect(logs).toMatch(/data\.txt/);
expect(logs).toMatch(/\.next\/server\/pages/); expect(logs).toMatch(/\.next\/server\/pages/);
}); });
it('Should de-dupe correctly when limit is close (uncompressed)', async () => {
const origLog = console.log;
const origError = console.error;
const caughtLogs = [];
console.log = function (...args) {
caughtLogs.push(args.join(' '));
origLog.apply(this, args);
};
console.error = function (...args) {
caughtLogs.push(args.join(' '));
origError.apply(this, args);
};
const {
buildResult: { output },
} = await runBuildLambda(
path.join(__dirname, 'test-limit-large-uncompressed-files')
);
console.log = origLog;
console.error = origError;
expect(output['index']).toBeDefined();
expect(output['another']).toBeDefined();
expect(output['api/hello']).toBeDefined();
expect(output['api/hello-1']).toBeDefined();
expect(output['api/hello-2']).toBeDefined();
expect(output['api/hello-3']).toBeDefined();
expect(output['api/hello-4']).toBeDefined();
expect(output['_app']).not.toBeDefined();
expect(output['_error']).not.toBeDefined();
expect(output['_document']).not.toBeDefined();
expect(output['index'] === output['another']).toBe(true);
expect(output['index'] !== output['api/hello']).toBe(true);
expect(output['api/hello'] === output['api/hello-1']).toBe(true);
expect(output['api/hello'] === output['api/hello-2']).toBe(true);
expect(output['api/hello'] === output['api/hello-3']).toBe(true);
expect(output['api/hello'] === output['api/hello-4']).toBe(true);
expect(
caughtLogs.some(log =>
log.includes('WARNING: Unable to find source file for page')
)
).toBeFalsy();
const lambdas = new Set();
let totalLambdas = 0;
for (const item of Object.values(output)) {
if (item.type === 'Lambda') {
totalLambdas += 1;
lambdas.add(item);
} else if (item.type === 'Prerender') {
lambdas.add(item.lambda);
totalLambdas += 1;
}
}
expect(lambdas.size).toBe(2);
expect(lambdas.size).toBeLessThan(totalLambdas);
});

View File

@@ -0,0 +1,6 @@
const fs = require('fs');
// generate 200MB file which will be traced in `/api/hello`
fs.writeFileSync('data.txt', Buffer.alloc(200 * 1024 * 1024));
module.exports = {};

View File

@@ -0,0 +1,12 @@
{
"name": "test-limit",
"version": "1.0.0",
"scripts": {
"build": "next build"
},
"dependencies": {
"next": "canary",
"react": "17.0.2",
"react-dom": "17.0.2"
}
}

View File

@@ -0,0 +1,16 @@
/* eslint-disable */
import React from 'react';
if (typeof window === 'undefined') {
try {
const fs = require('fs');
const path = require('path');
fs.readdirSync(path.join(process.cwd(), 'public'));
fs.readdirSync(path.join(process.cwd(), 'node_modules/chrome-aws-lambda'));
fs.readdirSync(path.join(process.cwd(), 'node_modules/firebase'));
} catch (_) {}
}
export default function MyApp({ Component, pageProps }) {
return React.createElement(Component, pageProps);
}

View File

@@ -0,0 +1,10 @@
export default function Home() {
return 'another page';
}
export function getServerSideProps() {
require('fs').readFileSync(require('path').join(process.cwd(), 'data.txt'));
return {
props: {},
};
}

View File

@@ -0,0 +1,12 @@
import fs from 'fs';
import path from 'path';
try {
fs.readFileSync(path.join(process.cwd(), 'data.txt'));
} catch (_) {
/**/
}
export default function handler(req, res) {
res.end('hello');
}

View File

@@ -0,0 +1,12 @@
import fs from 'fs';
import path from 'path';
try {
fs.readFileSync(path.join(process.cwd(), 'data.txt'));
} catch (_) {
/**/
}
export default function handler(req, res) {
res.end('hello');
}

View File

@@ -0,0 +1,12 @@
import fs from 'fs';
import path from 'path';
try {
fs.readFileSync(path.join(process.cwd(), 'data.txt'));
} catch (_) {
/**/
}
export default function handler(req, res) {
res.end('hello');
}

View File

@@ -0,0 +1,12 @@
import fs from 'fs';
import path from 'path';
try {
fs.readFileSync(path.join(process.cwd(), 'data.txt'));
} catch (_) {
/**/
}
export default function handler(req, res) {
res.end('hello');
}

View File

@@ -0,0 +1,12 @@
import fs from 'fs';
import path from 'path';
try {
fs.readFileSync(path.join(process.cwd(), 'data.txt'));
} catch (_) {
/**/
}
export default function handler(req, res) {
res.end('hello');
}

View File

@@ -0,0 +1,10 @@
export default function Home() {
return 'index page';
}
export function getServerSideProps() {
require('fs').readFileSync(require('path').join(process.cwd(), 'data.txt'));
return {
props: {},
};
}

View File

@@ -0,0 +1,9 @@
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/next"
}
]
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/node", "name": "@vercel/node",
"version": "2.3.0", "version": "2.3.3",
"license": "MIT", "license": "MIT",
"main": "./dist/index", "main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js", "homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
@@ -31,11 +31,11 @@
}, },
"dependencies": { "dependencies": {
"@types/node": "*", "@types/node": "*",
"@vercel/build-utils": "4.2.0", "@vercel/build-utils": "4.2.1",
"@vercel/ncc": "0.24.0",
"@vercel/node-bridge": "3.0.0", "@vercel/node-bridge": "3.0.0",
"@vercel/static-config": "2.0.1", "@vercel/static-config": "2.0.1",
"edge-runtime": "1.0.1", "edge-runtime": "1.0.1",
"esbuild": "0.14.47",
"exit-hook": "2.2.1", "exit-hook": "2.2.1",
"node-fetch": "2.6.1", "node-fetch": "2.6.1",
"ts-node": "8.9.1", "ts-node": "8.9.1",
@@ -52,7 +52,8 @@
"@types/jest": "27.4.1", "@types/jest": "27.4.1",
"@types/node-fetch": "^2.6.1", "@types/node-fetch": "^2.6.1",
"@types/test-listen": "1.1.0", "@types/test-listen": "1.1.0",
"@vercel/nft": "0.19.1", "@vercel/ncc": "0.24.0",
"@vercel/nft": "0.20.1",
"content-type": "1.0.4", "content-type": "1.0.4",
"cookie": "0.4.0", "cookie": "0.4.0",
"etag": "1.8.1", "etag": "1.8.1",

View File

@@ -79,8 +79,9 @@ import exitHook from 'exit-hook';
import { EdgeRuntime, Primitives, runServer } from 'edge-runtime'; import { EdgeRuntime, Primitives, runServer } from 'edge-runtime';
import { getConfig } from '@vercel/static-config'; import { getConfig } from '@vercel/static-config';
import { Project } from 'ts-morph'; import { Project } from 'ts-morph';
import ncc from '@vercel/ncc'; import esbuild from 'esbuild';
import fetch from 'node-fetch'; import fetch from 'node-fetch';
import { TextDecoder } from 'util';
function logError(error: Error) { function logError(error: Error) {
console.error(error.message); console.error(error.message);
@@ -149,10 +150,22 @@ async function serializeRequest(message: IncomingMessage) {
async function compileUserCode(entrypoint: string) { async function compileUserCode(entrypoint: string) {
try { try {
const buildResult = await ncc(entrypoint, { const result = await esbuild.build({
target: 'es2022', platform: 'node',
target: 'node14',
sourcemap: 'inline',
bundle: true,
entryPoints: [entrypoint],
write: false, // operate in memory
format: 'cjs',
}); });
const userCode = buildResult.code;
const compiledFile = result.outputFiles?.[0];
if (!compiledFile) {
throw new Error(`Compilation of ${entrypoint} produced no output files.`);
}
const userCode = new TextDecoder().decode(compiledFile.contents);
return ` return `
${userCode}; ${userCode};
@@ -200,7 +213,8 @@ async function compileUserCode(entrypoint: string) {
} catch (error) { } catch (error) {
// We can't easily show a meaningful stack trace from ncc -> edge-runtime. // We can't easily show a meaningful stack trace from ncc -> edge-runtime.
// So, stick with just the message for now. // So, stick with just the message for now.
console.log(`Failed to instantiate edge runtime: ${error.message}`); console.error(`Failed to instantiate edge runtime.`);
logError(error);
return undefined; return undefined;
} }
} }
@@ -231,7 +245,8 @@ async function createEdgeRuntime(userCode: string | undefined) {
} catch (error) { } catch (error) {
// We can't easily show a meaningful stack trace from ncc -> edge-runtime. // We can't easily show a meaningful stack trace from ncc -> edge-runtime.
// So, stick with just the message for now. // So, stick with just the message for now.
console.log(`Failed to instantiate edge runtime: ${error.message}`); console.error('Failed to instantiate edge runtime.');
logError(error);
return undefined; return undefined;
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/python", "name": "@vercel/python",
"version": "3.0.2", "version": "3.0.3",
"main": "./dist/index.js", "main": "./dist/index.js",
"license": "MIT", "license": "MIT",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python", "homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -23,7 +23,7 @@
"devDependencies": { "devDependencies": {
"@types/execa": "^0.9.0", "@types/execa": "^0.9.0",
"@types/jest": "27.4.1", "@types/jest": "27.4.1",
"@vercel/build-utils": "4.2.0", "@vercel/build-utils": "4.2.1",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"execa": "^1.0.0", "execa": "^1.0.0",
"typescript": "4.3.4" "typescript": "4.3.4"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/redwood", "name": "@vercel/redwood",
"version": "1.0.2", "version": "1.0.4",
"main": "./dist/index.js", "main": "./dist/index.js",
"license": "MIT", "license": "MIT",
"homepage": "https://vercel.com/docs", "homepage": "https://vercel.com/docs",
@@ -20,7 +20,7 @@
"prepublishOnly": "node build.js" "prepublishOnly": "node build.js"
}, },
"dependencies": { "dependencies": {
"@vercel/nft": "0.19.1", "@vercel/nft": "0.20.1",
"@vercel/routing-utils": "1.13.5", "@vercel/routing-utils": "1.13.5",
"semver": "6.1.1" "semver": "6.1.1"
}, },
@@ -28,6 +28,6 @@
"@types/aws-lambda": "8.10.19", "@types/aws-lambda": "8.10.19",
"@types/node": "*", "@types/node": "*",
"@types/semver": "6.0.0", "@types/semver": "6.0.0",
"@vercel/build-utils": "4.2.0" "@vercel/build-utils": "4.2.1"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/remix", "name": "@vercel/remix",
"version": "1.0.2", "version": "1.0.4",
"license": "MIT", "license": "MIT",
"main": "./dist/index.js", "main": "./dist/index.js",
"homepage": "https://vercel.com/docs", "homepage": "https://vercel.com/docs",
@@ -21,12 +21,12 @@
"default-server.js" "default-server.js"
], ],
"dependencies": { "dependencies": {
"@vercel/nft": "0.19.1" "@vercel/nft": "0.20.1"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "27.5.1", "@types/jest": "27.5.1",
"@types/node": "*", "@types/node": "*",
"@vercel/build-utils": "4.2.0", "@vercel/build-utils": "4.2.1",
"typescript": "4.6.4" "typescript": "4.6.4"
} }
} }

View File

@@ -1,7 +1,7 @@
{ {
"name": "@vercel/ruby", "name": "@vercel/ruby",
"author": "Nathan Cahill <nathan@nathancahill.com>", "author": "Nathan Cahill <nathan@nathancahill.com>",
"version": "1.3.10", "version": "1.3.11",
"license": "MIT", "license": "MIT",
"main": "./dist/index", "main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby", "homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
@@ -23,7 +23,7 @@
"devDependencies": { "devDependencies": {
"@types/fs-extra": "8.0.0", "@types/fs-extra": "8.0.0",
"@types/semver": "6.0.0", "@types/semver": "6.0.0",
"@vercel/build-utils": "4.2.0", "@vercel/build-utils": "4.2.1",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"execa": "2.0.4", "execa": "2.0.4",
"fs-extra": "^7.0.1", "fs-extra": "^7.0.1",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/static-build", "name": "@vercel/static-build",
"version": "1.0.2", "version": "1.0.3",
"license": "MIT", "license": "MIT",
"main": "./dist/index", "main": "./dist/index",
"homepage": "https://vercel.com/docs/build-step", "homepage": "https://vercel.com/docs/build-step",
@@ -37,7 +37,7 @@
"@types/ms": "0.7.31", "@types/ms": "0.7.31",
"@types/node-fetch": "2.5.4", "@types/node-fetch": "2.5.4",
"@types/promise-timeout": "1.3.0", "@types/promise-timeout": "1.3.0",
"@vercel/build-utils": "4.2.0", "@vercel/build-utils": "4.2.1",
"@vercel/frameworks": "1.0.2", "@vercel/frameworks": "1.0.2",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "1.13.5", "@vercel/routing-utils": "1.13.5",

View File

@@ -3042,10 +3042,10 @@
resolved "https://registry.yarnpkg.com/@vercel/ncc/-/ncc-0.24.0.tgz#a2e8783a185caa99b5d8961a57dfc9665de16296" resolved "https://registry.yarnpkg.com/@vercel/ncc/-/ncc-0.24.0.tgz#a2e8783a185caa99b5d8961a57dfc9665de16296"
integrity sha512-crqItMcIwCkvdXY/V3/TzrHJQx6nbIaRqE1cOopJhgGX6izvNov40SmD//nS5flfEvdK54YGjwVVq+zG6crjOg== integrity sha512-crqItMcIwCkvdXY/V3/TzrHJQx6nbIaRqE1cOopJhgGX6izvNov40SmD//nS5flfEvdK54YGjwVVq+zG6crjOg==
"@vercel/nft@0.19.1": "@vercel/nft@0.20.1":
version "0.19.1" version "0.20.1"
resolved "https://registry.yarnpkg.com/@vercel/nft/-/nft-0.19.1.tgz#dcd3c20d7ef14d2050244e7d5edcfecc7303dd46" resolved "https://registry.yarnpkg.com/@vercel/nft/-/nft-0.20.1.tgz#41e559af189405c526ac1f6709773bc99995b95b"
integrity sha512-klR5oN7S3WJsZz0r6Xsq7o8YlFEyU3/00VmlpZzIPVFzKfbcEjXo/sVR5lQBUqNKuOzhcbxaFtzW9aOyHjmPYA== integrity sha512-hSLcr64KHOkcNiTAlv154K4p4faEFBwYIi2eIgu1QCDhB1qyQYvFuEhtw3eaapNjA4/7x/2jcclfCAjILua/ag==
dependencies: dependencies:
"@mapbox/node-pre-gyp" "^1.0.5" "@mapbox/node-pre-gyp" "^1.0.5"
acorn "^8.6.0" acorn "^8.6.0"
@@ -3055,7 +3055,6 @@
graceful-fs "^4.2.9" graceful-fs "^4.2.9"
micromatch "^4.0.2" micromatch "^4.0.2"
node-gyp-build "^4.2.2" node-gyp-build "^4.2.2"
node-pre-gyp "^0.13.0"
resolve-from "^5.0.0" resolve-from "^5.0.0"
rollup-pluginutils "^2.8.2" rollup-pluginutils "^2.8.2"
@@ -4986,7 +4985,7 @@ debug@4, debug@4.1.1, debug@^4.1.0, debug@^4.1.1:
dependencies: dependencies:
ms "^2.1.1" ms "^2.1.1"
debug@^3.0.0, debug@^3.1.0, debug@^3.2.6: debug@^3.0.0, debug@^3.1.0:
version "3.2.6" version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
@@ -5175,7 +5174,7 @@ detect-indent@^5.0.0:
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d"
integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50=
detect-libc@^1.0.2, detect-libc@^1.0.3: detect-libc@^1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
@@ -5607,12 +5606,7 @@ esbuild@0.12.22:
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.22.tgz#6031a1257b8d0307d306bed673b79c3668607f51" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.22.tgz#6031a1257b8d0307d306bed673b79c3668607f51"
integrity sha512-yWCr9RoFehpqoe/+MwZXJpYOEIt7KOEvNnjIeMZpMSyQt+KCBASM3y7yViiN5dJRphf1wGdUz1+M4rTtWd/ulA== integrity sha512-yWCr9RoFehpqoe/+MwZXJpYOEIt7KOEvNnjIeMZpMSyQt+KCBASM3y7yViiN5dJRphf1wGdUz1+M4rTtWd/ulA==
esbuild@^0.11.20: esbuild@0.14.47, esbuild@^0.14.25:
version "0.11.23"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.11.23.tgz#c42534f632e165120671d64db67883634333b4b8"
integrity sha512-iaiZZ9vUF5wJV8ob1tl+5aJTrwDczlvGP0JoMmnpC2B0ppiMCu8n8gmy5ZTGl5bcG081XBVn+U+jP+mPFm5T5Q==
esbuild@^0.14.25:
version "0.14.47" version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.47.tgz#0d6415f6bd8eb9e73a58f7f9ae04c5276cda0e4d" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.47.tgz#0d6415f6bd8eb9e73a58f7f9ae04c5276cda0e4d"
integrity sha512-wI4ZiIfFxpkuxB8ju4MHrGwGLyp1+awEHAHVpx6w7a+1pmYIq8T9FGEVVwFo0iFierDoMj++Xq69GXWYn2EiwA== integrity sha512-wI4ZiIfFxpkuxB8ju4MHrGwGLyp1+awEHAHVpx6w7a+1pmYIq8T9FGEVVwFo0iFierDoMj++Xq69GXWYn2EiwA==
@@ -5638,6 +5632,11 @@ esbuild@^0.14.25:
esbuild-windows-64 "0.14.47" esbuild-windows-64 "0.14.47"
esbuild-windows-arm64 "0.14.47" esbuild-windows-arm64 "0.14.47"
esbuild@^0.11.20:
version "0.11.23"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.11.23.tgz#c42534f632e165120671d64db67883634333b4b8"
integrity sha512-iaiZZ9vUF5wJV8ob1tl+5aJTrwDczlvGP0JoMmnpC2B0ppiMCu8n8gmy5ZTGl5bcG081XBVn+U+jP+mPFm5T5Q==
escalade@^3.1.1: escalade@^3.1.1:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
@@ -7077,7 +7076,7 @@ husky@7.0.4:
resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535" resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535"
integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13:
version "0.4.24" version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
@@ -9328,15 +9327,6 @@ ndjson@2.0.0:
split2 "^3.0.0" split2 "^3.0.0"
through2 "^4.0.0" through2 "^4.0.0"
needle@^2.2.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/needle/-/needle-2.5.2.tgz#cf1a8fce382b5a280108bba90a14993c00e4010a"
integrity sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==
dependencies:
debug "^3.2.6"
iconv-lite "^0.4.4"
sax "^1.2.4"
negotiator@0.6.2: negotiator@0.6.2:
version "0.6.2" version "0.6.2"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
@@ -9403,22 +9393,6 @@ node-int64@^0.4.0:
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=
node-pre-gyp@^0.13.0:
version "0.13.0"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz#df9ab7b68dd6498137717838e4f92a33fc9daa42"
integrity sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==
dependencies:
detect-libc "^1.0.2"
mkdirp "^0.5.1"
needle "^2.2.1"
nopt "^4.0.1"
npm-packlist "^1.1.6"
npmlog "^4.0.2"
rc "^1.2.7"
rimraf "^2.6.1"
semver "^5.3.0"
tar "^4"
node-releases@^2.0.3: node-releases@^2.0.3:
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.3.tgz#225ee7488e4a5e636da8da52854844f9d716ca96" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.3.tgz#225ee7488e4a5e636da8da52854844f9d716ca96"
@@ -9510,7 +9484,7 @@ npm-package-arg@6.1.0:
semver "^5.6.0" semver "^5.6.0"
validate-npm-package-name "^3.0.0" validate-npm-package-name "^3.0.0"
npm-packlist@^1.1.6, npm-packlist@^1.4.4: npm-packlist@^1.4.4:
version "1.4.8" version "1.4.8"
resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e"
integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==
@@ -9549,7 +9523,7 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1:
dependencies: dependencies:
path-key "^3.0.0" path-key "^3.0.0"
npmlog@^4.0.2, npmlog@^4.1.2: npmlog@^4.1.2:
version "4.1.2" version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
@@ -10532,7 +10506,7 @@ rc9@1.2.0:
destr "^1.0.0" destr "^1.0.0"
flat "^5.0.0" flat "^5.0.0"
rc@^1.2.7, rc@^1.2.8: rc@^1.2.8:
version "1.2.8" version "1.2.8"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
@@ -10972,7 +10946,7 @@ rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2:
dependencies: dependencies:
glob "^7.1.3" glob "^7.1.3"
rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3:
version "2.7.1" version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
@@ -11039,11 +11013,6 @@ safe-regex@^1.1.0:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sax@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
semver-compare@^1.0.0: semver-compare@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
@@ -11063,7 +11032,7 @@ semver-diff@^3.1.1:
dependencies: dependencies:
semver "^6.3.0" 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.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: "semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.0.3, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
version "5.7.1" version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
@@ -11864,7 +11833,7 @@ tar@4.4.8:
safe-buffer "^5.1.2" safe-buffer "^5.1.2"
yallist "^3.0.2" yallist "^3.0.2"
tar@^4, tar@^4.4.10, tar@^4.4.12, tar@^4.4.8: tar@^4.4.10, tar@^4.4.12, tar@^4.4.8:
version "4.4.13" version "4.4.13"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==