Compare commits

...

27 Commits

Author SHA1 Message Date
Steven
32664cd13b Publish Canary
- @vercel/build-utils@2.12.3-canary.19
 - vercel@23.1.3-canary.35
 - @vercel/client@10.2.3-canary.20
 - @vercel/frameworks@0.5.1-canary.12
 - vercel-plugin-go@1.0.0-canary.4
 - vercel-plugin-node@1.12.2-canary.10
 - vercel-plugin-python@1.0.0-canary.4
 - vercel-plugin-ruby@1.0.0-canary.3
2021-11-20 10:20:05 -05:00
Sam Ko
db468c489a [frameworks] Fix Parcel default output directory to dist (#7029)
### Related Issues

The Parcel template via the Project Creating Flow returns a build error because the default output directory for the Parcel **Framework Preset** is searching for `public` instead of `dist`. The default behavior of Parcel's CLI I believe is `dist`.

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2021-11-20 01:44:22 +00:00
Steven
edd9bb506c [frameworks][cli] Consolidate buildCommand and devCommand (#7032)
* [frameworks] Consolidate buildCommand and devCommand

* Fix default build command when no override or package.json found
2021-11-19 19:07:52 -05:00
Steven
a72549a290 [tests] Make test more reliable for function timeout (#7033) 2021-11-19 17:26:32 -05:00
Steven
4aa6a13912 Publish Canary
- vercel@23.1.3-canary.34
 - vercel-plugin-python@1.0.0-canary.3
 - @vercel/python@2.0.6-canary.6
2021-11-19 14:42:02 -05:00
Steven
81ea0082f1 [python] Upgrade python 3.9 (#7008)
* [python] Upgrade python 3.9

* Fix python path

* Improve testing

* Add support for version selection via Pipfile

* Parse the "Pipfile.lock" instead

* Fix support for Node.js 12
2021-11-19 13:42:17 -05:00
Jared Palmer
6dff0875f5 [cli] Switch from hardlinks to symlinks in vc build (#7026)
* Switch from hardlinks to symlinks in vc build

* Remove error handling that fs-extra already takes care of

* Add comment

* Put back symlink error handling
2021-11-19 12:36:31 -05:00
Steven
30aa392c0a [tests] Increase CI duration (#7030)
Seems like macOS sometimes exceeds 60 min so this bumps the timeout.

The build can be between 3 minutes and 5 minutes. The tests can be between 45 minutes and 55 minutes.
2021-11-19 14:59:12 +00:00
Miroslav Simulcik
c4fc060030 [cli] Fix spinner output outside of TTY (#7017)
* Fix spinner output outside of TTY

* Use console.log

* Fix tests

* Revert "Fix tests"

This reverts commit 44383eea2dc3bb699d943079d77961ed86fe65d6.

* Revert "Use console.log"

This reverts commit 251bd1d624532e9ecfb4aee5b31538aa4ba740ef.
2021-11-17 14:04:59 +01:00
Steven
3fa08bf64f [cli] Fix update command to favor npm (#7018) 2021-11-16 16:07:31 -05:00
Steven
43056bde1f Publish Canary
- vercel@23.1.3-canary.33
2021-11-15 18:07:17 -05:00
Steven
a49966b9b4 [cli] Change vc build to use stdout (#7005) 2021-11-15 18:06:48 -05:00
Steven
7f55de71bb Publish Canary
- vercel@23.1.3-canary.32
 - @vercel/go@1.2.4-canary.4
 - vercel-plugin-go@1.0.0-canary.3
2021-11-15 13:37:04 -05:00
Steven
db8e36e04c [go] Add support for version 1.17 (#7002)
* Support more Go versions

Co-authored-by: Stefan M <StefMa@users.noreply.github.com>
2021-11-15 13:36:26 -05:00
Andy Bitz
82924bb5c4 Publish Canary
- vercel@23.1.3-canary.31
 - vercel-plugin-node@1.12.2-canary.9
2021-11-15 14:11:55 +01:00
Andy
18b5fac93e [vercel-plugin-node] Use .nft.json files instead of directories. (#6988)
* Ignore install step if package.json does not exist in entrypoint directory

* [plugin-node] Use `.nft.json` files

* Bump @vercel/fun

* Update package.json

* Remove package

* Remove import

* Update paths

* Revert "Update paths"

This reverts commit fa304e90381f475c64b2333402dca489813522da.

* Update path for windows

* Update handler path
2021-11-15 14:08:49 +01:00
Miroslav Simulcik
a6012e600b [cli] Fix updating of gitignore in vercel link (#6986)
* Fix updating of gitignore in link

* Fix modification of gitignore file during linking

* Fix empty file case
2021-11-12 17:11:21 +01:00
Steven
c3abf73f58 Publish Canary
- @vercel/build-utils@2.12.3-canary.18
 - vercel@23.1.3-canary.30
 - @vercel/client@10.2.3-canary.19
 - vercel-plugin-go@1.0.0-canary.2
 - vercel-plugin-node@1.12.2-canary.8
 - vercel-plugin-python@1.0.0-canary.2
 - vercel-plugin-ruby@1.0.0-canary.2
2021-11-11 14:54:33 -05:00
Steven
4873b8b379 [build-utils][node] Add updateFunctionsManifest function (#6971) 2021-11-11 14:53:43 -05:00
Miroslav Simulcik
6248139281 [cli] Prevent recursion of vercel build and vercel dev (#6960)
* Prevent infinite recursion of vercel build and dev

* Improved text

* Improved the text further

* Add tests

* Debug test

* Add debug log

* Debug test

* debug test

* Debug test

* Fix test

* Debug tests

* Debug test

* Remove only from tests

* Add error documentation

* Improved docs

* Fix other test

* Fixed links to error docs

* Remove legacy error page

* Use nicer error links

Co-authored-by: Leo Lamprecht <leo@vercel.com>
2021-11-11 10:51:30 -05:00
Miroslav Simulcik
507a5de3cd [tests] Fix getting of config paths (#6969)
Co-authored-by: Steven <steven@ceriously.com>
2021-11-09 14:34:41 -05:00
Gary Borton
be1c78e72f Publish Canary
- vercel@23.1.3-canary.29
 - vercel-plugin-middleware@0.0.0-canary.7
2021-11-09 11:07:01 -08:00
Gary Borton
c277c649c6 Fix middleware build result. Return middleware next when invalid result. (#6963)
* Fix middleware build result. Return middleware next when invalid result.

* Add a unit test that would have failed before previous commit.

* Copy next.js behavior.

* Add a test for invalid responses from middleware.

Co-authored-by: Leo Lamprecht <leo@vercel.com>
2021-11-09 11:06:16 -08:00
Steven
ed1dacd276 Publish Canary
- @vercel/build-utils@2.12.3-canary.17
 - vercel@23.1.3-canary.28
 - @vercel/client@10.2.3-canary.18
 - vercel-plugin-go@1.0.0-canary.1
 - vercel-plugin-python@1.0.0-canary.1
 - vercel-plugin-ruby@1.0.0-canary.1
 - @vercel/python@2.0.6-canary.5
 - @vercel/ruby@1.2.8-canary.4
2021-11-09 12:50:34 -05:00
Steven
144e890bfa Add plugin packages for go/python/ruby (#6961)
* Add plugin packages

* Fix usage

* Fix build

* Fix workspace linking to build-utils
2021-11-09 12:48:40 -05:00
Andy Bitz
af097c2c06 Publish Canary
- vercel@23.1.3-canary.27
2021-11-09 16:44:14 +01:00
Andy
873a582986 [cli] Ignore .next/cache in vc build (#6968)
* [cli] Ignore .next/cache in `vc build`

* Handle middleware-manifest.json

* Update manifest update
2021-11-09 16:43:26 +01:00
58 changed files with 1006 additions and 583 deletions

View File

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

1
.gitignore vendored
View File

@@ -27,3 +27,4 @@ test/lib/deployment/failed-page.txt
/public
__pycache__
.vercel
.output

View File

@@ -14,8 +14,6 @@ const frameworks = (_frameworks as Framework[])
sort: undefined,
dependency: undefined,
defaultRoutes: undefined,
devCommand: undefined,
buildCommand: undefined,
};
if (framework.logo) {

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "2.12.3-canary.16",
"version": "2.12.3-canary.19",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
@@ -30,7 +30,7 @@
"@types/node-fetch": "^2.1.6",
"@types/semver": "6.0.0",
"@types/yazl": "^2.4.1",
"@vercel/frameworks": "0.5.1-canary.11",
"@vercel/frameworks": "0.5.1-canary.12",
"@vercel/ncc": "0.24.0",
"aggregate-error": "3.0.1",
"async-retry": "1.2.3",

View File

@@ -2,7 +2,7 @@ import fs from 'fs-extra';
import { join, dirname, relative } from 'path';
import glob from './fs/glob';
import { normalizePath } from './fs/normalize-path';
import { FILES_SYMBOL, Lambda } from './lambda';
import { FILES_SYMBOL, getLambdaOptionsFromFunction, Lambda } from './lambda';
import type FileBlob from './file-blob';
import type { BuilderFunctions, BuildOptions, Files } from './types';
import minimatch from 'minimatch';
@@ -12,7 +12,7 @@ import minimatch from 'minimatch';
* @param buildRuntime - a legacy build() function from a Runtime
* @param ext - the file extension, for example `.py`
*/
export async function convertRuntimeToPlugin(
export function convertRuntimeToPlugin(
buildRuntime: (options: BuildOptions) => Promise<{ output: Lambda }>,
ext: string
) {
@@ -21,8 +21,8 @@ export async function convertRuntimeToPlugin(
const files = await glob('**', opts);
delete files['vercel.json']; // Builders/Runtimes didn't have vercel.json
const entrypoints = await glob(`api/**/*${ext}`, opts);
const functionsManifest: { [key: string]: any } = {};
const functions = await readVercelConfigFunctions(workPath);
const pages: { [key: string]: any } = {};
const { functions = {} } = await readVercelConfig(workPath);
const traceDir = join(workPath, '.output', 'runtime-traced-files');
await fs.ensureDir(traceDir);
@@ -44,11 +44,11 @@ export async function convertRuntimeToPlugin(
},
});
functionsManifest[entrypoint] = {
pages[entrypoint] = {
handler: output.handler,
runtime: output.runtime,
memory: config.memory || output.memory,
maxDuration: config.maxDuration || output.maxDuration,
memory: output.memory,
maxDuration: output.maxDuration,
environment: output.environment,
allowQuery: output.allowQuery,
regions: output.regions,
@@ -98,10 +98,7 @@ export async function convertRuntimeToPlugin(
await fs.writeFile(nft, json);
}
await fs.writeFile(
join(workPath, '.output', 'functions-manifest.json'),
JSON.stringify(functionsManifest)
);
await updateFunctionsManifest({ workPath, pages });
};
}
@@ -115,14 +112,10 @@ async function linkOrCopy(existingPath: string, newPath: string) {
}
}
async function readVercelConfigFunctions(
workPath: string
): Promise<BuilderFunctions> {
const vercelJsonPath = join(workPath, 'vercel.json');
async function readJson(filePath: string): Promise<{ [key: string]: any }> {
try {
const str = await fs.readFile(vercelJsonPath, 'utf8');
const obj = JSON.parse(str);
return obj.functions || {};
const str = await fs.readFile(filePath, 'utf8');
return JSON.parse(str);
} catch (err) {
if (err.code === 'ENOENT') {
return {};
@@ -130,3 +123,49 @@ async function readVercelConfigFunctions(
throw err;
}
}
async function readVercelConfig(
workPath: string
): Promise<{ functions?: BuilderFunctions; regions?: string[] }> {
const vercelJsonPath = join(workPath, 'vercel.json');
return readJson(vercelJsonPath);
}
/**
* If `.output/functions-manifest.json` exists, append to the pages
* property. Otherwise write a new file. This will also read `vercel.json`
* and apply relevant `functions` property config.
*/
export async function updateFunctionsManifest({
workPath,
pages,
}: {
workPath: string;
pages: { [key: string]: any };
}) {
const functionsManifestPath = join(
workPath,
'.output',
'functions-manifest.json'
);
const vercelConfig = await readVercelConfig(workPath);
const functionsManifest = await readJson(functionsManifestPath);
if (!functionsManifest.version) functionsManifest.version = 1;
if (!functionsManifest.pages) functionsManifest.pages = {};
for (const [pageKey, pageConfig] of Object.entries(pages)) {
const fnConfig = await getLambdaOptionsFromFunction({
sourceFile: pageKey,
config: vercelConfig,
});
functionsManifest.pages[pageKey] = {
...pageConfig,
memory: fnConfig.memory || pageConfig.memory,
maxDuration: fnConfig.maxDuration || pageConfig.maxDuration,
regions: vercelConfig.regions || pageConfig.regions,
};
}
await fs.writeFile(functionsManifestPath, JSON.stringify(functionsManifest));
}

View File

@@ -82,6 +82,10 @@ export { detectFramework } from './detect-framework';
export { DetectorFilesystem } from './detectors/filesystem';
export { readConfigFile } from './fs/read-config-file';
export { normalizePath } from './fs/normalize-path';
export {
convertRuntimeToPlugin,
updateFunctionsManifest,
} from './convert-runtime-to-plugin';
export * from './schemas';
export * from './types';

View File

@@ -36,7 +36,7 @@ interface CreateLambdaOptions {
interface GetLambdaOptionsFromFunctionOptions {
sourceFile: string;
config?: Config;
config?: Pick<Config, 'functions'>;
}
export const FILES_SYMBOL = Symbol('files');

View File

@@ -71,9 +71,12 @@ describe('convert-runtime-to-plugin', () => {
const funcManifest = JSON.parse(output['functions-manifest.json']);
expect(funcManifest).toMatchObject({
'api/index.py': lambdaOptions,
'api/users/get.py': lambdaOptions,
'api/users/post.py': { ...lambdaOptions, memory: 3008 },
version: 1,
pages: {
'api/index.py': lambdaOptions,
'api/users/get.py': lambdaOptions,
'api/users/post.py': { ...lambdaOptions, memory: 3008 },
},
});
const indexJson = JSON.parse(output.server.pages.api['index.py.nft.json']);

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "23.1.3-canary.26",
"version": "23.1.3-canary.35",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -43,14 +43,14 @@
"node": ">= 12"
},
"dependencies": {
"@vercel/build-utils": "2.12.3-canary.16",
"@vercel/go": "1.2.4-canary.3",
"@vercel/build-utils": "2.12.3-canary.19",
"@vercel/go": "1.2.4-canary.4",
"@vercel/node": "1.12.2-canary.6",
"@vercel/python": "2.0.6-canary.4",
"@vercel/ruby": "1.2.8-canary.3",
"@vercel/python": "2.0.6-canary.6",
"@vercel/ruby": "1.2.8-canary.4",
"update-notifier": "4.1.0",
"vercel-plugin-middleware": "0.0.0-canary.6",
"vercel-plugin-node": "1.12.2-canary.7"
"vercel-plugin-middleware": "0.0.0-canary.7",
"vercel-plugin-node": "1.12.2-canary.10"
},
"devDependencies": {
"@next/env": "11.1.2",
@@ -90,7 +90,7 @@
"@types/update-notifier": "5.1.0",
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@vercel/frameworks": "0.5.1-canary.11",
"@vercel/frameworks": "0.5.1-canary.12",
"@vercel/ncc": "0.24.0",
"@vercel/nft": "0.17.0",
"@zeit/fun": "0.11.2",

View File

@@ -30,10 +30,7 @@ import { getCommandName, getPkgName } from '../util/pkg-name';
import { loadCliPlugins } from '../util/plugins';
import { findFramework } from '../util/projects/find-framework';
import { VERCEL_DIR } from '../util/projects/link';
import {
ProjectLinkAndSettings,
readProjectSettings,
} from '../util/projects/project-settings';
import { readProjectSettings } from '../util/projects/project-settings';
import pull from './pull';
const sema = new Sema(16, {
@@ -68,16 +65,23 @@ const help = () => {
const OUTPUT_DIR = '.output';
const fields: {
name: string;
value: keyof ProjectLinkAndSettings['settings'];
}[] = [
{ name: 'Build Command', value: 'buildCommand' },
{ name: 'Output Directory', value: 'outputDirectory' },
{ name: 'Root Directory', value: 'rootDirectory' },
];
export default async function main(client: Client) {
if (process.env.__VERCEL_BUILD_RUNNING) {
client.output.error(
`${cmd(
`${getPkgName()} build`
)} must not recursively invoke itself. Check the Build Command in the Project Settings or the ${cmd(
'build'
)} script in ${cmd('package.json')}`
);
client.output.error(
`Learn More: https://vercel.link/recursive-invocation-of-commands`
);
return 1;
} else {
process.env.__VERCEL_BUILD_RUNNING = '1';
}
let argv;
const buildStamp = stamp();
try {
@@ -152,47 +156,57 @@ export default async function main(client: Client) {
}
const buildState = { ...project.settings };
client.output.log(`Retrieved Project Settings:`);
client.output.print(
chalk.dim(` - ${chalk.bold(`Framework Preset:`)} ${framework.name}\n`)
const formatSetting = (
name: string,
override: string | null | undefined,
defaults: typeof framework.settings.outputDirectory
) =>
` - ${chalk.bold(`${name}:`)} ${`${
override
? override + ` (override)`
: 'placeholder' in defaults
? chalk.italic(`${defaults.placeholder}`)
: defaults.value
}`}`;
console.log(`Retrieved Project Settings:`);
console.log(
chalk.dim(` - ${chalk.bold(`Framework Preset:`)} ${framework.name}`)
);
console.log(
chalk.dim(
formatSetting(
'Build Command',
project.settings.buildCommand,
framework.settings.buildCommand
)
)
);
console.log(
chalk.dim(
formatSetting(
'Output Directory',
project.settings.outputDirectory,
framework.settings.outputDirectory
)
)
);
for (let field of fields) {
const defaults = (framework.settings as any)[field.value];
if (defaults) {
client.output.print(
chalk.dim(
` - ${chalk.bold(`${field.name}:`)} ${`${
project.settings[field.value]
? project.settings[field.value] + ` (override)`
: isSettingValue(defaults)
? defaults.value
: chalk.italic(`${defaults.placeholder}`)
}`}\n`
)
);
}
if (field.value != 'buildCommand') {
(buildState as any)[field.value] = project.settings[field.value]
? project.settings[field.value]
: defaults
? isSettingValue(defaults)
? defaults.value
: null
: null;
}
}
buildState.outputDirectory =
project.settings.outputDirectory ||
(isSettingValue(framework.settings.outputDirectory)
? framework.settings.outputDirectory.value
: null);
buildState.rootDirectory = project.settings.rootDirectory;
if (loadedEnvFiles.length > 0) {
client.output.log(
console.log(
`Loaded Environment Variables from ${loadedEnvFiles.length} ${pluralize(
'file',
loadedEnvFiles.length
)}:`
);
for (let envFile of loadedEnvFiles) {
client.output.print(chalk.dim(` - ${envFile.path}\n`));
console.log(chalk.dim(` - ${envFile.path}`));
}
}
@@ -223,7 +237,7 @@ export default async function main(client: Client) {
};
if (plugins?.pluginCount && plugins?.pluginCount > 0) {
client.output.log(
console.log(
`Loaded ${plugins.pluginCount} CLI ${pluralize(
'Plugin',
plugins.pluginCount
@@ -231,7 +245,7 @@ export default async function main(client: Client) {
);
// preBuild Plugins
if (plugins.preBuildPlugins.length > 0) {
client.output.log(
console.log(
`Running ${plugins.pluginCount} CLI ${pluralize(
'Plugin',
plugins.pluginCount
@@ -269,17 +283,19 @@ export default async function main(client: Client) {
// Clean the output directory
fs.removeSync(join(cwd, OUTPUT_DIR));
// Yarn v2 PnP mode may be activated, so force
// "node-modules" linker style
const env = {
YARN_NODE_LINKER: 'node-modules',
...spawnOpts.env,
};
if (typeof buildState.buildCommand === 'string') {
client.output.log(`Running Build Command: ${cmd(buildState.buildCommand)}`);
console.log(`Running Build Command: ${cmd(buildState.buildCommand)}`);
await execCommand(buildState.buildCommand, {
...spawnOpts,
// Yarn v2 PnP mode may be activated, so force
// "node-modules" linker style
env: {
YARN_NODE_LINKER: 'node-modules',
...spawnOpts.env,
},
cwd: cwd,
env,
cwd,
});
} else if (fs.existsSync(join(cwd, 'package.json'))) {
await runPackageJsonScript(
@@ -288,6 +304,15 @@ export default async function main(client: Client) {
['vercel-build', 'now-build', 'build'],
spawnOpts
);
} else if (typeof framework.settings.buildCommand.value === 'string') {
console.log(
`Running Build Command: ${cmd(framework.settings.buildCommand.value)}`
);
await execCommand(framework.settings.buildCommand.value, {
...spawnOpts,
env,
cwd,
});
}
if (!fs.existsSync(join(cwd, OUTPUT_DIR))) {
@@ -317,6 +342,7 @@ export default async function main(client: Client) {
'_middleware.js',
'api/**',
'.git/**',
'.next/cache/**',
],
nodir: true,
dot: true,
@@ -335,7 +361,7 @@ export default async function main(client: Client) {
)
);
client.output.stopSpinner();
client.output.log(
console.log(
`Copied ${files.length.toLocaleString()} files from ${param(
distDir
)} to ${param(outputDir)} ${copyStamp()}`
@@ -389,6 +415,36 @@ export default async function main(client: Client) {
join(cwd, OUTPUT_DIR, 'static', '_next', 'static')
);
// Next.js might reference files from the `static` directory in `middleware-manifest.json`.
// Since we move all files from `static` to `static/_next/static`, we'll need to change
// those references as well and update the manifest file.
const middlewareManifest = join(
cwd,
OUTPUT_DIR,
'server',
'middleware-manifest.json'
);
if (fs.existsSync(middlewareManifest)) {
const manifest = await fs.readJSON(middlewareManifest);
Object.keys(manifest.middleware).forEach(key => {
const files = manifest.middleware[key].files.map((f: string) => {
if (f.startsWith('static/')) {
const next = f.replace(/^static\//gm, 'static/_next/static/');
client.output.debug(
`Replacing file in \`middleware-manifest.json\`: ${f} => ${next}`
);
return next;
}
return f;
});
manifest.middleware[key].files = files;
});
await fs.writeJSON(middlewareManifest, manifest);
}
// We want to pick up directories for user-provided static files into `.`output/static`.
// More specifically, the static directory contents would then be mounted to `output/static/static`,
// and the public directory contents would be mounted to `output/static`. Old Next.js versions
@@ -526,7 +582,7 @@ export default async function main(client: Client) {
// Build Plugins
if (plugins?.buildPlugins && plugins.buildPlugins.length > 0) {
client.output.log(
console.log(
`Running ${plugins.pluginCount} CLI ${pluralize(
'Plugin',
plugins.pluginCount
@@ -561,13 +617,13 @@ export default async function main(client: Client) {
}
}
client.output.print(
console.log(
`${prependEmoji(
`Build Completed in ${chalk.bold(OUTPUT_DIR)} ${chalk.gray(
buildStamp()
)}`,
emoji('success')
)}\n`
)}`
);
return 0;
@@ -613,25 +669,37 @@ export async function runPackageJsonScript(
}
}
client.output.log(`Running Build Command: ${cmd(opts.prettyCommand)}\n`);
console.log(`Running Build Command: ${cmd(opts.prettyCommand)}\n`);
await spawnAsync(cliType, ['run', scriptName], opts);
client.output.print('\n'); // give it some room
console.log(); // give it some room
client.output.debug(`Script complete [${Date.now() - runScriptTime}ms]`);
return true;
}
async function linkOrCopy(existingPath: string, newPath: string) {
try {
await fs.createLink(existingPath, newPath);
if (
newPath.endsWith('.nft.json') ||
newPath.endsWith('middleware-manifest.json') ||
newPath.endsWith('required-server-files.json')
) {
await fs.copy(existingPath, newPath, {
overwrite: true,
});
} else {
await fs.createSymlink(existingPath, newPath, 'file');
}
} catch (err: any) {
// eslint-disable-line
// If a hard link to the same file already exists
// If a symlink to the same file already exists
// then trying to copy it will make an empty file from it.
if (err['code'] === 'EEXIST') return;
// In some VERY rare cases (1 in a thousand), hard-link creation fails on Windows.
// In some VERY rare cases (1 in a thousand), symlink creation fails on Windows.
// In that case, we just fall back to copying.
// This issue is reproducible with "pnpm add @material-ui/icons@4.9.1"
await fs.copyFile(existingPath, newPath);
await fs.copy(existingPath, newPath, {
overwrite: true,
});
}
}

View File

@@ -6,7 +6,6 @@ import { ProjectEnvVariable } from '../../types';
import Client from '../../util/client';
import { getLinkedProject } from '../../util/projects/link';
import { getFrameworks } from '../../util/get-frameworks';
import { isSettingValue } from '../../util/is-setting-value';
import { ProjectSettings } from '../../types';
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
import setupAndLink from '../../util/link/setup-and-link';
@@ -71,9 +70,9 @@ export default async function dev(
frameworkSlug = framework.slug;
}
const defaults = framework.settings.devCommand;
if (isSettingValue(defaults)) {
devCommand = defaults.value;
const defaults = framework.settings.devCommand.value;
if (defaults) {
devCommand = defaults;
}
}
}

View File

@@ -48,6 +48,22 @@ const help = () => {
};
export default async function main(client: Client) {
if (process.env.__VERCEL_DEV_RUNNING) {
client.output.error(
`${cmd(
`${getPkgName()} dev`
)} must not recursively invoke itself. Check the Development Command in the Project Settings or the ${cmd(
'dev'
)} script in ${cmd('package.json')}`
);
client.output.error(
`Learn More: https://vercel.link/recursive-invocation-of-commands`
);
return 1;
} else {
process.env.__VERCEL_DEV_RUNNING = '1';
}
let argv;
let args;
const { output } = client;
@@ -90,22 +106,21 @@ export default async function main(client: Client) {
if (pkg) {
const { scripts } = pkg as PackageJson;
if (scripts && scripts.dev && /\bnow\b\W+\bdev\b/.test(scripts.dev)) {
output.error(
`The ${cmd('dev')} script in ${cmd(
'package.json'
)} must not contain ${cmd('now dev')}`
if (
scripts &&
scripts.dev &&
/\b(now|vercel)\b\W+\bdev\b/.test(scripts.dev)
) {
client.output.error(
`${cmd(
`${getPkgName()} dev`
)} must not recursively invoke itself. Check the Development Command in the Project Settings or the ${cmd(
'dev'
)} script in ${cmd('package.json')}`
);
output.error(`Learn More: http://err.sh/vercel/now-dev-as-dev-script`);
return 1;
}
if (scripts && scripts.dev && /\bvercel\b\W+\bdev\b/.test(scripts.dev)) {
output.error(
`The ${cmd('dev')} script in ${cmd(
'package.json'
)} must not contain ${cmd('vercel dev')}`
client.output.error(
`Learn More: https://vercel.link/recursive-invocation-of-commands`
);
output.error(`Learn More: http://err.sh/vercel/now-dev-as-dev-script`);
return 1;
}
}

View File

@@ -160,24 +160,26 @@ const main = async () => {
// * a path to deploy (as in: `vercel path/`)
// * a subcommand (as in: `vercel ls`)
const targetOrSubcommand = argv._[2];
const isBuildOrDev =
targetOrSubcommand === 'build' || targetOrSubcommand === 'dev';
output.print(
`${chalk.grey(
`${getTitleName()} CLI ${pkg.version}${
targetOrSubcommand === 'dev'
? ' dev (beta)'
: targetOrSubcommand === 'build'
? ' build (beta)'
: ''
}${
isCanary ||
targetOrSubcommand === 'dev' ||
targetOrSubcommand === 'build'
? ' — https://vercel.com/feedback'
: ''
}`
)}\n`
);
if (isBuildOrDev) {
console.log(
`${chalk.grey(
`${getTitleName()} CLI ${
pkg.version
} ${targetOrSubcommand} (beta) — https://vercel.com/feedback`
)}`
);
} else {
output.print(
`${chalk.grey(
`${getTitleName()} CLI ${pkg.version}${
isCanary ? ' — https://vercel.com/feedback' : ''
}`
)}\n`
);
}
// Handle `--version` directly
if (!targetOrSubcommand && argv['--version']) {

View File

@@ -18,12 +18,8 @@ export const isDirectory = (path: string): boolean => {
const getGlobalPathConfig = (): string => {
let customPath: string | undefined;
try {
const argv = getArgs(process.argv.slice(2), {});
customPath = argv['--global-config'];
} catch (_error) {
// args are optional so consume error
}
const argv = getArgs(process.argv.slice(2), {}, { permissive: true });
customPath = argv['--global-config'];
const vercelDirectories = XDGAppPaths('com.vercel.cli').dataDirs();

View File

@@ -7,12 +7,8 @@ import getArgs from '../../util/get-args';
export default function getLocalPathConfig(prefix: string) {
let customPath: string | undefined;
try {
const argv = getArgs(process.argv.slice(2), {});
customPath = argv['--local-config'];
} catch (_error) {
// args are optional so consume error
}
const argv = getArgs(process.argv.slice(2), {}, { permissive: true });
customPath = argv['--local-config'];
// If `--local-config` flag was specified, then that takes priority
if (customPath) {

View File

@@ -1358,13 +1358,20 @@ export default class DevServer {
this.cwd,
this.output
);
for (let plugin of devMiddlewarePlugins) {
const result = await plugin.plugin.runDevMiddleware(req, res, this.cwd);
if (result.finished) {
return result;
try {
for (let plugin of devMiddlewarePlugins) {
const result = await plugin.plugin.runDevMiddleware(req, res, this.cwd);
if (result.finished) {
return result;
}
}
return { finished: false };
} catch (e) {
return {
finished: true,
error: e,
};
}
return { finished: false };
};
/**
@@ -2154,7 +2161,10 @@ export default class DevServer {
process.stdout.write(data.replace(proxyPort, devPort));
});
p.on('exit', () => {
p.on('exit', (code: number) => {
if (code > 0) {
process.exit(code);
}
this.devProcessPort = undefined;
});

View File

@@ -1,11 +1,9 @@
import { Stats } from 'fs';
import { sep, dirname, join, resolve } from 'path';
import { readJSON, lstat, readlink, readFile, realpath } from 'fs-extra';
import { lstat, readlink, readFile, realpath } from 'fs-extra';
import { isCanary } from './is-canary';
import { getPkgName } from './pkg-name';
// `npm` tacks a bunch of extra properties on the `package.json` file,
// so check for one of them to determine yarn vs. npm.
async function isYarn(): Promise<boolean> {
let s: Stats;
let binPath = process.argv[1];
@@ -20,8 +18,12 @@ async function isYarn(): Promise<boolean> {
}
}
const pkgPath = join(dirname(binPath), '..', 'package.json');
const pkg = await readJSON(pkgPath).catch(() => ({}));
return !('_id' in pkg);
/*
* Generally, pkgPath looks like:
* "/Users/username/.config/yarn/global/node_modules/vercel/package.json"
* "/usr/local/share/.config/yarn/global/node_modules/vercel/package.json"
*/
return pkgPath.includes(join('yarn', 'global'));
}
async function getConfigPrefix() {

View File

@@ -125,10 +125,14 @@ export class Output {
this.debug(`Spinner invoked (${message}) with a ${delay}ms delay`);
return;
}
if (this._spinner) {
this._spinner.text = message;
if (this.isTTY) {
if (this._spinner) {
this._spinner.text = message;
} else {
this._spinner = wait(message, delay);
}
} else {
this._spinner = wait(message, delay);
this.print(`${message}\n`);
}
};

View File

@@ -244,20 +244,27 @@ export async function linkFolderToProject(
try {
const gitIgnorePath = join(path, '.gitignore');
const gitIgnore = await readFile(gitIgnorePath, 'utf8').catch(() => null);
const EOL = gitIgnore && gitIgnore.includes('\r\n') ? '\r\n' : os.EOL;
let gitIgnore =
(await readFile(gitIgnorePath, 'utf8').catch(() => null)) ?? '';
const EOL = gitIgnore.includes('\r\n') ? '\r\n' : os.EOL;
let contentModified = false;
if (
!gitIgnore ||
!gitIgnore.split(EOL).includes(VERCEL_DIR) ||
!gitIgnore.split(EOL).includes(VERCEL_OUTPUT_DIR)
) {
await writeFile(
gitIgnorePath,
gitIgnore
? `${gitIgnore}${EOL}${VERCEL_DIR}${EOL}${VERCEL_OUTPUT_DIR}${EOL}`
: `${VERCEL_DIR}${EOL}${VERCEL_OUTPUT_DIR}${EOL}`
);
if (!gitIgnore.split(EOL).includes(VERCEL_DIR)) {
gitIgnore += `${
gitIgnore.endsWith(EOL) || gitIgnore.length === 0 ? '' : EOL
}${VERCEL_DIR}${EOL}`;
contentModified = true;
}
if (!gitIgnore.split(EOL).includes(VERCEL_OUTPUT_DIR)) {
gitIgnore += `${
gitIgnore.endsWith(EOL) || gitIgnore.length === 0 ? '' : EOL
}${VERCEL_OUTPUT_DIR}${EOL}`;
contentModified = true;
}
if (contentModified) {
await writeFile(gitIgnorePath, gitIgnore);
isGitIgnoreUpdated = true;
}
} catch (error) {

View File

@@ -0,0 +1,3 @@
export default function () {
return 'freecandy';
}

View File

@@ -19,7 +19,7 @@ const getRevertAliasConfigFile = () => {
],
});
};
module.exports = async function prepare(session) {
module.exports = async function prepare(session, binaryPath) {
const spec = {
'static-single-file': {
'first.png': getImageFile(session, { size: 30 }),
@@ -114,6 +114,23 @@ module.exports = async function prepare(session) {
2
),
},
'dev-fail-on-recursion-command': {
'package.json': '{}',
},
'build-fail-on-recursion-command': {
'package.json': '{}',
},
'build-fail-on-recursion-script': {
'package.json': JSON.stringify(
{
scripts: {
build: `${binaryPath} build`,
},
},
null,
2
),
},
'static-deployment': {
'index.txt': 'Hello World',
},
@@ -348,6 +365,10 @@ module.exports = async function prepare(session) {
'project-link-dev': {
'package.json': '{}',
},
'project-link-gitignore': {
'package.json': '{}',
'.gitignore': '.output',
},
'project-link-legacy': {
'index.html': 'Hello',
'vercel.json': '{"builds":[{"src":"*.html","use":"@vercel/static"}]}',

View File

@@ -252,10 +252,69 @@ const createUser = async () => {
const getConfigAuthPath = () => path.join(globalDir, 'auth.json');
async function setupProject(process, projectName, overrides) {
await waitForPrompt(process, chunk => /Set up [^?]+\?/.test(chunk));
process.stdin.write('yes\n');
await waitForPrompt(process, chunk => /Which scope [^?]+\?/.test(chunk));
process.stdin.write('\n');
await waitForPrompt(process, chunk =>
chunk.includes('Link to existing project?')
);
process.stdin.write('no\n');
await waitForPrompt(process, chunk =>
chunk.includes('Whats your projects name?')
);
process.stdin.write(`${projectName}\n`);
await waitForPrompt(process, chunk =>
chunk.includes('In which directory is your code located?')
);
process.stdin.write('\n');
await waitForPrompt(process, chunk =>
chunk.includes('Want to override the settings?')
);
if (overrides) {
process.stdin.write('yes\n');
const { buildCommand, outputDirectory, devCommand } = overrides;
await waitForPrompt(process, chunk =>
chunk.includes(
'Which settings would you like to overwrite (select multiple)?'
)
);
process.stdin.write('a\n'); // 'a' means select all
await waitForPrompt(process, chunk =>
chunk.includes(`What's your Build Command?`)
);
process.stdin.write(`${buildCommand ?? ''}\n`);
await waitForPrompt(process, chunk =>
chunk.includes(`What's your Output Directory?`)
);
process.stdin.write(`${outputDirectory ?? ''}\n`);
await waitForPrompt(process, chunk =>
chunk.includes(`What's your Development Command?`)
);
process.stdin.write(`${devCommand ?? ''}\n`);
} else {
process.stdin.write('no\n');
}
await waitForPrompt(process, chunk => chunk.includes('Linked to'));
}
test.before(async () => {
try {
await createUser();
await prepareFixtures(contextName);
await prepareFixtures(contextName, binaryPath);
} catch (err) {
console.log('Failed `test.before`');
console.log(err);
@@ -2227,13 +2286,93 @@ test('whoami', async t => {
t.is(stdout, contextName, formatOutput({ stdout, stderr }));
});
test('fail `now dev` dev script without now.json', async t => {
test('[vercel dev] fails when dev script calls vercel dev recursively', async t => {
const deploymentPath = fixture('now-dev-fail-dev-script');
const { exitCode, stderr } = await execute(['dev', deploymentPath]);
t.is(exitCode, 1);
t.true(
stderr.includes('must not contain `now dev`'),
stderr.includes('must not recursively invoke itself'),
`Received instead: "${stderr}"`
);
});
test('[vercel dev] fails when development commad calls vercel dev recursively', async t => {
const dir = fixture('dev-fail-on-recursion-command');
const projectName = `dev-fail-on-recursion-command-${
Math.random().toString(36).split('.')[1]
}`;
const dev = execa(binaryPath, ['dev', ...defaultArgs], {
cwd: dir,
reject: false,
});
await setupProject(dev, projectName, {
devCommand: `${binaryPath} dev`,
});
const { exitCode, stderr } = await dev;
t.is(exitCode, 1);
t.true(
stderr.includes('must not recursively invoke itself'),
`Received instead: "${stderr}"`
);
});
test('[vercel build] fails when build commad calls vercel build recursively', async t => {
const dir = fixture('build-fail-on-recursion-command');
const projectName = `build-fail-on-recursion-command-${
Math.random().toString(36).split('.')[1]
}`;
const build = execa(binaryPath, ['build', ...defaultArgs], {
cwd: dir,
reject: false,
});
await waitForPrompt(build, chunk =>
chunk.includes('No Project Settings found locally')
);
build.stdin.write('yes\n');
await setupProject(build, projectName, {
buildCommand: `${binaryPath} build`,
});
const { exitCode, stderr } = await build;
t.is(exitCode, 1);
t.true(
stderr.includes('must not recursively invoke itself'),
`Received instead: "${stderr}"`
);
});
test('[vercel build] fails when build script calls vercel build recursively', async t => {
const dir = fixture('build-fail-on-recursion-script');
const projectName = `build-fail-on-recursion-script-${
Math.random().toString(36).split('.')[1]
}`;
const build = execa(binaryPath, ['build', ...defaultArgs], {
cwd: dir,
reject: false,
});
await waitForPrompt(build, chunk =>
chunk.includes('No Project Settings found locally')
);
build.stdin.write('yes\n');
await setupProject(build, projectName);
const { exitCode, stderr } = await build;
t.is(exitCode, 1);
t.true(
stderr.includes('must not recursively invoke itself'),
`Received instead: "${stderr}"`
);
});
@@ -2544,7 +2683,7 @@ test('deploy a Lambda with 3 seconds of maxDuration', async t => {
const url = new URL(output.stdout);
// Should time out
url.pathname = '/api/wait-for/4';
url.pathname = '/api/wait-for/5';
const response1 = await fetch(url.href);
t.is(
response1.status,
@@ -2553,7 +2692,7 @@ test('deploy a Lambda with 3 seconds of maxDuration', async t => {
);
// Should not time out
url.pathname = '/api/wait-for/2';
url.pathname = '/api/wait-for/1';
const response2 = await fetch(url.href);
t.is(
response2.status,
@@ -2683,59 +2822,10 @@ test('should show prompts to set up project during first deploy', async t => {
const now = execa(binaryPath, [dir, ...defaultArgs]);
await waitForPrompt(now, chunk => /Set up and deploy [^?]+\?/.test(chunk));
now.stdin.write('yes\n');
await waitForPrompt(now, chunk =>
chunk.includes('Which scope do you want to deploy to?')
);
now.stdin.write('\n');
await waitForPrompt(now, chunk =>
chunk.includes('Link to existing project?')
);
now.stdin.write('no\n');
await waitForPrompt(now, chunk =>
chunk.includes('Whats your projects name?')
);
now.stdin.write(`${projectName}\n`);
await waitForPrompt(now, chunk =>
chunk.includes('In which directory is your code located?')
);
now.stdin.write('\n');
await waitForPrompt(now, chunk =>
chunk.includes('Want to override the settings?')
);
now.stdin.write('yes\n');
await waitForPrompt(now, chunk =>
chunk.includes(
'Which settings would you like to overwrite (select multiple)?'
)
);
now.stdin.write('a\n'); // 'a' means select all
await waitForPrompt(now, chunk =>
chunk.includes(`What's your Build Command?`)
);
now.stdin.write(
`mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html\n`
);
await waitForPrompt(now, chunk =>
chunk.includes(`What's your Output Directory?`)
);
now.stdin.write(`o\n`);
await waitForPrompt(now, chunk =>
chunk.includes(`What's your Development Command?`)
);
now.stdin.write(`\n`);
await waitForPrompt(now, chunk => chunk.includes('Linked to'));
await setupProject(now, projectName, {
buildCommand: `mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html`,
outputDirectory: 'o',
});
const output = await now;
@@ -3301,55 +3391,10 @@ test('[vc link] should show prompts to set up project', async t => {
const vc = execa(binaryPath, ['link', ...defaultArgs], { cwd: dir });
await waitForPrompt(vc, chunk => /Set up [^?]+\?/.test(chunk));
vc.stdin.write('yes\n');
await waitForPrompt(vc, chunk =>
chunk.includes('Which scope should contain your project?')
);
vc.stdin.write('\n');
await waitForPrompt(vc, chunk => chunk.includes('Link to existing project?'));
vc.stdin.write('no\n');
await waitForPrompt(vc, chunk =>
chunk.includes('Whats your projects name?')
);
vc.stdin.write(`${projectName}\n`);
await waitForPrompt(vc, chunk =>
chunk.includes('In which directory is your code located?')
);
vc.stdin.write('\n');
await waitForPrompt(vc, chunk =>
chunk.includes('Want to override the settings?')
);
vc.stdin.write('yes\n');
await waitForPrompt(vc, chunk =>
chunk.includes(
'Which settings would you like to overwrite (select multiple)?'
)
);
vc.stdin.write('a\n'); // 'a' means select all
await waitForPrompt(vc, chunk =>
chunk.includes(`What's your Build Command?`)
);
vc.stdin.write(`mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html\n`);
await waitForPrompt(vc, chunk =>
chunk.includes(`What's your Output Directory?`)
);
vc.stdin.write(`o\n`);
await waitForPrompt(vc, chunk =>
chunk.includes(`What's your Development Command?`)
);
vc.stdin.write(`\n`);
await waitForPrompt(vc, chunk => chunk.includes('Linked to'));
await setupProject(vc, projectName, {
buildCommand: `mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html`,
outputDirectory: 'o',
});
const output = await vc;
@@ -3408,6 +3453,29 @@ test('[vc link --confirm] should not show prompts and autolink', async t => {
);
});
test('[vc link] should not duplicate paths in .gitignore', async t => {
const dir = fixture('project-link-gitignore');
// remove previously linked project if it exists
await remove(path.join(dir, '.vercel'));
const { exitCode, stderr, stdout } = await execa(
binaryPath,
['link', '--confirm', ...defaultArgs],
{ cwd: dir, reject: false }
);
// Ensure the exit code is right
t.is(exitCode, 0, formatOutput({ stderr, stdout }));
// Ensure the message is correct pattern
t.regex(stderr, /Linked to /m);
// Ensure .gitignore is created
const gitignore = await readFile(path.join(dir, '.gitignore'), 'utf8');
t.is(gitignore, '.output\n.vercel\n');
});
test('[vc dev] should show prompts to set up project', async t => {
const dir = fixture('project-link-dev');
const port = 58352;
@@ -3422,59 +3490,10 @@ test('[vc dev] should show prompts to set up project', async t => {
cwd: dir,
});
await waitForPrompt(dev, chunk => /Set up and develop [^?]+\?/.test(chunk));
dev.stdin.write('yes\n');
await waitForPrompt(dev, chunk =>
chunk.includes('Which scope should contain your project?')
);
dev.stdin.write('\n');
await waitForPrompt(dev, chunk =>
chunk.includes('Link to existing project?')
);
dev.stdin.write('no\n');
await waitForPrompt(dev, chunk =>
chunk.includes('Whats your projects name?')
);
dev.stdin.write(`${projectName}\n`);
await waitForPrompt(dev, chunk =>
chunk.includes('In which directory is your code located?')
);
dev.stdin.write('\n');
await waitForPrompt(dev, chunk =>
chunk.includes('Want to override the settings?')
);
dev.stdin.write('yes\n');
await waitForPrompt(dev, chunk =>
chunk.includes(
'Which settings would you like to overwrite (select multiple)?'
)
);
dev.stdin.write('a\n'); // 'a' means select all
await waitForPrompt(dev, chunk =>
chunk.includes(`What's your Build Command?`)
);
dev.stdin.write(
`mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html\n`
);
await waitForPrompt(dev, chunk =>
chunk.includes(`What's your Output Directory?`)
);
dev.stdin.write(`o\n`);
await waitForPrompt(dev, chunk =>
chunk.includes(`What's your Development Command?`)
);
dev.stdin.write(`\n`);
await waitForPrompt(dev, chunk => chunk.includes('Linked to'));
await setupProject(dev, projectName, {
buildCommand: `mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html`,
outputDirectory: 'o',
});
// Ensure .gitignore is created
const gitignore = await readFile(path.join(dir, '.gitignore'), 'utf8');
@@ -3574,59 +3593,12 @@ test('[vc dev] should send the platform proxy request headers to frontend dev se
cwd: dir,
});
await waitForPrompt(dev, chunk => /Set up and develop [^?]+\?/.test(chunk));
dev.stdin.write('yes\n');
await setupProject(dev, projectName, {
buildCommand: `mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html`,
outputDirectory: 'o',
devCommand: 'node server.js',
});
await waitForPrompt(dev, chunk =>
chunk.includes('Which scope should contain your project?')
);
dev.stdin.write('\n');
await waitForPrompt(dev, chunk =>
chunk.includes('Link to existing project?')
);
dev.stdin.write('no\n');
await waitForPrompt(dev, chunk =>
chunk.includes('Whats your projects name?')
);
dev.stdin.write(`${projectName}\n`);
await waitForPrompt(dev, chunk =>
chunk.includes('In which directory is your code located?')
);
dev.stdin.write('\n');
await waitForPrompt(dev, chunk =>
chunk.includes('Want to override the settings?')
);
dev.stdin.write('yes\n');
await waitForPrompt(dev, chunk =>
chunk.includes(
'Which settings would you like to overwrite (select multiple)?'
)
);
dev.stdin.write('a\n'); // 'a' means select all
await waitForPrompt(dev, chunk =>
chunk.includes(`What's your Build Command?`)
);
dev.stdin.write(
`mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html\n`
);
await waitForPrompt(dev, chunk =>
chunk.includes(`What's your Output Directory?`)
);
dev.stdin.write(`o\n`);
await waitForPrompt(dev, chunk =>
chunk.includes(`What's your Development Command?`)
);
dev.stdin.write(`node server.js\n`);
await waitForPrompt(dev, chunk => chunk.includes('Linked to'));
await waitForPrompt(dev, chunk => chunk.includes('Ready! Available at'));
// Ensure that `vc dev` also works

View File

@@ -374,4 +374,15 @@ describe('DevServer', () => {
);
})
);
it(
'should render an error page when the middleware returns not a Response',
testFixture('edge-middleware-invalid-response', async server => {
const response = await fetch(`${server.address}/index.html`);
const body = await response.text();
expect(body).toStrictEqual(
'A server error has occurred\n\nEDGE_FUNCTION_INVOCATION_FAILED\n'
);
})
);
});

View File

@@ -5,7 +5,7 @@ describe('getUpdateCommand', () => {
it('should detect update command', async () => {
const updateCommand = await getUpdateCommand();
expect(updateCommand).toEqual(
`yarn add vercel@${isCanary() ? 'canary' : 'latest'}`
`npm i vercel@${isCanary() ? 'canary' : 'latest'}`
);
});
});

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/frameworks",
"version": "0.5.1-canary.11",
"version": "0.5.1-canary.12",
"main": "./dist/frameworks.js",
"types": "./dist/frameworks.d.ts",
"files": [

View File

@@ -43,6 +43,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `blitz build`',
value: 'blitz build',
},
devCommand: {
value: 'blitz start',
@@ -51,8 +52,6 @@ export const frameworks = [
placeholder: 'Next.js default',
},
},
devCommand: 'blitz start',
buildCommand: 'blitz build',
getFsOutputDir: async () => '.next',
getOutputDirName: async () => 'public',
},
@@ -83,6 +82,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `next build`',
value: 'next build',
},
devCommand: {
value: 'next dev --port $PORT',
@@ -98,8 +98,6 @@ export const frameworks = [
dependencies: ['next-plugin-sentry', 'next-sentry-source-maps'],
},
],
devCommand: 'next dev --port $PORT',
buildCommand: 'next build',
getFsOutputDir: async () => '.next',
getOutputDirName: async () => 'public',
cachePattern: '.next/cache/**',
@@ -131,6 +129,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `gatsby build`',
value: 'gatsby build',
},
devCommand: {
value: 'gatsby develop --port $PORT',
@@ -141,8 +140,6 @@ export const frameworks = [
},
},
dependency: 'gatsby',
devCommand: 'gatsby develop --port $PORT',
buildCommand: 'gatsby build',
getOutputDirName: async () => 'public',
getFsOutputDir: async () => 'public',
defaultRoutes: async (dirPrefix: string) => {
@@ -219,6 +216,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `hexo generate`',
value: 'hexo generate',
},
devCommand: {
value: 'hexo server --port $PORT',
@@ -229,8 +227,6 @@ export const frameworks = [
},
},
dependency: 'hexo',
devCommand: 'hexo server --port $PORT',
buildCommand: 'hexo generate',
getFsOutputDir: async () => 'public',
getOutputDirName: async () => 'public',
},
@@ -259,6 +255,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `npx @11ty/eleventy`',
value: 'npx @11ty/eleventy',
},
devCommand: {
value: 'npx @11ty/eleventy --serve --watch --port $PORT',
@@ -269,8 +266,6 @@ export const frameworks = [
},
},
dependency: '@11ty/eleventy',
devCommand: 'npx @11ty/eleventy --serve --watch --port $PORT',
buildCommand: 'npx @11ty/eleventy',
getFsOutputDir: async () => '_site',
getOutputDirName: async () => '_site',
cachePattern: '.cache/**',
@@ -300,6 +295,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `docusaurus build`',
value: 'docusaurus build',
},
devCommand: {
value: 'docusaurus start --port $PORT',
@@ -310,8 +306,6 @@ export const frameworks = [
},
},
dependency: '@docusaurus/core',
devCommand: 'docusaurus start --port $PORT',
buildCommand: 'docusaurus build',
getFsOutputDir: async (dirPrefix: string) => {
const base = 'build';
try {
@@ -456,6 +450,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `docusaurus-build`',
value: 'docusaurus-build',
},
devCommand: {
value: 'docusaurus-start --port $PORT',
@@ -466,8 +461,6 @@ export const frameworks = [
},
},
dependency: 'docusaurus',
devCommand: 'docusaurus-start --port $PORT',
buildCommand: 'docusaurus-build',
getFsOutputDir: async (dirPrefix: string) => {
const base = 'build';
try {
@@ -523,6 +516,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `preact build`',
value: 'preact build',
},
devCommand: {
value: 'preact watch --port $PORT',
@@ -533,8 +527,6 @@ export const frameworks = [
},
},
dependency: 'preact-cli',
devCommand: 'preact watch --port $PORT',
buildCommand: 'preact build',
getFsOutputDir: async () => 'build',
getOutputDirName: async () => 'build',
defaultRoutes: [
@@ -581,6 +573,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `dojo build`',
value: 'dojo build',
},
devCommand: {
value: 'dojo build -m dev -w -s -p $PORT',
@@ -591,8 +584,6 @@ export const frameworks = [
},
},
dependency: '@dojo/cli',
devCommand: 'dojo build -m dev -w -s -p $PORT',
buildCommand: 'dojo build',
getFsOutputDir: async () => 'output/dist',
getOutputDirName: async () => join('output', 'dist'),
defaultRoutes: [
@@ -649,6 +640,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `ember build`',
value: 'ember build',
},
devCommand: {
value: 'ember serve --port $PORT',
@@ -659,8 +651,6 @@ export const frameworks = [
},
},
dependency: 'ember-cli',
devCommand: 'ember serve --port $PORT',
buildCommand: 'ember build',
getFsOutputDir: async () => 'dist',
getOutputDirName: async () => 'dist',
defaultRoutes: [
@@ -705,6 +695,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `vue-cli-service build`',
value: 'vue-cli-service build',
},
devCommand: {
value: 'vue-cli-service serve --port $PORT',
@@ -715,8 +706,6 @@ export const frameworks = [
},
},
dependency: '@vue/cli-service',
devCommand: 'vue-cli-service serve --port $PORT',
buildCommand: 'vue-cli-service build',
getFsOutputDir: async () => 'dist',
getOutputDirName: async () => 'dist',
defaultRoutes: [
@@ -783,6 +772,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `ng build && scully`',
value: 'ng build && scully',
},
devCommand: {
value: 'ng serve --port $PORT',
@@ -793,8 +783,6 @@ export const frameworks = [
},
},
dependency: '@scullyio/init',
devCommand: 'ng serve --port $PORT',
buildCommand: 'ng build && scully',
getFsOutputDir: async () => 'dist',
getOutputDirName: async () => 'dist/static',
},
@@ -822,6 +810,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `ng build`',
value: 'ng build',
},
devCommand: {
value: 'ng serve --port $PORT',
@@ -831,8 +820,6 @@ export const frameworks = [
},
},
dependency: '@ionic/angular',
devCommand: 'ng serve --port $PORT',
buildCommand: 'ng build',
getFsOutputDir: async () => 'www',
getOutputDirName: async () => 'www',
defaultRoutes: [
@@ -876,6 +863,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `ng build`',
value: 'ng build',
},
devCommand: {
value: 'ng serve --port $PORT',
@@ -886,8 +874,6 @@ export const frameworks = [
},
},
dependency: '@angular/cli',
devCommand: 'ng serve --port $PORT',
buildCommand: 'ng build',
getFsOutputDir: async () => 'dist',
getOutputDirName: async (dirPrefix: string) => {
const base = 'dist';
@@ -945,6 +931,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `polymer build`',
value: 'polymer build',
},
devCommand: {
value: 'polymer serve --port $PORT',
@@ -955,8 +942,6 @@ export const frameworks = [
},
},
dependency: 'polymer-cli',
devCommand: 'polymer serve --port $PORT',
buildCommand: 'polymer build',
getFsOutputDir: async () => 'build',
getOutputDirName: async (dirPrefix: string) => {
const base = 'build';
@@ -1016,6 +1001,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `rollup -c`',
value: 'rollup -c',
},
devCommand: {
value: 'rollup -c -w',
@@ -1025,8 +1011,6 @@ export const frameworks = [
},
},
dependency: 'sirv-cli',
devCommand: 'rollup -c -w',
buildCommand: 'rollup -c',
getFsOutputDir: async () => 'public',
getOutputDirName: async () => 'public',
defaultRoutes: [
@@ -1070,6 +1054,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `svelte-kit build`',
value: 'svelte-kit build',
},
devCommand: {
value: 'svelte-kit dev --port $PORT',
@@ -1079,8 +1064,6 @@ export const frameworks = [
placeholder: 'public',
},
},
devCommand: 'svelte-kit dev --port $PORT',
buildCommand: 'svelte-kit build',
getFsOutputDir: async () => '.output',
getOutputDirName: async () => 'public',
},
@@ -1108,6 +1091,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `react-scripts build`',
value: 'react-scripts build',
},
devCommand: {
value: 'react-scripts start',
@@ -1117,8 +1101,6 @@ export const frameworks = [
},
},
dependency: '@ionic/react',
devCommand: 'react-scripts start',
buildCommand: 'react-scripts build',
getFsOutputDir: async () => 'build',
getOutputDirName: async () => 'build',
defaultRoutes: [
@@ -1216,6 +1198,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `react-scripts build`',
value: 'react-scripts build',
},
devCommand: {
value: 'react-scripts start',
@@ -1225,8 +1208,6 @@ export const frameworks = [
},
},
dependency: 'react-scripts',
devCommand: 'react-scripts start',
buildCommand: 'react-scripts build',
getFsOutputDir: async () => 'build',
getOutputDirName: async () => 'build',
defaultRoutes: [
@@ -1318,6 +1299,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `gridsome build`',
value: 'gridsome build',
},
devCommand: {
value: 'gridsome develop -p $PORT',
@@ -1328,8 +1310,6 @@ export const frameworks = [
},
},
dependency: 'gridsome',
devCommand: 'gridsome develop -p $PORT',
buildCommand: 'gridsome build',
getFsOutputDir: async () => 'dist',
getOutputDirName: async () => 'dist',
},
@@ -1357,6 +1337,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `umi build`',
value: 'umi build',
},
devCommand: {
value: 'umi dev --port $PORT',
@@ -1367,8 +1348,6 @@ export const frameworks = [
},
},
dependency: 'umi',
devCommand: 'umi dev --port $PORT',
buildCommand: 'umi build',
getFsOutputDir: async () => 'dist',
getOutputDirName: async () => 'dist',
defaultRoutes: [
@@ -1412,6 +1391,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `sapper export`',
value: 'sapper export',
},
devCommand: {
value: 'sapper dev --port $PORT',
@@ -1422,8 +1402,6 @@ export const frameworks = [
},
},
dependency: 'sapper',
devCommand: 'sapper dev --port $PORT',
buildCommand: 'sapper export',
getFsOutputDir: async () => '__sapper__/export',
getOutputDirName: async () => '__sapper__/export',
},
@@ -1451,6 +1429,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `saber build`',
value: 'saber build',
},
devCommand: {
value: 'saber --port $PORT',
@@ -1461,8 +1440,6 @@ export const frameworks = [
},
},
dependency: 'saber',
devCommand: 'saber --port $PORT',
buildCommand: 'saber build',
getFsOutputDir: async () => 'public',
getOutputDirName: async () => 'public',
defaultRoutes: [
@@ -1521,6 +1498,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `stencil build`',
value: 'stencil build',
},
devCommand: {
value: 'stencil build --dev --watch --serve --port $PORT',
@@ -1531,8 +1509,6 @@ export const frameworks = [
},
},
dependency: '@stencil/core',
devCommand: 'stencil build --dev --watch --serve --port $PORT',
buildCommand: 'stencil build',
getFsOutputDir: async () => 'www',
getOutputDirName: async () => 'www',
defaultRoutes: [
@@ -1611,6 +1587,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `nuxt generate`',
value: 'nuxt generate',
},
devCommand: {
value: 'nuxt',
@@ -1620,8 +1597,6 @@ export const frameworks = [
},
},
dependency: 'nuxt',
devCommand: 'nuxt',
buildCommand: 'nuxt generate',
getFsOutputDir: async () => '.output',
getOutputDirName: async () => 'dist',
cachePattern: '.nuxt/**',
@@ -1680,8 +1655,6 @@ export const frameworks = [
placeholder: 'RedwoodJS default',
},
},
devCommand: 'yarn rw dev --fwd="--port=$PORT --open=false',
buildCommand: 'yarn rw deploy vercel',
getFsOutputDir: async () => 'public',
getOutputDirName: async () => 'public',
},
@@ -1717,6 +1690,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `hugo -D --gc`',
value: 'hugo -D --gc',
},
devCommand: {
value: 'hugo server -D -w -p $PORT',
@@ -1726,8 +1700,6 @@ export const frameworks = [
placeholder: '`public` or `publishDir` from the `config` file',
},
},
devCommand: 'hugo server -D -w -p $PORT',
buildCommand: 'hugo -D --gc',
getFsOutputDir: async (dirPrefix: string): Promise<string> => {
type HugoConfig = { publishDir?: string };
const config = await readConfigFile<HugoConfig>(
@@ -1772,6 +1744,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `jekyll build`',
value: 'jekyll build',
},
devCommand: {
value: 'bundle exec jekyll serve --watch --port $PORT',
@@ -1781,8 +1754,6 @@ export const frameworks = [
placeholder: '`_site` or `destination` from `_config.yml`',
},
},
devCommand: 'bundle exec jekyll serve --watch --port $PORT',
buildCommand: 'jekyll build',
getFsOutputDir: async (dirPrefix: string): Promise<string> => {
type JekyllConfig = { destination?: string };
const config = await readConfigFile<JekyllConfig>(
@@ -1821,6 +1792,7 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `brunch build --production`',
value: 'brunch build --production',
},
devCommand: {
value: 'brunch watch --server --port $PORT',
@@ -1830,8 +1802,6 @@ export const frameworks = [
value: 'public',
},
},
devCommand: 'brunch watch --server --port $PORT',
buildCommand: 'brunch build --production',
getFsOutputDir: async () => 'public',
getOutputDirName: async () => 'public',
},
@@ -1856,18 +1826,17 @@ export const frameworks = [
value: 'bundle install',
},
buildCommand: {
value: '`npm run build` or `bundle exec middleman build`',
placeholder: '`npm run build` or `bundle exec middleman build`',
value: 'bundle exec middleman build',
},
devCommand: {
value: 'bundle exec middleman server -p $PORT',
placeholder: 'bundle exec middleman server',
value: 'bundle exec middleman server -p $PORT',
},
outputDirectory: {
value: 'build',
},
},
devCommand: 'bundle exec middleman server -p $PORT',
buildCommand: 'bundle exec middleman build',
getFsOutputDir: async () => 'build',
getOutputDirName: async () => 'build',
cachePattern: '{vendor/bin,vendor/cache,vendor/bundle}/**',
@@ -1896,15 +1865,13 @@ export const frameworks = [
value: 'zola build',
},
devCommand: {
value: 'zola serve --port $PORT',
placeholder: 'zola serve',
value: 'zola serve --port $PORT',
},
outputDirectory: {
value: 'public',
},
},
devCommand: 'zola serve --port $PORT',
buildCommand: 'zola build',
getFsOutputDir: async () => 'public',
getOutputDirName: async () => 'public',
defaultVersion: '0.13.0',
@@ -1934,17 +1901,17 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `vite build`',
value: 'vite build',
},
devCommand: {
placeholder: 'vite',
value: 'vite',
},
outputDirectory: {
value: 'dist',
},
},
dependency: 'vite',
devCommand: 'vite',
buildCommand: 'vite build',
getFsOutputDir: async () => 'dist',
getOutputDirName: async () => 'dist',
},
@@ -1972,17 +1939,17 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run build` or `parcel build`',
value: 'parcel build',
},
devCommand: {
placeholder: 'parcel',
value: 'parcel',
},
outputDirectory: {
placeholder: 'dist',
value: 'dist',
},
},
dependency: 'parcel',
devCommand: 'parcel',
buildCommand: 'parcel build',
getFsOutputDir: async () => 'dist',
getOutputDirName: async () => 'dist',
defaultRoutes: [
@@ -2016,16 +1983,16 @@ export const frameworks = [
},
buildCommand: {
placeholder: '`npm run vercel-build` or `npm run build`',
value: null,
},
devCommand: {
placeholder: 'None',
value: null,
},
outputDirectory: {
placeholder: '`public` if it exists, or `.`',
},
},
devCommand: null,
buildCommand: null,
getFsOutputDir: async (dirPrefix: string): Promise<string> => {
// Public if it exists or `.`
let base = 'public';

View File

@@ -26,7 +26,7 @@ export interface SettingValue {
* A predefined setting for the detected framework
* @example "next dev --port $PORT"
*/
value: string;
value: string | null;
placeholder?: string;
}
@@ -129,11 +129,11 @@ export interface Framework {
/**
* Default Build Command or a placeholder
*/
buildCommand: Setting;
buildCommand: SettingValue;
/**
* Default Development Command or a placeholder
*/
devCommand: Setting;
devCommand: SettingValue;
/**
* Default Output Directory
*/
@@ -157,6 +157,7 @@ export interface Framework {
/**
* Name of a dependency in `package.json` to detect this framework.
* @example "hexo"
* @deprecated use `detectors` instead (new frameworks should not use this prop)
*/
dependency?: string;
/**
@@ -201,16 +202,6 @@ export interface Framework {
* @example ".cache/**"
*/
cachePattern?: string;
/**
* The default build command for the framework.
* @example "next build"
*/
buildCommand: string | null;
/**
* The default development command for the framework.
* @example "next dev"
*/
devCommand: string | null;
/**
* The default version of the framework command that is available within the
* build image. Usually an environment variable can be set to override this.

View File

@@ -34,7 +34,7 @@ const SchemaSettings = {
additionalProperties: false,
properties: {
value: {
type: 'string',
type: ['string', 'null'],
},
placeholder: {
type: 'string',
@@ -58,15 +58,7 @@ const Schema = {
type: 'array',
items: {
type: 'object',
required: [
'name',
'slug',
'logo',
'description',
'settings',
'buildCommand',
'devCommand',
],
required: ['name', 'slug', 'logo', 'description', 'settings'],
properties: {
name: { type: 'string' },
slug: { type: ['string', 'null'] },
@@ -138,8 +130,6 @@ const Schema = {
dependency: { type: 'string' },
cachePattern: { type: 'string' },
buildCommand: { type: ['string', 'null'] },
devCommand: { type: ['string', 'null'] },
defaultVersion: { type: 'string' },
},
},

View File

@@ -6,7 +6,8 @@ import { join } from 'path';
import stringArgv from 'string-argv';
import { debug } from '@vercel/build-utils';
const versionMap = new Map([
['1.16', '1.16'],
['1.17', '1.17.3'],
['1.16', '1.16.10'],
['1.15', '1.15.8'],
['1.14', '1.14.15'],
['1.13', '1.13.15'],

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/go",
"version": "1.2.4-canary.3",
"version": "1.2.4-canary.4",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",

View File

@@ -5,10 +5,10 @@
{ "src": "subdirectory/index.go", "use": "@vercel/go" }
],
"probes": [
{ "path": "/", "mustContain": "cow:go1.16:RANDOMNESS_PLACEHOLDER" },
{ "path": "/", "mustContain": "cow:go1.17.3:RANDOMNESS_PLACEHOLDER" },
{
"path": "/subdirectory",
"mustContain": "subcow:go1.16:RANDOMNESS_PLACEHOLDER"
"mustContain": "subcow:go1.17.3:RANDOMNESS_PLACEHOLDER"
}
]
}

View File

@@ -1,3 +1,3 @@
module with-nested
go 1.12
go 1.16

View File

@@ -1,6 +1,6 @@
{
"name": "vercel-plugin-middleware",
"version": "0.0.0-canary.6",
"version": "0.0.0-canary.7",
"license": "MIT",
"main": "./dist/index",
"homepage": "",

View File

@@ -3,13 +3,16 @@ _ENTRIES = typeof _ENTRIES === 'undefined' ? {} : _ENTRIES;
_ENTRIES['middleware_pages/_middleware'] = {
default: async function (ev) {
const result = await middleware.default(ev.request, ev);
if (result instanceof Response) {
return {
promise: Promise.resolve(),
waitUntil: Promise.resolve(),
response: result,
};
}
return result;
return {
promise: Promise.resolve(),
waitUntil: Promise.resolve(),
response:
result ||
new Response(null, {
headers: {
'x-middleware-next': 1,
},
}),
};
},
};

View File

@@ -1,8 +1,19 @@
import { join } from 'path';
import { promises as fsp } from 'fs';
import { build } from '../src';
import { Response } from 'node-fetch';
describe('build()', () => {
beforeEach(() => {
//@ts-ignore
global.Response = Response;
});
afterEach(() => {
//@ts-ignore
delete global.Response;
//@ts-ignore
delete global._ENTRIES;
});
it('should build simple middleware', async () => {
const fixture = join(__dirname, 'fixtures/simple');
await build({
@@ -17,8 +28,30 @@ describe('build()', () => {
);
expect(middlewareManifest).toMatchSnapshot();
const outputFile = join(fixture, '.output/server/pages/_middleware.js');
expect(await fsp.stat(outputFile)).toBeTruthy();
require(outputFile);
//@ts-ignore
const middleware = global._ENTRIES['middleware_pages/_middleware'].default;
expect(typeof middleware).toStrictEqual('function');
const handledResponse = await middleware({
request: {
url: 'http://google.com',
},
});
const unhandledResponse = await middleware({
request: {
url: 'literallyanythingelse',
},
});
expect(String(handledResponse.response.body)).toEqual('Hi from the edge!');
expect(
await fsp.stat(join(fixture, '.output/server/pages/_middleware.js'))
).toBeTruthy();
(handledResponse.response as Response).headers.get('x-middleware-next')
).toEqual(null);
expect(unhandledResponse.response.body).toEqual(null);
expect(
(unhandledResponse.response as Response).headers.get('x-middleware-next')
).toEqual('1');
});
});

View File

@@ -1 +1,5 @@
export default () => new Response('Hi from the edge!');
export default req => {
if (req.url === 'http://google.com') {
return new Response('Hi from the edge!');
}
};

View File

@@ -0,0 +1,27 @@
{
"private": false,
"name": "vercel-plugin-go",
"version": "1.0.0-canary.4",
"main": "dist/index.js",
"license": "MIT",
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/vercel/vercel.git",
"directory": "packages/vercel-plugin-go"
},
"scripts": {
"build": "tsc",
"prepublishOnly": "tsc"
},
"dependencies": {
"@vercel/build-utils": "2.12.3-canary.19",
"@vercel/go": "1.2.4-canary.4"
},
"devDependencies": {
"@types/node": "*",
"typescript": "4.3.4"
}
}

View File

@@ -0,0 +1,6 @@
import { convertRuntimeToPlugin } from '@vercel/build-utils';
import * as go from '@vercel/go';
export const build = convertRuntimeToPlugin(go.build, '.go');
export const startDevServer = go.startDevServer;

View File

@@ -0,0 +1,17 @@
{
"compilerOptions": {
"declaration": false,
"esModuleInterop": true,
"lib": ["esnext"],
"module": "commonjs",
"moduleResolution": "node",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "dist",
"strict": true,
"target": "esnext"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "vercel-plugin-node",
"version": "1.12.2-canary.7",
"version": "1.12.2-canary.10",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
@@ -33,7 +33,9 @@
"@types/jest": "27.0.2",
"@types/node-fetch": "2",
"@types/test-listen": "1.1.0",
"@vercel/fun": "1.0.1",
"@types/yazl": "2.4.2",
"@vercel/build-utils": "2.12.3-canary.19",
"@vercel/fun": "1.0.3",
"@vercel/ncc": "0.24.0",
"@vercel/nft": "0.14.0",
"@vercel/node-bridge": "2.1.1-canary.2",
@@ -46,7 +48,8 @@
"node-fetch": "2",
"source-map-support": "0.5.12",
"test-listen": "1.1.0",
"ts-morph": "12.0.0"
"ts-morph": "12.0.0",
"yazl": "2.5.1"
},
"jest": {
"preset": "ts-jest",

View File

@@ -6,6 +6,7 @@ import {
readlinkSync,
statSync,
promises as fsp,
existsSync,
} from 'fs';
import {
basename,
@@ -35,6 +36,7 @@ import {
debug,
isSymbolicLink,
runNpmInstall,
updateFunctionsManifest,
walkParentDirs,
} from '@vercel/build-utils';
import { FromSchema } from 'json-schema-to-ts';
@@ -43,6 +45,7 @@ import { AbortController } from 'abort-controller';
import { Register, register } from './typescript';
import { pageToRoute } from './router/page-to-route';
import { isDynamicRoute } from './router/is-dynamic';
import crypto from 'crypto';
export { shouldServe };
export {
@@ -117,9 +120,14 @@ async function downloadInstallAndBundle({
const nodeVersion = await getNodeVersion(entrypointFsDirname);
const spawnOpts = getSpawnOptions({}, nodeVersion);
const installTime = Date.now();
await runNpmInstall(entrypointFsDirname, [], spawnOpts, {}, nodeVersion);
debug(`Install complete [${Date.now() - installTime}ms]`);
// Only run when `package.json` exists.
if (existsSync(join(entrypointFsDirname, 'package.json'))) {
const installTime = Date.now();
await runNpmInstall(entrypointFsDirname, [], spawnOpts, {}, nodeVersion);
debug(`Install complete [${Date.now() - installTime}ms]`);
} else {
debug(`Skip install command for \`vercel-plugin-node\`.`);
}
return {
nodeVersion,
@@ -383,20 +391,23 @@ export async function buildEntrypoint({
entrypoint: string;
config: FunctionConfig;
}) {
// Unique hash that will be used as directory name for `.output`.
const entrypointHash = crypto
.createHash('sha256')
.update(entrypoint)
.digest('hex');
const outputDirPath = join(workPath, '.output');
const { dir, name } = parsePath(entrypoint);
const entrypointWithoutExt = join('/', dir, name);
const entrypointWithoutExtIndex = join(
dir,
name,
name === 'index' ? '' : 'index'
);
const outputWorkPath = join(
outputDirPath,
'server/pages',
entrypointWithoutExtIndex
);
const outputWorkPath = join(outputDirPath, 'inputs', entrypointHash);
const pagesDir = join(outputDirPath, 'server', 'pages');
const pageOutput = join(pagesDir, renameTStoJS(entrypoint));
const nftOutput = `${pageOutput}.nft.json`;
await fsp.mkdir(outputWorkPath, { recursive: true });
await fsp.mkdir(parsePath(pageOutput).dir, { recursive: true });
console.log(`Compiling "${entrypoint}" to "${outputWorkPath}"`);
const shouldAddHelpers =
@@ -470,6 +481,8 @@ export async function buildEntrypoint({
...launcherFiles,
};
const nftFiles: { input: string; output: string }[] = [];
for (const filename of Object.keys(files)) {
const outPath = join(outputWorkPath, filename);
const file = files[filename];
@@ -480,30 +493,42 @@ export async function buildEntrypoint({
const finishPromise = once(ws, 'finish');
file.toStream().pipe(ws);
await finishPromise;
// The `handler` will be `.output/server/pages/api/subdirectory/___vc/__launcher.launcher`
// or `.output/server/pages/api/___vc/__launcher.launcher`.
// This means everything has to be mounted to the `dirname` of the entrypoint.
nftFiles.push({
input: relative(dirname(nftOutput), outPath),
output: join('.output', 'server', 'pages', dirname(entrypoint), filename),
});
}
// Update the `functions-mainifest.json` file with this entrypoint
// TODO NATE: turn this into a `@vercel/plugin-utils` helper function?
const functionsManifestPath = join(outputDirPath, 'functions-manifest.json');
let functionsManifest: any = {};
try {
functionsManifest = JSON.parse(
await fsp.readFile(functionsManifestPath, 'utf8')
);
} catch (_err) {
// ignore...
}
if (!functionsManifest.version) functionsManifest.version = 1;
if (!functionsManifest.pages) functionsManifest.pages = {};
functionsManifest.pages[entrypointWithoutExtIndex] = {
handler: `${getFileName(LAUNCHER_FILENAME).slice(0, -3)}.launcher`,
runtime: nodeVersion.runtime,
};
await fsp.writeFile(
functionsManifestPath,
JSON.stringify(functionsManifest, null, 2)
nftOutput,
JSON.stringify({
version: 1,
files: nftFiles,
})
);
await fsp.copyFile(
join(outputWorkPath, renameTStoJS(entrypoint)),
pageOutput
);
let pageKey = relative(pagesDir, pageOutput);
if (process.platform === 'win32') {
pageKey = pageKey.replace(/\\/gm, '/');
}
const pages = {
[pageKey]: {
handler: `${getFileName(LAUNCHER_FILENAME).slice(0, -3)}.launcher`,
runtime: nodeVersion.runtime,
},
};
await updateFunctionsManifest({ workPath, pages });
// Update the `routes-mainifest.json` file with the wildcard route
// when the entrypoint is dynamic (i.e. `/api/[id].ts`).
if (isDynamicRoute(entrypointWithoutExt)) {

View File

@@ -1,6 +1,7 @@
import { join } from 'path';
import path from 'path';
import { parse } from 'url';
import { promises as fsp } from 'fs';
import { ZipFile } from 'yazl';
import { createFunction, Lambda } from '@vercel/fun';
import {
Request,
@@ -11,6 +12,7 @@ import {
Headers,
} from 'node-fetch';
import { build } from '../src';
import { runNpmInstall, streamToBuffer } from '@vercel/build-utils';
interface TestParams {
fixture: string;
@@ -50,40 +52,64 @@ function withFixture<T>(
t: (props: TestParams) => Promise<T>
): () => Promise<T> {
return async () => {
const fixture = join(__dirname, 'fixtures', name);
const fixture = path.join(__dirname, 'fixtures', name);
const functions = new Map<string, Lambda>();
async function fetch(r: RequestInfo, init?: RequestInit) {
const req = new Request(r, init);
const url = parse(req.url);
const pathWithIndex = join(
url.pathname!,
url.pathname!.endsWith('/index') ? '' : 'index'
).substring(1);
const functionPath = url.pathname!.substring(1);
let status = 404;
let headers: HeadersInit = {};
let body: string | Buffer = 'Function not found';
let fn = functions.get(pathWithIndex);
let fn = functions.get(functionPath);
if (!fn) {
const manifest = JSON.parse(
await fsp.readFile(
join(fixture, '.output/functions-manifest.json'),
path.join(fixture, '.output/functions-manifest.json'),
'utf8'
)
);
const functionManifest = manifest.pages[pathWithIndex];
const keyFile = `${functionPath}.js`;
const keyIndex = `${functionPath}/index.js`;
const fnKey = keyFile in manifest.pages ? keyFile : keyIndex;
const functionManifest = manifest.pages[fnKey];
if (functionManifest) {
const dir = join(fixture, '.output/server/pages', pathWithIndex);
const entry = path.join(fixture, '.output/server/pages', fnKey);
const nftFile = JSON.parse(
await fsp.readFile(`${entry}.nft.json`, 'utf8')
);
const zip = new ZipFile();
zip.addFile(
path.join(fixture, '.output/server/pages', fnKey),
path.join('.output/server/pages', fnKey)
);
nftFile.files.forEach((f: { input: string; output: string }) => {
const input = path.join(path.dirname(entry), f.input);
zip.addFile(input, f.output);
});
zip.end();
const handler = path.posix.join(
'.output/server/pages',
path.dirname(fnKey),
functionManifest.handler
);
fn = await createFunction({
Code: {
Directory: dir,
ZipFile: await streamToBuffer(zip.outputStream),
},
Handler: functionManifest.handler,
Handler: handler,
Runtime: functionManifest.runtime,
});
functions.set(pathWithIndex, fn);
functions.set(functionPath, fn);
}
}
@@ -109,6 +135,12 @@ function withFixture<T>(
});
}
if (
await fsp.lstat(path.join(fixture, 'package.json')).catch(() => false)
) {
await runNpmInstall(fixture);
}
await build({ workPath: fixture });
try {

View File

@@ -0,0 +1,27 @@
{
"private": false,
"name": "vercel-plugin-python",
"version": "1.0.0-canary.4",
"main": "dist/index.js",
"license": "MIT",
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/vercel/vercel.git",
"directory": "packages/vercel-plugin-python"
},
"scripts": {
"build": "tsc",
"prepublishOnly": "tsc"
},
"dependencies": {
"@vercel/build-utils": "2.12.3-canary.19",
"@vercel/python": "2.0.6-canary.6"
},
"devDependencies": {
"@types/node": "*",
"typescript": "4.3.4"
}
}

View File

@@ -0,0 +1,6 @@
import { convertRuntimeToPlugin } from '@vercel/build-utils';
import * as python from '@vercel/python';
export const build = convertRuntimeToPlugin(python.build, '.py');
//export const startDevServer = python.startDevServer;

View File

@@ -0,0 +1,18 @@
{
"compilerOptions": {
"declaration": false,
"esModuleInterop": true,
"lib": ["esnext"],
"module": "commonjs",
"moduleResolution": "node",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "dist",
"types": ["node"],
"strict": true,
"target": "esnext"
}
}

View File

@@ -0,0 +1,27 @@
{
"private": false,
"name": "vercel-plugin-ruby",
"version": "1.0.0-canary.3",
"main": "dist/index.js",
"license": "MIT",
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/vercel/vercel.git",
"directory": "packages/vercel-plugin-ruby"
},
"scripts": {
"build": "tsc",
"prepublishOnly": "tsc"
},
"dependencies": {
"@vercel/build-utils": "2.12.3-canary.19",
"@vercel/ruby": "1.2.8-canary.4"
},
"devDependencies": {
"@types/node": "*",
"typescript": "4.3.4"
}
}

View File

@@ -0,0 +1,6 @@
import { convertRuntimeToPlugin } from '@vercel/build-utils';
import * as ruby from '@vercel/ruby';
export const build = convertRuntimeToPlugin(ruby.build, '.rb');
//export const startDevServer = ruby.startDevServer;

View File

@@ -0,0 +1,18 @@
{
"compilerOptions": {
"declaration": false,
"esModuleInterop": true,
"lib": ["esnext"],
"module": "commonjs",
"moduleResolution": "node",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "dist",
"types": ["node"],
"strict": true,
"target": "esnext"
}
}

View File

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

View File

@@ -13,6 +13,7 @@ import {
createLambda,
shouldServe,
debug,
NowBuildError,
} from '@vercel/build-utils';
import { installRequirement, installRequirementsFile } from './install';
@@ -22,6 +23,7 @@ async function pipenvConvert(cmd: string, srcDir: string) {
const out = await execa.stdout(cmd, [], {
cwd: srcDir,
});
debug('Contents of requirements.txt is: ' + out);
fs.writeFileSync(join(srcDir, 'requirements.txt'), out);
} catch (err) {
console.log('Failed to run "pipfile2req"');
@@ -57,6 +59,10 @@ export const build = async ({
meta = {},
config,
}: BuildOptions) => {
let pipPath = meta.isDev ? 'pip3' : 'pip3.9';
let pythonPath = meta.isDev ? 'python3' : 'python3.9';
let pythonRuntime = meta.isDev ? 'python3' : 'python3.9';
workPath = await downloadFilesInWorkPath({
workPath,
files: originalFiles,
@@ -85,6 +91,8 @@ export const build = async ({
console.log('Installing required dependencies...');
await installRequirement({
pythonPath,
pipPath,
dependency: 'werkzeug',
version: '1.0.1',
workPath,
@@ -103,12 +111,29 @@ export const build = async ({
if (pipfileLockDir) {
debug('Found "Pipfile.lock"');
try {
const json = await readFile(join(pipfileLockDir, 'Pipfile.lock'), 'utf8');
const obj = JSON.parse(json);
if (!meta.isDev && obj?._meta?.requires?.python_version === '3.6') {
pipPath = 'pip3.6';
pythonPath = 'python3.6';
pythonRuntime = 'python3.6';
}
} catch (err) {
throw new NowBuildError({
code: 'INVALID_PIPFILE_LOCK',
message: 'Unable to parse Pipfile.lock',
});
}
// Convert Pipenv.Lock to requirements.txt.
// We use a different`workPath` here because we want `pipfile-requirements` and it's dependencies
// to not be part of the lambda environment. By using pip's `--target` directive we can isolate
// it into a separate folder.
const tempDir = await getWriteableDirectory();
await installRequirement({
pythonPath,
pipPath,
dependency: 'pipfile-requirements',
version: '0.3.0',
workPath: tempDir,
@@ -130,6 +155,8 @@ export const build = async ({
debug('Found local "requirements.txt"');
const requirementsTxtPath = fsFiles[requirementsTxt].fsPath;
await installRequirementsFile({
pythonPath,
pipPath,
filePath: requirementsTxtPath,
workPath,
meta,
@@ -138,6 +165,8 @@ export const build = async ({
debug('Found global "requirements.txt"');
const requirementsTxtPath = fsFiles['requirements.txt'].fsPath;
await installRequirementsFile({
pythonPath,
pipPath,
filePath: requirementsTxtPath,
workPath,
meta,
@@ -163,7 +192,7 @@ export const build = async ({
await writeFile(join(workPath, `${handlerPyFilename}.py`), handlerPyContents);
// Use the system-installed version of `python3` when running via `vercel dev`
const runtime = meta.isDev ? 'python3' : 'python3.6';
const runtime = meta.isDev ? 'python3' : pythonRuntime;
const globOptions: GlobOptions = {
cwd: workPath,

View File

@@ -1,6 +1,5 @@
import execa from 'execa';
import { Meta, debug } from '@vercel/build-utils';
const pipPath = 'pip3';
const makeDependencyCheckCode = (dependency: string) => `
from importlib import util
@@ -9,10 +8,14 @@ spec = util.find_spec(dep)
print(spec.origin)
`;
async function isInstalled(dependency: string, cwd: string) {
async function isInstalled(
pythonPath: string,
dependency: string,
cwd: string
) {
try {
const { stdout } = await execa(
'python3',
pythonPath,
['-c', makeDependencyCheckCode(dependency)],
{
stdio: 'pipe',
@@ -33,10 +36,14 @@ dependencies = distutils.text_file.TextFile(filename='${requirementsPath}').read
pkg_resources.require(dependencies)
`;
async function areRequirementsInstalled(requirementsPath: string, cwd: string) {
async function areRequirementsInstalled(
pythonPath: string,
requirementsPath: string,
cwd: string
) {
try {
await execa(
'python3',
pythonPath,
['-c', makeRequirementsCheckCode(requirementsPath)],
{
stdio: 'pipe',
@@ -49,7 +56,7 @@ async function areRequirementsInstalled(requirementsPath: string, cwd: string) {
}
}
async function pipInstall(workPath: string, args: string[]) {
async function pipInstall(pipPath: string, workPath: string, args: string[]) {
const target = '.';
// See: https://github.com/pypa/pip/issues/4222#issuecomment-417646535
//
@@ -66,19 +73,21 @@ async function pipInstall(workPath: string, args: string[]) {
target,
...args,
];
debug(`Running "pip3 ${cmdArgs.join(' ')}"...`);
const pretty = `${pipPath} ${cmdArgs.join(' ')}`;
debug(`Running "${pretty}"...`);
try {
await execa(pipPath, cmdArgs, {
cwd: workPath,
stdio: 'pipe',
});
} catch (err) {
console.log(`Failed to run "pip3 ${cmdArgs.join(' ')}"`);
console.log(`Failed to run "${pretty}"`);
throw err;
}
}
interface InstallRequirementArg {
pythonPath: string;
pipPath: string;
dependency: string;
version: string;
workPath: string;
@@ -91,23 +100,27 @@ interface InstallRequirementArg {
// so vc_init should do runtime version checks to be compatible with any recent
// version of its dependencies
export async function installRequirement({
pythonPath,
pipPath,
dependency,
version,
workPath,
meta,
args = [],
}: InstallRequirementArg) {
if (meta.isDev && (await isInstalled(dependency, workPath))) {
if (meta.isDev && (await isInstalled(pythonPath, dependency, workPath))) {
debug(
`Skipping ${dependency} dependency installation, already installed in ${workPath}`
);
return;
}
const exact = `${dependency}==${version}`;
await pipInstall(workPath, [exact, ...args]);
await pipInstall(pipPath, workPath, [exact, ...args]);
}
interface InstallRequirementsFileArg {
pythonPath: string;
pipPath: string;
filePath: string;
workPath: string;
meta: Meta;
@@ -115,14 +128,19 @@ interface InstallRequirementsFileArg {
}
export async function installRequirementsFile({
pythonPath,
pipPath,
filePath,
workPath,
meta,
args = [],
}: InstallRequirementsFileArg) {
if (meta.isDev && (await areRequirementsInstalled(filePath, workPath))) {
if (
meta.isDev &&
(await areRequirementsInstalled(pythonPath, filePath, workPath))
) {
debug(`Skipping requirements file installation, already installed`);
return;
}
await pipInstall(workPath, ['--upgrade', '-r', filePath, ...args]);
await pipInstall(pipPath, workPath, ['--upgrade', '-r', filePath, ...args]);
}

View File

@@ -1,6 +1,6 @@
{
"compilerOptions": {
"declaration": false,
"declaration": true,
"esModuleInterop": true,
"lib": ["esnext"],
"module": "commonjs",
@@ -13,6 +13,6 @@
"outDir": "dist",
"types": ["node"],
"strict": true,
"target": "esnext"
"target": "es2018"
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "@vercel/ruby",
"author": "Nathan Cahill <nathan@nathancahill.com>",
"version": "1.2.8-canary.3",
"version": "1.2.8-canary.4",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",

View File

@@ -1,6 +1,6 @@
{
"compilerOptions": {
"declaration": false,
"declaration": true,
"esModuleInterop": true,
"lib": ["esnext"],
"module": "commonjs",

View File

@@ -20,7 +20,12 @@ async function nowDeploy(bodies, randomness, uploadNowJson) {
(path.extname(n) === '.sh' ? 0o100755 : 0o100644),
}));
const { FORCE_BUILD_IN_REGION, NOW_DEBUG, VERCEL_DEBUG } = process.env;
const {
FORCE_BUILD_IN_REGION,
NOW_DEBUG,
VERCEL_DEBUG,
VERCEL_BUILDER_DEBUG,
} = process.env;
const nowJson = JSON.parse(bodies['vercel.json'] || bodies['now.json']);
const nowDeployPayload = {
@@ -34,6 +39,7 @@ async function nowDeploy(bodies, randomness, uploadNowJson) {
FORCE_BUILD_IN_REGION,
NOW_DEBUG,
VERCEL_DEBUG,
VERCEL_BUILDER_DEBUG,
},
},
name: 'test2020',

5
utils/run.js vendored
View File

@@ -10,11 +10,14 @@ const allPackages = [
'middleware',
'client',
'node-bridge',
'plugin-node',
'node',
'go',
'python',
'ruby',
'plugin-go',
'plugin-node',
'plugin-python',
'plugin-ruby',
'cli',
];

View File

@@ -2537,7 +2537,7 @@
dependencies:
"@types/yargs-parser" "*"
"@types/yazl@^2.4.1":
"@types/yazl@2.4.2", "@types/yazl@^2.4.1":
version "2.4.2"
resolved "https://registry.yarnpkg.com/@types/yazl/-/yazl-2.4.2.tgz#d5f8a4752261badbf1a36e8b49e042dc18ec84bc"
integrity sha512-T+9JH8O2guEjXNxqmybzQ92mJUh2oCwDDMSSimZSe1P+pceZiFROZLYmcbqkzV5EUwz6VwcKXCO2S2yUpra6XQ==
@@ -2613,10 +2613,10 @@
"@typescript-eslint/types" "4.28.0"
eslint-visitor-keys "^2.0.0"
"@vercel/fun@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@vercel/fun/-/fun-1.0.1.tgz#4b25f50c27bbbdca5b7e71f25ef5018701467962"
integrity sha512-v++KDTdKIr0357sm0AbyYk5jLmsqfUlOKhTsaN0Z/XHpk2YJHOLlGFTDzZPt+8i+ofdPG8lCfht729DHNQskJg==
"@vercel/fun@1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@vercel/fun/-/fun-1.0.3.tgz#5e5c4921a3a3a35ee129ce063e6b3f5c4b1eae48"
integrity sha512-9LKNosC+Sdx7ZNU7dnj7XXasHzgRkuDnz9+N6JzSVvb0v8KOZN0ky4+wn3c1B0/wF3dTn2W5nEzgmnsrVCDtkQ==
dependencies:
"@tootallnate/once" "2.0.0"
async-listen "1.0.0"
@@ -2629,6 +2629,7 @@
node-fetch "2.6.1"
path-match "1.2.4"
promisepipe "3.0.0"
semver "7.3.5"
stat-mode "0.3.0"
stream-to-promise "2.2.0"
tar "4.4.18"
@@ -8992,10 +8993,10 @@ node-fetch-npm@^2.0.2:
json-parse-better-errors "^1.0.0"
safe-buffer "^5.1.1"
node-fetch@2, node-fetch@^2:
version "2.6.5"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd"
integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==
node-fetch@2:
version "2.6.6"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==
dependencies:
whatwg-url "^5.0.0"
@@ -9009,6 +9010,13 @@ node-fetch@2.6.1, node-fetch@^2.2.1, node-fetch@^2.3.0, node-fetch@^2.5.0, node-
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
node-fetch@^2:
version "2.6.5"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd"
integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==
dependencies:
whatwg-url "^5.0.0"
node-gyp-build@^4.2.2:
version "4.2.3"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739"
@@ -10665,7 +10673,7 @@ semver@6.1.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b"
integrity sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==
semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
semver@7.3.5, semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
version "7.3.5"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
@@ -12618,6 +12626,13 @@ yazl@2.4.3:
dependencies:
buffer-crc32 "~0.2.3"
yazl@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.5.1.tgz#a3d65d3dd659a5b0937850e8609f22fffa2b5c35"
integrity sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==
dependencies:
buffer-crc32 "~0.2.3"
yn@3.1.1, yn@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"