Compare commits
84 Commits
@vercel/ne
...
vercel@21.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25bea3f83e | ||
|
|
b3e1828ebe | ||
|
|
e0e2a8e87e | ||
|
|
37ec89796d | ||
|
|
d3e2a4c4db | ||
|
|
ece3645914 | ||
|
|
fee386493b | ||
|
|
d95ed184ab | ||
|
|
aef8e6388e | ||
|
|
ba43e88603 | ||
|
|
e61f9740c4 | ||
|
|
c4b010fe8b | ||
|
|
db18eb091f | ||
|
|
360e62d172 | ||
|
|
8c3cd0332d | ||
|
|
f5f276021e | ||
|
|
9fbec823f3 | ||
|
|
18c3dd3a63 | ||
|
|
5a4a20b33f | ||
|
|
4489ed0c85 | ||
|
|
359f23daf1 | ||
|
|
4ef92e85db | ||
|
|
659c4d6ccd | ||
|
|
e93d477df8 | ||
|
|
f64625655b | ||
|
|
25a8189997 | ||
|
|
25c3e627cf | ||
|
|
1d6d8b530f | ||
|
|
e821cc0ae7 | ||
|
|
8ecbdc5d03 | ||
|
|
895224985b | ||
|
|
0f42a63c03 | ||
|
|
81e4c9e6fe | ||
|
|
a0a29dc836 | ||
|
|
c1f9d51d7a | ||
|
|
422f0558c1 | ||
|
|
f064ae2908 | ||
|
|
58c3e636f0 | ||
|
|
d5081367f3 | ||
|
|
0ee88366ff | ||
|
|
9ae42c9e92 | ||
|
|
62b8df4a8d | ||
|
|
73ec7f3018 | ||
|
|
2d24a75ca6 | ||
|
|
3f92c42ad6 | ||
|
|
d824f79d5a | ||
|
|
eefbf3a52d | ||
|
|
dda793018b | ||
|
|
9e588068ea | ||
|
|
76a7a51bdc | ||
|
|
5f5b5b0340 | ||
|
|
a0ce373dc9 | ||
|
|
049ffb268b | ||
|
|
94483c8b36 | ||
|
|
078f9de44e | ||
|
|
0ae41f97a4 | ||
|
|
9fb6a1ec4c | ||
|
|
21be46c8bb | ||
|
|
91942fc4d2 | ||
|
|
8e02f2289f | ||
|
|
4205d643a1 | ||
|
|
b2dda4dbb9 | ||
|
|
f36eac3d0a | ||
|
|
a2c56425f4 | ||
|
|
c759bfda9c | ||
|
|
57995001ac | ||
|
|
e5553d8ec2 | ||
|
|
dcee5b16c7 | ||
|
|
b190f2e118 | ||
|
|
2389d3e936 | ||
|
|
0f4ed1965a | ||
|
|
e1e38ee536 | ||
|
|
dc1ff00610 | ||
|
|
5f31736603 | ||
|
|
9a57cc72dd | ||
|
|
9d9c5f3753 | ||
|
|
deeefc0c93 | ||
|
|
5c23b08bc1 | ||
|
|
7c4e25ccce | ||
|
|
1bfa310945 | ||
|
|
c96062266b | ||
|
|
5bea99c1d9 | ||
|
|
358be773a2 | ||
|
|
9ebf4e531d |
20
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
### Related Issues
|
||||
|
||||
> Fixes #1
|
||||
> Related to #2
|
||||
|
||||
### 📋 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
|
||||
@@ -1,7 +1,7 @@
|
||||
version = 1
|
||||
|
||||
[merge]
|
||||
automerge_label = "automerge"
|
||||
automerge_label = ["semver-major","semver-minor","semver-patch"]
|
||||
blacklist_title_regex = "^WIP.*"
|
||||
blacklist_labels = ["work in progress"]
|
||||
method = "squash"
|
||||
|
||||
7
examples/svelte/.gitignore
vendored
@@ -1,5 +1,8 @@
|
||||
/node_modules/
|
||||
/public/build/
|
||||
.DS_Store
|
||||
node_modules
|
||||
public/bundle.*
|
||||
.env
|
||||
.env.local
|
||||
.env.build
|
||||
|
||||
.vercel
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
yarn.lock
|
||||
README.md
|
||||
@@ -1,24 +1,21 @@
|
||||
{
|
||||
"name": "svelte-app",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
"dev": "rollup -c -w",
|
||||
"start": "sirv public"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^13.0.0",
|
||||
"@rollup/plugin-node-resolve": "^8.1.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"rollup": "^2.18.0",
|
||||
"rollup-plugin-livereload": "^1.0.0",
|
||||
"rollup-plugin-svelte": "^5.0.3",
|
||||
"rollup-plugin-terser": "^6.1.0",
|
||||
"@rollup/plugin-commonjs": "^14.0.0",
|
||||
"@rollup/plugin-node-resolve": "^8.0.0",
|
||||
"rollup": "^2.3.4",
|
||||
"rollup-plugin-livereload": "^2.0.0",
|
||||
"rollup-plugin-svelte": "^6.0.0",
|
||||
"rollup-plugin-terser": "^7.0.0",
|
||||
"svelte": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"sirv-cli": "^1.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
"autobuild": "rollup -c -w",
|
||||
"dev": "run-p start:dev autobuild",
|
||||
"start": "sirv public --single",
|
||||
"start:dev": "sirv public --single --dev"
|
||||
"sirv-cli": "^1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta name='viewport' content='width=device-width,initial-scale=1'>
|
||||
|
||||
<title>Svelte + Node.js API</title>
|
||||
<title>Svelte + Node.js API</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="/favicon.png" />
|
||||
<link rel="stylesheet" href="/global.css" />
|
||||
<link rel="stylesheet" href="/bundle.css" />
|
||||
</head>
|
||||
<link rel='icon' type='image/png' href='/favicon.png'>
|
||||
<link rel='stylesheet' href='/global.css'>
|
||||
<link rel='stylesheet' href='/build/bundle.css'>
|
||||
|
||||
<body>
|
||||
<script src="/bundle.js"></script>
|
||||
</body>
|
||||
<script defer src='/build/bundle.js'></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -6,42 +6,70 @@ import { terser } from 'rollup-plugin-terser';
|
||||
|
||||
const production = !process.env.ROLLUP_WATCH;
|
||||
|
||||
function serve() {
|
||||
let server;
|
||||
|
||||
function toExit() {
|
||||
if (server) server.kill(0);
|
||||
}
|
||||
|
||||
return {
|
||||
writeBundle() {
|
||||
if (server) return;
|
||||
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
|
||||
stdio: ['ignore', 'inherit', 'inherit'],
|
||||
shell: true
|
||||
});
|
||||
|
||||
process.on('SIGTERM', toExit);
|
||||
process.on('exit', toExit);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
input: 'src/main.js',
|
||||
output: {
|
||||
sourcemap: true,
|
||||
format: 'iife',
|
||||
name: 'app',
|
||||
file: 'public/bundle.js',
|
||||
},
|
||||
plugins: [
|
||||
svelte({
|
||||
// enable run-time checks when not in production
|
||||
dev: !production,
|
||||
// we'll extract any component CSS out into
|
||||
// a separate file — better for performance
|
||||
css: css => {
|
||||
css.write('public/bundle.css');
|
||||
},
|
||||
}),
|
||||
input: 'src/main.js',
|
||||
output: {
|
||||
sourcemap: true,
|
||||
format: 'iife',
|
||||
name: 'app',
|
||||
file: 'public/build/bundle.js'
|
||||
},
|
||||
plugins: [
|
||||
svelte({
|
||||
// enable run-time checks when not in production
|
||||
dev: !production,
|
||||
// we'll extract any component CSS out into
|
||||
// a separate file - better for performance
|
||||
css: css => {
|
||||
css.write('bundle.css');
|
||||
}
|
||||
}),
|
||||
|
||||
// If you have external dependencies installed from
|
||||
// npm, you'll most likely need these plugins. In
|
||||
// some cases you'll need additional configuration —
|
||||
// consult the documentation for details:
|
||||
// https://github.com/rollup/rollup-plugin-commonjs
|
||||
resolve({ browser: true }),
|
||||
commonjs(),
|
||||
// If you have external dependencies installed from
|
||||
// npm, you'll most likely need these plugins. In
|
||||
// some cases you'll need additional configuration -
|
||||
// consult the documentation for details:
|
||||
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
||||
resolve({
|
||||
browser: true,
|
||||
dedupe: ['svelte']
|
||||
}),
|
||||
commonjs(),
|
||||
|
||||
// Watch the `public` directory and refresh the
|
||||
// browser on changes when not in production
|
||||
!production && livereload('public'),
|
||||
// In dev mode, call `npm run start` once
|
||||
// the bundle has been generated
|
||||
!production && serve(),
|
||||
|
||||
// If we're building for production (npm run build
|
||||
// instead of npm run dev), minify
|
||||
production && terser(),
|
||||
],
|
||||
watch: {
|
||||
clearScreen: false,
|
||||
},
|
||||
// Watch the `public` directory and refresh the
|
||||
// browser on changes when not in production
|
||||
!production && livereload('public'),
|
||||
|
||||
// If we're building for production (npm run build
|
||||
// instead of npm run dev), minify
|
||||
production && terser()
|
||||
],
|
||||
watch: {
|
||||
clearScreen: false
|
||||
}
|
||||
};
|
||||
|
||||
128
examples/svelte/scripts/setupTypeScript.js
Normal file
@@ -0,0 +1,128 @@
|
||||
// @ts-check
|
||||
|
||||
/** This script modifies the project to support TS code in .svelte files like:
|
||||
|
||||
<script lang="ts">
|
||||
export let name: string;
|
||||
</script>
|
||||
|
||||
As well as validating the code for CI.
|
||||
*/
|
||||
|
||||
/** To work on this script:
|
||||
rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template
|
||||
*/
|
||||
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
const { argv } = require("process")
|
||||
|
||||
const projectRoot = argv[2] || path.join(__dirname, "..")
|
||||
|
||||
// Add deps to pkg.json
|
||||
const packageJSON = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8"))
|
||||
packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, {
|
||||
"svelte-check": "^1.0.0",
|
||||
"svelte-preprocess": "^4.0.0",
|
||||
"@rollup/plugin-typescript": "^6.0.0",
|
||||
"typescript": "^3.9.3",
|
||||
"tslib": "^2.0.0",
|
||||
"@tsconfig/svelte": "^1.0.0"
|
||||
})
|
||||
|
||||
// Add script for checking
|
||||
packageJSON.scripts = Object.assign(packageJSON.scripts, {
|
||||
"validate": "svelte-check"
|
||||
})
|
||||
|
||||
// Write the package JSON
|
||||
fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(packageJSON, null, " "))
|
||||
|
||||
// mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too
|
||||
const beforeMainJSPath = path.join(projectRoot, "src", "main.js")
|
||||
const afterMainTSPath = path.join(projectRoot, "src", "main.ts")
|
||||
fs.renameSync(beforeMainJSPath, afterMainTSPath)
|
||||
|
||||
// Switch the app.svelte file to use TS
|
||||
const appSveltePath = path.join(projectRoot, "src", "App.svelte")
|
||||
let appFile = fs.readFileSync(appSveltePath, "utf8")
|
||||
appFile = appFile.replace("<script>", '<script lang="ts">')
|
||||
appFile = appFile.replace("export let name;", 'export let name: string;')
|
||||
fs.writeFileSync(appSveltePath, appFile)
|
||||
|
||||
// Edit rollup config
|
||||
const rollupConfigPath = path.join(projectRoot, "rollup.config.js")
|
||||
let rollupConfig = fs.readFileSync(rollupConfigPath, "utf8")
|
||||
|
||||
// Edit imports
|
||||
rollupConfig = rollupConfig.replace(`'rollup-plugin-terser';`, `'rollup-plugin-terser';
|
||||
import sveltePreprocess from 'svelte-preprocess';
|
||||
import typescript from '@rollup/plugin-typescript';`)
|
||||
|
||||
// Replace name of entry point
|
||||
rollupConfig = rollupConfig.replace(`'src/main.js'`, `'src/main.ts'`)
|
||||
|
||||
// Add preprocess to the svelte config, this is tricky because there's no easy signifier.
|
||||
// Instead we look for `css:` then the next `}` and add the preprocessor to that
|
||||
let foundCSS = false
|
||||
let match
|
||||
|
||||
// https://regex101.com/r/OtNjwo/1
|
||||
const configEditor = new RegExp(/css:.|\n*}/gmi)
|
||||
while (( match = configEditor.exec(rollupConfig)) != null) {
|
||||
if (foundCSS) {
|
||||
const endOfCSSIndex = match.index + 1
|
||||
rollupConfig = rollupConfig.slice(0, endOfCSSIndex) + ",\n preprocess: sveltePreprocess()," + rollupConfig.slice(endOfCSSIndex);
|
||||
break
|
||||
}
|
||||
if (match[0].includes("css:")) foundCSS = true
|
||||
}
|
||||
|
||||
|
||||
// Add TypeScript
|
||||
rollupConfig = rollupConfig.replace(
|
||||
'commonjs(),',
|
||||
'commonjs(),\n\t\ttypescript({\n\t\t\tsourceMap: !production,\n\t\t\tinlineSources: !production\n\t\t}),'
|
||||
);
|
||||
fs.writeFileSync(rollupConfigPath, rollupConfig)
|
||||
|
||||
// Add TSConfig
|
||||
const tsconfig = `{
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules/*", "__sapper__/*", "public/*"]
|
||||
}`
|
||||
const tsconfigPath = path.join(projectRoot, "tsconfig.json")
|
||||
fs.writeFileSync(tsconfigPath, tsconfig)
|
||||
|
||||
// Delete this script, but not during testing
|
||||
if (!argv[2]) {
|
||||
// Remove the script
|
||||
fs.unlinkSync(path.join(__filename))
|
||||
|
||||
// Check for Mac's DS_store file, and if it's the only one left remove it
|
||||
const remainingFiles = fs.readdirSync(path.join(__dirname))
|
||||
if (remainingFiles.length === 1 && remainingFiles[0] === '.DS_store') {
|
||||
fs.unlinkSync(path.join(__dirname, '.DS_store'))
|
||||
}
|
||||
|
||||
// Check if the scripts folder is empty
|
||||
if (fs.readdirSync(path.join(__dirname)).length === 0) {
|
||||
// Remove the scripts folder
|
||||
fs.rmdirSync(path.join(__dirname))
|
||||
}
|
||||
}
|
||||
|
||||
// Adds the extension recommendation
|
||||
fs.mkdirSync(path.join(projectRoot, ".vscode"))
|
||||
fs.writeFileSync(path.join(projectRoot, ".vscode", "extensions.json"), `{
|
||||
"recommendations": ["svelte.svelte-vscode"]
|
||||
}
|
||||
`)
|
||||
|
||||
console.log("Converted to TypeScript.")
|
||||
|
||||
if (fs.existsSync(path.join(projectRoot, "node_modules"))) {
|
||||
console.log("\nYou will need to re-run your dependency manager to get started.")
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import App from './App.svelte';
|
||||
|
||||
const app = new App({
|
||||
target: document.body,
|
||||
props: {
|
||||
name: 'world',
|
||||
},
|
||||
target: document.body,
|
||||
props: {
|
||||
name: 'world'
|
||||
}
|
||||
});
|
||||
|
||||
export default app;
|
||||
export default app;
|
||||
635
examples/svelte/yarn.lock
Normal file
@@ -0,0 +1,635 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@babel/code-frame@^7.10.4":
|
||||
version "7.10.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
|
||||
integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.10.4"
|
||||
|
||||
"@babel/helper-validator-identifier@^7.10.4":
|
||||
version "7.10.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
|
||||
integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==
|
||||
|
||||
"@babel/highlight@^7.10.4":
|
||||
version "7.10.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143"
|
||||
integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.10.4"
|
||||
chalk "^2.0.0"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@polka/url@^1.0.0-next.9":
|
||||
version "1.0.0-next.11"
|
||||
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.11.tgz#aeb16f50649a91af79dbe36574b66d0f9e4d9f71"
|
||||
integrity sha512-3NsZsJIA/22P3QUyrEDNA2D133H4j224twJrdipXN38dpnIOzAbUDtOwkcJ5pXmn75w7LSQDjA4tO9dm1XlqlA==
|
||||
|
||||
"@rollup/plugin-commonjs@^14.0.0":
|
||||
version "14.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-14.0.0.tgz#4285f9ec2db686a31129e5a2b415c94aa1f836f0"
|
||||
integrity sha512-+PSmD9ePwTAeU106i9FRdc+Zb3XUWyW26mo5Atr2mk82hor8+nPwkztEjFo8/B1fJKfaQDg9aM2bzQkjhi7zOw==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^3.0.8"
|
||||
commondir "^1.0.1"
|
||||
estree-walker "^1.0.1"
|
||||
glob "^7.1.2"
|
||||
is-reference "^1.1.2"
|
||||
magic-string "^0.25.2"
|
||||
resolve "^1.11.0"
|
||||
|
||||
"@rollup/plugin-node-resolve@^8.0.0":
|
||||
version "8.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.4.0.tgz#261d79a680e9dc3d86761c14462f24126ba83575"
|
||||
integrity sha512-LFqKdRLn0ShtQyf6SBYO69bGE1upV6wUhBX0vFOUnLAyzx5cwp8svA0eHUnu8+YU57XOkrMtfG63QOpQx25pHQ==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^3.1.0"
|
||||
"@types/resolve" "1.17.1"
|
||||
builtin-modules "^3.1.0"
|
||||
deep-freeze "^0.0.1"
|
||||
deepmerge "^4.2.2"
|
||||
is-module "^1.0.0"
|
||||
resolve "^1.17.0"
|
||||
|
||||
"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
|
||||
integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
|
||||
dependencies:
|
||||
"@types/estree" "0.0.39"
|
||||
estree-walker "^1.0.1"
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@types/estree@*":
|
||||
version "0.0.45"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884"
|
||||
integrity sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==
|
||||
|
||||
"@types/estree@0.0.39":
|
||||
version "0.0.39"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
|
||||
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
|
||||
|
||||
"@types/node@*":
|
||||
version "14.14.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.7.tgz#8ea1e8f8eae2430cf440564b98c6dfce1ec5945d"
|
||||
integrity sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg==
|
||||
|
||||
"@types/resolve@1.17.1":
|
||||
version "1.17.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
|
||||
integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
ansi-styles@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
|
||||
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
|
||||
dependencies:
|
||||
color-convert "^1.9.0"
|
||||
|
||||
anymatch@~3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142"
|
||||
integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==
|
||||
dependencies:
|
||||
normalize-path "^3.0.0"
|
||||
picomatch "^2.0.4"
|
||||
|
||||
async-limiter@~1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
|
||||
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
binary-extensions@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
|
||||
integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
braces@~3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
||||
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
|
||||
dependencies:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
||||
|
||||
builtin-modules@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484"
|
||||
integrity sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==
|
||||
|
||||
chalk@^2.0.0:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
dependencies:
|
||||
ansi-styles "^3.2.1"
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chokidar@^3.3.0:
|
||||
version "3.4.3"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b"
|
||||
integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==
|
||||
dependencies:
|
||||
anymatch "~3.1.1"
|
||||
braces "~3.0.2"
|
||||
glob-parent "~5.1.0"
|
||||
is-binary-path "~2.1.0"
|
||||
is-glob "~4.0.1"
|
||||
normalize-path "~3.0.0"
|
||||
readdirp "~3.5.0"
|
||||
optionalDependencies:
|
||||
fsevents "~2.1.2"
|
||||
|
||||
color-convert@^1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
|
||||
dependencies:
|
||||
color-name "1.1.3"
|
||||
|
||||
color-name@1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
||||
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
|
||||
|
||||
commander@^2.20.0:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||
|
||||
commondir@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
console-clear@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/console-clear/-/console-clear-1.1.1.tgz#995e20cbfbf14dd792b672cde387bd128d674bf7"
|
||||
integrity sha512-pMD+MVR538ipqkG5JXeOEbKWS5um1H4LUUccUQG68qpeqBYbzYy79Gh55jkd2TtPdRfUaLWdv6LPP//5Zt0aPQ==
|
||||
|
||||
deep-freeze@^0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84"
|
||||
integrity sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ=
|
||||
|
||||
deepmerge@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
|
||||
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
|
||||
|
||||
escape-string-regexp@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
||||
|
||||
estree-walker@^0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362"
|
||||
integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==
|
||||
|
||||
estree-walker@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700"
|
||||
integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==
|
||||
|
||||
fill-range@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
||||
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
|
||||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||
|
||||
fsevents@~2.1.2:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e"
|
||||
integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==
|
||||
|
||||
function-bind@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||
|
||||
get-port@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc"
|
||||
integrity sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=
|
||||
|
||||
glob-parent@~5.1.0:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
|
||||
integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
|
||||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
glob@^7.1.2:
|
||||
version "7.1.6"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.4"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
has-flag@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
||||
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
|
||||
|
||||
has-flag@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
|
||||
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
||||
|
||||
has@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
|
||||
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
|
||||
dependencies:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
is-binary-path@~2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
|
||||
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
|
||||
dependencies:
|
||||
binary-extensions "^2.0.0"
|
||||
|
||||
is-core-module@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.1.0.tgz#a4cc031d9b1aca63eecbd18a650e13cb4eeab946"
|
||||
integrity sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-extglob@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
|
||||
|
||||
is-glob@^4.0.1, is-glob@~4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
|
||||
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
|
||||
dependencies:
|
||||
is-extglob "^2.1.1"
|
||||
|
||||
is-module@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
||||
integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
|
||||
|
||||
is-number@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
||||
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
||||
|
||||
is-reference@^1.1.2:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7"
|
||||
integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==
|
||||
dependencies:
|
||||
"@types/estree" "*"
|
||||
|
||||
jest-worker@^26.2.1:
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed"
|
||||
integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
merge-stream "^2.0.0"
|
||||
supports-color "^7.0.0"
|
||||
|
||||
js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||
|
||||
kleur@^3.0.0:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
|
||||
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
|
||||
|
||||
livereload-js@^3.1.0:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-3.3.1.tgz#61f887468086762e61fb2987412cf9d1dda99202"
|
||||
integrity sha512-CBu1gTEfzVhlOK1WASKAAJ9Qx1fHECTq0SUB67sfxwQssopTyvzqTlgl+c0h9pZ6V+Fzd2rc510ppuNusg9teQ==
|
||||
|
||||
livereload@^0.9.1:
|
||||
version "0.9.1"
|
||||
resolved "https://registry.yarnpkg.com/livereload/-/livereload-0.9.1.tgz#65125dabdf2db4fd3f1169e953fe56e3bcc6f477"
|
||||
integrity sha512-9g7sua11kkyZNo2hLRCG3LuZZwqexoyEyecSlV8cAsfAVVCZqLzVir6XDqmH0r+Vzgnd5LrdHDMyjtFnJQLAYw==
|
||||
dependencies:
|
||||
chokidar "^3.3.0"
|
||||
livereload-js "^3.1.0"
|
||||
opts ">= 1.2.0"
|
||||
ws "^6.2.1"
|
||||
|
||||
local-access@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/local-access/-/local-access-1.0.1.tgz#5121258146d64e869046c642ea4f1dd39ff942bb"
|
||||
integrity sha512-ykt2pgN0aqIy6KQC1CqdWTWkmUwNgaOS6dcpHVjyBJONA+Xi7AtSB1vuxC/U/0tjIP3wcRudwQk1YYzUvzk2bA==
|
||||
|
||||
magic-string@^0.25.2:
|
||||
version "0.25.7"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
|
||||
integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==
|
||||
dependencies:
|
||||
sourcemap-codec "^1.4.4"
|
||||
|
||||
merge-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
|
||||
|
||||
mime@^2.3.1:
|
||||
version "2.4.6"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1"
|
||||
integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==
|
||||
|
||||
minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
mri@^1.1.0:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.6.tgz#49952e1044db21dbf90f6cd92bc9c9a777d415a6"
|
||||
integrity sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ==
|
||||
|
||||
normalize-path@^3.0.0, normalize-path@~3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
||||
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
"opts@>= 1.2.0":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/opts/-/opts-2.0.2.tgz#a17e189fbbfee171da559edd8a42423bc5993ce1"
|
||||
integrity sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==
|
||||
|
||||
path-is-absolute@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||
|
||||
path-parse@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
|
||||
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
||||
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
|
||||
|
||||
randombytes@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
||||
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
|
||||
dependencies:
|
||||
safe-buffer "^5.1.0"
|
||||
|
||||
readdirp@~3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e"
|
||||
integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==
|
||||
dependencies:
|
||||
picomatch "^2.2.1"
|
||||
|
||||
require-relative@^0.8.7:
|
||||
version "0.8.7"
|
||||
resolved "https://registry.yarnpkg.com/require-relative/-/require-relative-0.8.7.tgz#7999539fc9e047a37928fa196f8e1563dabd36de"
|
||||
integrity sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=
|
||||
|
||||
resolve@^1.11.0, resolve@^1.17.0:
|
||||
version "1.19.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c"
|
||||
integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==
|
||||
dependencies:
|
||||
is-core-module "^2.1.0"
|
||||
path-parse "^1.0.6"
|
||||
|
||||
rollup-plugin-livereload@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.0.tgz#d3928d74e8cf2ae4286c5dd46b770fd3f3b82313"
|
||||
integrity sha512-oC/8NqumGYuphkqrfszOHUUIwzKsaHBICw6QRwT5uD07gvePTS+HW+GFwu6f9K8W02CUuTvtIM9AWJrbj4wE1A==
|
||||
dependencies:
|
||||
livereload "^0.9.1"
|
||||
|
||||
rollup-plugin-svelte@^6.0.0:
|
||||
version "6.1.1"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-svelte/-/rollup-plugin-svelte-6.1.1.tgz#66362cf0500fb7a848283ebcf19d289a60ef0871"
|
||||
integrity sha512-ijnm0pH1ScrY4uxwaNXBpNVejVzpL2769hIEbAlnqNUWZrffLspu5/k9/l/Wsj3NrEHLQ6wCKGagVJonyfN7ow==
|
||||
dependencies:
|
||||
require-relative "^0.8.7"
|
||||
rollup-pluginutils "^2.8.2"
|
||||
sourcemap-codec "^1.4.8"
|
||||
|
||||
rollup-plugin-terser@^7.0.0:
|
||||
version "7.0.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d"
|
||||
integrity sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.10.4"
|
||||
jest-worker "^26.2.1"
|
||||
serialize-javascript "^4.0.0"
|
||||
terser "^5.0.0"
|
||||
|
||||
rollup-pluginutils@^2.8.2:
|
||||
version "2.8.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e"
|
||||
integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==
|
||||
dependencies:
|
||||
estree-walker "^0.6.1"
|
||||
|
||||
rollup@^2.3.4:
|
||||
version "2.33.1"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.33.1.tgz#802795164164ee63cd47769d8879c33ec8ae0c40"
|
||||
integrity sha512-uY4O/IoL9oNW8MMcbA5hcOaz6tZTMIh7qJHx/tzIJm+n1wLoY38BLn6fuy7DhR57oNFLMbDQtDeJoFURt5933w==
|
||||
optionalDependencies:
|
||||
fsevents "~2.1.2"
|
||||
|
||||
sade@^1.6.0:
|
||||
version "1.7.4"
|
||||
resolved "https://registry.yarnpkg.com/sade/-/sade-1.7.4.tgz#ea681e0c65d248d2095c90578c03ca0bb1b54691"
|
||||
integrity sha512-y5yauMD93rX840MwUJr7C1ysLFBgMspsdTo4UVrDg3fXDvtwOyIqykhVAAm6fk/3au77773itJStObgK+LKaiA==
|
||||
dependencies:
|
||||
mri "^1.1.0"
|
||||
|
||||
safe-buffer@^5.1.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
semiver@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/semiver/-/semiver-1.1.0.tgz#9c97fb02c21c7ce4fcf1b73e2c7a24324bdddd5f"
|
||||
integrity sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==
|
||||
|
||||
serialize-javascript@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa"
|
||||
integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==
|
||||
dependencies:
|
||||
randombytes "^2.1.0"
|
||||
|
||||
sirv-cli@^1.0.0:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/sirv-cli/-/sirv-cli-1.0.8.tgz#150c3f62694203a86cf5d71ec60e6ff4c34064a9"
|
||||
integrity sha512-bJI+kkzQvMKfAOfgLzv09kWsdymLm39LgKmGjacB19GHIAQLCvXXg8e8HzcofTjDZlA8zVv4dQjU9SWkNzkJhw==
|
||||
dependencies:
|
||||
console-clear "^1.1.0"
|
||||
get-port "^3.2.0"
|
||||
kleur "^3.0.0"
|
||||
local-access "^1.0.1"
|
||||
sade "^1.6.0"
|
||||
semiver "^1.0.0"
|
||||
sirv "^1.0.7"
|
||||
tinydate "^1.0.0"
|
||||
|
||||
sirv@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.7.tgz#ad8ca1f84430777a59162592626c2b8e9b9f1384"
|
||||
integrity sha512-QMT2OTD3CTr8de9VByPmvSEeyt6k8/Cxg0J2kQJ5HNhIWfhFg9ypcIWWzez9rPWnGj+WtJ7AZD/MdT/vdilV/A==
|
||||
dependencies:
|
||||
"@polka/url" "^1.0.0-next.9"
|
||||
mime "^2.3.1"
|
||||
totalist "^1.0.0"
|
||||
|
||||
source-map-support@~0.5.19:
|
||||
version "0.5.19"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
|
||||
integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
|
||||
dependencies:
|
||||
buffer-from "^1.0.0"
|
||||
source-map "^0.6.0"
|
||||
|
||||
source-map@^0.6.0:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
source-map@~0.7.2:
|
||||
version "0.7.3"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
||||
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
||||
|
||||
sourcemap-codec@^1.4.4, sourcemap-codec@^1.4.8:
|
||||
version "1.4.8"
|
||||
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
|
||||
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
|
||||
|
||||
supports-color@^5.3.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
|
||||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
supports-color@^7.0.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
|
||||
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
|
||||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
svelte@^3.0.0:
|
||||
version "3.29.7"
|
||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.29.7.tgz#e254eb2d0d609ce0fd60f052d444ac4a66d90f7d"
|
||||
integrity sha512-rx0g311kBODvEWUU01DFBUl3MJuJven04bvTVFUG/w0On/wuj0PajQY/QlXcJndFxG+W1s8iXKaB418tdHWc3A==
|
||||
|
||||
terser@^5.0.0:
|
||||
version "5.3.8"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.8.tgz#991ae8ba21a3d990579b54aa9af11586197a75dd"
|
||||
integrity sha512-zVotuHoIfnYjtlurOouTazciEfL7V38QMAOhGqpXDEg6yT13cF4+fEP9b0rrCEQTn+tT46uxgFsTZzhygk+CzQ==
|
||||
dependencies:
|
||||
commander "^2.20.0"
|
||||
source-map "~0.7.2"
|
||||
source-map-support "~0.5.19"
|
||||
|
||||
tinydate@^1.0.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/tinydate/-/tinydate-1.3.0.tgz#e6ca8e5a22b51bb4ea1c3a2a4fd1352dbd4c57fb"
|
||||
integrity sha512-7cR8rLy2QhYHpsBDBVYnnWXm8uRTr38RoZakFSW7Bs7PzfMPNZthuMLkwqZv7MTu8lhQ91cOFYS5a7iFj2oR3w==
|
||||
|
||||
to-regex-range@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
||||
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
|
||||
dependencies:
|
||||
is-number "^7.0.0"
|
||||
|
||||
totalist@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df"
|
||||
integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
ws@^6.2.1:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
|
||||
integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
|
||||
dependencies:
|
||||
async-limiter "~1.0.0"
|
||||
@@ -17,6 +17,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `blitz build`"
|
||||
},
|
||||
@@ -47,6 +50,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `next build`"
|
||||
},
|
||||
@@ -82,6 +88,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `gatsby build`"
|
||||
},
|
||||
@@ -111,6 +120,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `hexo generate`"
|
||||
},
|
||||
@@ -140,6 +152,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `npx @11ty/eleventy`"
|
||||
},
|
||||
@@ -168,6 +183,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `docusaurus build`"
|
||||
},
|
||||
@@ -196,6 +214,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `docusaurus-build`"
|
||||
},
|
||||
@@ -224,6 +245,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `preact build`"
|
||||
},
|
||||
@@ -255,6 +279,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `dojo build`"
|
||||
},
|
||||
@@ -283,6 +310,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `ember build`"
|
||||
},
|
||||
@@ -311,6 +341,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `vue-cli-service build`"
|
||||
},
|
||||
@@ -339,6 +372,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `ng build && scully`"
|
||||
},
|
||||
@@ -367,6 +403,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `ng build`"
|
||||
},
|
||||
@@ -395,6 +434,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `ng build`"
|
||||
},
|
||||
@@ -423,6 +465,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `polymer build`"
|
||||
},
|
||||
@@ -451,6 +496,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `rollup -c`"
|
||||
},
|
||||
@@ -479,6 +527,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `react-scripts build`"
|
||||
},
|
||||
@@ -511,6 +562,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `react-scripts build`"
|
||||
},
|
||||
@@ -539,6 +593,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `gridsome build`"
|
||||
},
|
||||
@@ -567,6 +624,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `umi build`"
|
||||
},
|
||||
@@ -595,6 +655,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `sapper export`"
|
||||
},
|
||||
@@ -623,6 +686,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `saber build`"
|
||||
},
|
||||
@@ -651,6 +717,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `stencil build`"
|
||||
},
|
||||
@@ -679,6 +748,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `nuxt generate`"
|
||||
},
|
||||
@@ -709,6 +781,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"value": "yarn rw build && yarn rw db up --no-db-client --auto-approve && yarn rw dataMigrate up"
|
||||
},
|
||||
@@ -743,6 +818,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "None"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `hugo -D --gc`"
|
||||
},
|
||||
@@ -770,6 +848,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"value": "bundle install"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `jekyll build`"
|
||||
},
|
||||
@@ -797,6 +878,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `brunch build --production`"
|
||||
},
|
||||
@@ -824,6 +908,9 @@
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"value": "bundle install"
|
||||
},
|
||||
"buildCommand": {
|
||||
"value": "`npm run build` or `bundle exec middleman build`"
|
||||
},
|
||||
@@ -841,6 +928,9 @@
|
||||
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/other.svg",
|
||||
"description": "No framework or a unoptimized framework.",
|
||||
"settings": {
|
||||
"installCommand": {
|
||||
"placeholder": "`yarn install` or `npm install`"
|
||||
},
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run vercel-build` or `npm run build`"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/frameworks",
|
||||
"version": "0.1.2-canary.0",
|
||||
"version": "0.2.0",
|
||||
"main": "frameworks.json",
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
|
||||
@@ -89,9 +89,15 @@ const Schema = {
|
||||
},
|
||||
settings: {
|
||||
type: 'object',
|
||||
required: ['buildCommand', 'devCommand', 'outputDirectory'],
|
||||
required: [
|
||||
'installCommand',
|
||||
'buildCommand',
|
||||
'devCommand',
|
||||
'outputDirectory',
|
||||
],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
installCommand: SchemaSettings,
|
||||
buildCommand: SchemaSettings,
|
||||
devCommand: SchemaSettings,
|
||||
outputDirectory: SchemaSettings,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "2.5.5-canary.0",
|
||||
"version": "2.6.1-canary.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
@@ -29,7 +29,7 @@
|
||||
"@types/node-fetch": "^2.1.6",
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/yazl": "^2.4.1",
|
||||
"@vercel/frameworks": "0.1.2-canary.0",
|
||||
"@vercel/frameworks": "0.2.0",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"aggregate-error": "3.0.1",
|
||||
"async-retry": "1.2.3",
|
||||
|
||||
@@ -24,6 +24,7 @@ interface Options {
|
||||
projectSettings?: {
|
||||
framework?: string | null;
|
||||
devCommand?: string | null;
|
||||
installCommand?: string | null;
|
||||
buildCommand?: string | null;
|
||||
outputDirectory?: string | null;
|
||||
createdAt?: number;
|
||||
@@ -450,6 +451,10 @@ function detectFrontBuilder(
|
||||
config.devCommand = projectSettings.devCommand;
|
||||
}
|
||||
|
||||
if (typeof projectSettings.installCommand === 'string') {
|
||||
config.installCommand = projectSettings.installCommand;
|
||||
}
|
||||
|
||||
if (projectSettings.buildCommand) {
|
||||
config.buildCommand = projectSettings.buildCommand;
|
||||
}
|
||||
@@ -984,7 +989,6 @@ function getRouteResult(
|
||||
rewriteRoutes.push({
|
||||
src: '^/api(/.*)?$',
|
||||
status: 404,
|
||||
continue: true,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -41,6 +41,7 @@ export interface Config {
|
||||
import?: { [key: string]: string };
|
||||
functions?: BuilderFunctions;
|
||||
outputDirectory?: string;
|
||||
installCommand?: string;
|
||||
buildCommand?: string;
|
||||
devCommand?: string;
|
||||
framework?: string;
|
||||
|
||||
@@ -833,6 +833,50 @@ describe('Test `detectBuilders`', () => {
|
||||
describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
const featHandleMiss = true;
|
||||
|
||||
it('should select "installCommand"', async () => {
|
||||
const pkg = {
|
||||
scripts: { build: 'next build' },
|
||||
dependencies: { next: '9.0.0' },
|
||||
};
|
||||
const files = ['package.json', 'pages/index.js', 'public/index.html'];
|
||||
const { builders, errors } = await detectBuilders(files, pkg, {
|
||||
featHandleMiss,
|
||||
projectSettings: {
|
||||
installCommand: 'npx pnpm install',
|
||||
},
|
||||
});
|
||||
expect(errors).toBe(null);
|
||||
expect(builders).toBeDefined();
|
||||
expect(builders!.length).toStrictEqual(1);
|
||||
expect(builders![0].src).toStrictEqual('package.json');
|
||||
expect(builders![0].use).toStrictEqual('@vercel/next');
|
||||
expect(builders![0].config!.zeroConfig).toStrictEqual(true);
|
||||
expect(builders![0].config!.installCommand).toStrictEqual(
|
||||
'npx pnpm install'
|
||||
);
|
||||
});
|
||||
|
||||
it('should select empty "installCommand"', async () => {
|
||||
const pkg = {
|
||||
scripts: { build: 'next build' },
|
||||
dependencies: { next: '9.0.0' },
|
||||
};
|
||||
const files = ['package.json', 'pages/index.js', 'public/index.html'];
|
||||
const { builders, errors } = await detectBuilders(files, pkg, {
|
||||
featHandleMiss,
|
||||
projectSettings: {
|
||||
installCommand: '',
|
||||
},
|
||||
});
|
||||
expect(errors).toBe(null);
|
||||
expect(builders).toBeDefined();
|
||||
expect(builders!.length).toStrictEqual(1);
|
||||
expect(builders![0].src).toStrictEqual('package.json');
|
||||
expect(builders![0].use).toStrictEqual('@vercel/next');
|
||||
expect(builders![0].config!.zeroConfig).toStrictEqual(true);
|
||||
expect(builders![0].config!.installCommand).toStrictEqual('');
|
||||
});
|
||||
|
||||
it('should never select now.json src', async () => {
|
||||
const files = ['docs/index.md', 'mkdocs.yml', 'now.json'];
|
||||
const { builders, errors } = await detectBuilders(files, null, {
|
||||
@@ -2349,7 +2393,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
expect(errorRoutes).toStrictEqual([
|
||||
@@ -2451,7 +2494,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -2489,7 +2531,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -2527,7 +2568,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -2560,7 +2600,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -2588,7 +2627,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -2619,7 +2657,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -2646,7 +2683,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -2681,7 +2717,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
expect(errorRoutes).toStrictEqual([
|
||||
@@ -2776,7 +2811,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -2809,7 +2843,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -2843,7 +2876,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -2869,7 +2901,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -2893,7 +2924,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -2918,7 +2948,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -2939,7 +2968,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -2974,7 +3002,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -3032,7 +3059,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -3065,7 +3091,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -3099,7 +3124,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -3118,7 +3142,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -3142,7 +3165,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -3167,7 +3189,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -3188,7 +3209,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "20.1.3-canary.2",
|
||||
"version": "21.0.2-canary.1",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -13,7 +13,7 @@
|
||||
"scripts": {
|
||||
"preinstall": "node ./scripts/preinstall.js",
|
||||
"test-unit": "nyc ava test/unit.js test/dev-builder.unit.js test/dev-router.unit.js test/dev-server.unit.js test/dev-validate.unit.js --serial --fail-fast --verbose",
|
||||
"test-integration-cli": "ava test/integration.js --serial --fail-fast --verbose",
|
||||
"test-integration-cli": "rimraf test/fixtures/integration && ava test/integration.js --serial --fail-fast --verbose",
|
||||
"test-integration-dev": "ava test/dev/integration.js --serial --fail-fast --verbose",
|
||||
"prepublishOnly": "yarn build",
|
||||
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
|
||||
@@ -61,9 +61,9 @@
|
||||
"node": ">= 10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.5.5-canary.0",
|
||||
"@vercel/build-utils": "2.6.1-canary.0",
|
||||
"@vercel/go": "1.1.6",
|
||||
"@vercel/node": "1.8.4",
|
||||
"@vercel/node": "1.8.6-canary.0",
|
||||
"@vercel/python": "1.2.3",
|
||||
"@vercel/ruby": "1.2.4",
|
||||
"update-notifier": "4.1.0"
|
||||
@@ -100,7 +100,7 @@
|
||||
"@types/universal-analytics": "0.4.2",
|
||||
"@types/which": "1.3.2",
|
||||
"@types/write-json-file": "2.2.1",
|
||||
"@vercel/frameworks": "0.1.2-canary.0",
|
||||
"@vercel/frameworks": "0.2.0",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@zeit/fun": "0.11.2",
|
||||
"@zeit/source-map-support": "0.6.2",
|
||||
@@ -146,7 +146,6 @@
|
||||
"minimatch": "3.0.4",
|
||||
"mri": "1.1.5",
|
||||
"ms": "2.1.2",
|
||||
"nanoid": "3.0.2",
|
||||
"node-fetch": "2.6.1",
|
||||
"npm-package-arg": "6.1.0",
|
||||
"nyc": "13.2.0",
|
||||
@@ -158,6 +157,7 @@
|
||||
"psl": "1.1.31",
|
||||
"qr-image": "3.2.0",
|
||||
"raw-body": "2.4.1",
|
||||
"rimraf": "3.0.2",
|
||||
"semver": "5.5.0",
|
||||
"serve-handler": "6.1.1",
|
||||
"sinon": "4.4.2",
|
||||
|
||||
@@ -3,15 +3,15 @@ import { resolve, join } from 'path';
|
||||
import DevServer from '../../util/dev/server';
|
||||
import parseListen from '../../util/dev/parse-listen';
|
||||
import { Output } from '../../util/output';
|
||||
import { NowContext } from '../../types';
|
||||
import { NowContext, 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, ProjectEnvTarget } from '../../types';
|
||||
import { ProjectSettings } from '../../types';
|
||||
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
|
||||
import { Env } from '@vercel/build-utils';
|
||||
import setupAndLink from '../../util/link/setup-and-link';
|
||||
import getSystemEnvValues from '../../util/env/get-system-env-values';
|
||||
|
||||
type Options = {
|
||||
'--debug'?: boolean;
|
||||
@@ -70,7 +70,8 @@ export default async function dev(
|
||||
let devCommand: string | undefined;
|
||||
let frameworkSlug: string | undefined;
|
||||
let projectSettings: ProjectSettings | undefined;
|
||||
let environmentVars: Env | undefined;
|
||||
let projectEnvs: ProjectEnvVariable[] = [];
|
||||
let systemEnvValues: string[] = [];
|
||||
if (link.status === 'linked') {
|
||||
const { project, org } = link;
|
||||
client.currentTeam = org.type === 'team' ? org.id : undefined;
|
||||
@@ -98,12 +99,12 @@ export default async function dev(
|
||||
cwd = join(cwd, project.rootDirectory);
|
||||
}
|
||||
|
||||
environmentVars = await getDecryptedEnvRecords(
|
||||
output,
|
||||
client,
|
||||
project,
|
||||
ProjectEnvTarget.Development
|
||||
);
|
||||
[{ envs: projectEnvs }, { systemEnvValues }] = await Promise.all([
|
||||
getDecryptedEnvRecords(output, client, project.id),
|
||||
project.autoExposeSystemEnvs
|
||||
? getSystemEnvValues(output, client, project.id)
|
||||
: { systemEnvValues: [] },
|
||||
]);
|
||||
}
|
||||
|
||||
const devServer = new DevServer(cwd, {
|
||||
@@ -112,7 +113,8 @@ export default async function dev(
|
||||
devCommand,
|
||||
frameworkSlug,
|
||||
projectSettings,
|
||||
environmentVars,
|
||||
projectEnvs,
|
||||
systemEnvValues,
|
||||
});
|
||||
|
||||
process.once('SIGINT', () => devServer.stop());
|
||||
|
||||
131
packages/now-cli/src/commands/env/add.ts
vendored
@@ -1,6 +1,6 @@
|
||||
import chalk from 'chalk';
|
||||
import inquirer from 'inquirer';
|
||||
import { ProjectEnvTarget, Project } from '../../types';
|
||||
import { ProjectEnvTarget, Project, Secret, ProjectEnvType } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import Client from '../../util/client';
|
||||
import stamp from '../../util/output/stamp';
|
||||
@@ -11,12 +11,14 @@ import {
|
||||
getEnvTargetPlaceholder,
|
||||
getEnvTargetChoices,
|
||||
} from '../../util/env/env-target';
|
||||
import { isValidEnvType, getEnvTypePlaceholder } from '../../util/env/env-type';
|
||||
import readStandardInput from '../../util/input/read-standard-input';
|
||||
import param from '../../util/output/param';
|
||||
import withSpinner from '../../util/with-spinner';
|
||||
import { emoji, prependEmoji } from '../../util/emoji';
|
||||
import { isKnownError } from '../../util/env/known-error';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import getSystemEnvValues from '../../util/env/get-system-env-values';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -29,38 +31,74 @@ export default async function add(
|
||||
args: string[],
|
||||
output: Output
|
||||
) {
|
||||
const stdInput = await readStandardInput();
|
||||
let [envName, envTarget] = args;
|
||||
// improve the way we show inquirer prompts
|
||||
require('../../util/input/patch-inquirer');
|
||||
|
||||
if (args.length > 2) {
|
||||
const stdInput = await readStandardInput();
|
||||
let [envTypeArg, envName, envTargetArg] = args;
|
||||
|
||||
if (args.length > 3) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${getCommandName(
|
||||
`env add <name> ${getEnvTargetPlaceholder()}`
|
||||
`env add ${getEnvTypePlaceholder()} <name> ${getEnvTargetPlaceholder()}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (stdInput && (!envName || !envTarget)) {
|
||||
if (stdInput && (!envTypeArg || !envName || !envTargetArg)) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${getCommandName(
|
||||
`env add <name> <target> < <file>`
|
||||
`env add ${getEnvTypePlaceholder()} <name> <target> < <file>`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
let envTargets: ProjectEnvTarget[] = [];
|
||||
if (envTarget) {
|
||||
if (!isValidEnvTarget(envTarget)) {
|
||||
if (envTargetArg) {
|
||||
if (!isValidEnvTarget(envTargetArg)) {
|
||||
output.error(
|
||||
`The Environment ${param(
|
||||
envTarget
|
||||
envTargetArg
|
||||
)} is invalid. It must be one of: ${getEnvTargetPlaceholder()}.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
envTargets.push(envTarget);
|
||||
envTargets.push(envTargetArg);
|
||||
}
|
||||
|
||||
let envType: ProjectEnvType;
|
||||
if (envTypeArg) {
|
||||
if (!isValidEnvType(envTypeArg)) {
|
||||
output.error(
|
||||
`The Environment Variable type ${param(
|
||||
envTypeArg
|
||||
)} is invalid. It must be one of: ${getEnvTypePlaceholder()}.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
envType = envTypeArg;
|
||||
} else {
|
||||
const answers = (await inquirer.prompt({
|
||||
name: 'inputEnvType',
|
||||
type: 'list',
|
||||
message: `Which type of Environment Variable do you want to add?`,
|
||||
choices: [
|
||||
{ name: 'Plaintext', value: ProjectEnvType.Plaintext },
|
||||
{
|
||||
name: `Secret (can be created using ${getCommandName('secret add')})`,
|
||||
value: ProjectEnvType.Secret,
|
||||
},
|
||||
{
|
||||
name: 'Reference to System Environment Variable',
|
||||
value: ProjectEnvType.System,
|
||||
},
|
||||
],
|
||||
})) as { inputEnvType: ProjectEnvType };
|
||||
|
||||
envType = answers.inputEnvType;
|
||||
}
|
||||
|
||||
while (!envName) {
|
||||
@@ -77,7 +115,10 @@ export default async function add(
|
||||
}
|
||||
}
|
||||
|
||||
const { envs } = await getEnvVariables(output, client, project.id);
|
||||
const [{ envs }, { systemEnvValues }] = await Promise.all([
|
||||
getEnvVariables(output, client, project.id),
|
||||
getSystemEnvValues(output, client, project.id),
|
||||
]);
|
||||
const existing = new Set(
|
||||
envs.filter(r => r.key === envName).map(r => r.target)
|
||||
);
|
||||
@@ -98,15 +139,59 @@ export default async function add(
|
||||
|
||||
if (stdInput) {
|
||||
envValue = stdInput;
|
||||
} else if (isSystemEnvVariable(envName)) {
|
||||
envValue = '';
|
||||
} else {
|
||||
} else if (envType === ProjectEnvType.Plaintext) {
|
||||
const { inputValue } = await inquirer.prompt({
|
||||
type: 'password',
|
||||
type: 'input',
|
||||
name: 'inputValue',
|
||||
message: `What’s the value of ${envName}?`,
|
||||
});
|
||||
|
||||
envValue = inputValue || '';
|
||||
} else if (envType === ProjectEnvType.Secret) {
|
||||
let secretId: string | null = null;
|
||||
|
||||
while (!secretId) {
|
||||
let { secretName } = await inquirer.prompt({
|
||||
type: 'input',
|
||||
name: 'secretName',
|
||||
message: `What’s the value of ${envName}?`,
|
||||
});
|
||||
|
||||
secretName = secretName || '';
|
||||
|
||||
if (secretName[0] === '@') {
|
||||
secretName = secretName.slice(1);
|
||||
}
|
||||
|
||||
try {
|
||||
const secret = await client.fetch<Secret>(
|
||||
`/v2/now/secrets/${encodeURIComponent(secretName)}`
|
||||
);
|
||||
|
||||
secretId = secret.uid;
|
||||
} catch (error) {
|
||||
if (error.status === 404) {
|
||||
output.error(
|
||||
`Please enter the name of an existing Secret (can be created with ${getCommandName(
|
||||
'secret add'
|
||||
)}).`
|
||||
);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
envValue = secretId;
|
||||
} else {
|
||||
const { systemEnvValue } = await inquirer.prompt({
|
||||
name: 'systemEnvValue',
|
||||
type: 'list',
|
||||
message: `What’s the value of ${envName}?`,
|
||||
choices: systemEnvValues.map(value => ({ name: value, value })),
|
||||
});
|
||||
|
||||
envValue = systemEnvValue;
|
||||
}
|
||||
|
||||
while (envTargets.length === 0) {
|
||||
@@ -127,7 +212,15 @@ export default async function add(
|
||||
const addStamp = stamp();
|
||||
try {
|
||||
await withSpinner('Saving', () =>
|
||||
addEnvRecord(output, client, project.id, envName, envValue, envTargets)
|
||||
addEnvRecord(
|
||||
output,
|
||||
client,
|
||||
project.id,
|
||||
envType,
|
||||
envName,
|
||||
envValue,
|
||||
envTargets
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
if (isKnownError(error) && error.serverMessage) {
|
||||
@@ -148,7 +241,3 @@ export default async function add(
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function isSystemEnvVariable(envName: string) {
|
||||
return envName.startsWith('VERCEL_');
|
||||
}
|
||||
|
||||
34
packages/now-cli/src/commands/env/index.ts
vendored
@@ -6,6 +6,7 @@ import getArgs from '../../util/get-args';
|
||||
import getSubcommand from '../../util/get-subcommand';
|
||||
import getInvalidSubcommand from '../../util/get-invalid-subcommand';
|
||||
import { getEnvTargetPlaceholder } from '../../util/env/env-target';
|
||||
import { getEnvTypePlaceholder } from '../../util/env/env-type';
|
||||
import { getLinkedProject } from '../../util/projects/link';
|
||||
import Client from '../../util/client';
|
||||
import handleError from '../../util/handle-error';
|
||||
@@ -18,16 +19,17 @@ import ls from './ls';
|
||||
import rm from './rm';
|
||||
|
||||
const help = () => {
|
||||
const placeholder = getEnvTargetPlaceholder();
|
||||
const typePlaceholder = getEnvTypePlaceholder();
|
||||
const targetPlaceholder = getEnvTargetPlaceholder();
|
||||
console.log(`
|
||||
${chalk.bold(`${logo} ${getPkgName()} env`)} [options] <command>
|
||||
|
||||
${chalk.dim('Commands:')}
|
||||
|
||||
ls [environment] List all variables for the specified Environment
|
||||
add [name] [environment] Add an Environment Variable (see examples below)
|
||||
rm [name] [environment] Remove an Environment Variable (see examples below)
|
||||
pull [filename] Pull all Development Environment Variables from the cloud and write to a file [.env]
|
||||
ls [environment] List all variables for the specified Environment
|
||||
add [type] [name] [environment] Add an Environment Variable (see examples below)
|
||||
rm [name] [environment] Remove an Environment Variable (see examples below)
|
||||
pull [filename] Pull all Development Environment Variables from the cloud and write to a file [.env]
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
@@ -47,21 +49,27 @@ const help = () => {
|
||||
|
||||
${chalk.gray('–')} Add a new variable to multiple Environments
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} env add <name>`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} env add API_TOKEN`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} env add ${typePlaceholder} <name>`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} env add secret API_TOKEN`)}
|
||||
|
||||
${chalk.gray('–')} Add a new variable for a specific Environment
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} env add <name> ${placeholder}`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} env add DB_CONNECTION production`)}
|
||||
${chalk.cyan(
|
||||
`$ ${getPkgName()} env add ${typePlaceholder} <name> ${targetPlaceholder}`
|
||||
)}
|
||||
${chalk.cyan(`$ ${getPkgName()} env add secret DB_PASS production`)}
|
||||
|
||||
${chalk.gray('–')} Add a new Environment Variable from stdin
|
||||
|
||||
${chalk.cyan(
|
||||
`$ cat <file> | ${getPkgName()} env add <name> ${placeholder}`
|
||||
`$ cat <file> | ${getPkgName()} env add ${typePlaceholder} <name> ${targetPlaceholder}`
|
||||
)}
|
||||
${chalk.cyan(
|
||||
`$ cat ~/.npmrc | ${getPkgName()} env add plain NPM_RC preview`
|
||||
)}
|
||||
${chalk.cyan(
|
||||
`$ ${getPkgName()} env add plain API_URL production < url.txt`
|
||||
)}
|
||||
${chalk.cyan(`$ cat ~/.npmrc | ${getPkgName()} env add NPM_RC preview`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} env add DB_PASS production < secret.txt`)}
|
||||
|
||||
${chalk.gray('–')} Remove an variable from multiple Environments
|
||||
|
||||
@@ -70,7 +78,7 @@ const help = () => {
|
||||
|
||||
${chalk.gray('–')} Remove a variable from a specific Environment
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} env rm <name> ${placeholder}`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} env rm <name> ${targetPlaceholder}`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} env rm NPM_RC preview`)}
|
||||
`);
|
||||
};
|
||||
|
||||
39
packages/now-cli/src/commands/env/ls.ts
vendored
@@ -1,7 +1,12 @@
|
||||
import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import { Output } from '../../util/output';
|
||||
import { ProjectEnvTarget, Project, ProjectEnvVariableV5 } from '../../types';
|
||||
import {
|
||||
ProjectEnvTarget,
|
||||
Project,
|
||||
ProjectEnvVariable,
|
||||
ProjectEnvType,
|
||||
} from '../../types';
|
||||
import Client from '../../util/client';
|
||||
import formatTable from '../../util/format-table';
|
||||
import getEnvVariables from '../../util/env/get-env-records';
|
||||
@@ -13,6 +18,8 @@ import stamp from '../../util/output/stamp';
|
||||
import param from '../../util/output/param';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import ellipsis from '../../util/output/ellipsis';
|
||||
// @ts-ignore
|
||||
import title from 'title';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -47,19 +54,7 @@ export default async function ls(
|
||||
|
||||
const lsStamp = stamp();
|
||||
|
||||
const data = await getEnvVariables(output, client, project.id, envTarget);
|
||||
|
||||
// we expand env vars with multiple targets
|
||||
const envs: ProjectEnvVariableV5[] = [];
|
||||
for (let env of data.envs) {
|
||||
if (Array.isArray(env.target)) {
|
||||
for (let target of env.target) {
|
||||
envs.push({ ...env, target });
|
||||
}
|
||||
} else {
|
||||
envs.push({ ...env, target: env.target });
|
||||
}
|
||||
}
|
||||
const { envs } = await getEnvVariables(output, client, project.id, envTarget);
|
||||
|
||||
output.log(
|
||||
`${
|
||||
@@ -71,9 +66,9 @@ export default async function ls(
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getTable(records: ProjectEnvVariableV5[]) {
|
||||
function getTable(records: ProjectEnvVariable[]) {
|
||||
return formatTable(
|
||||
['name', 'value', 'environment', 'created'],
|
||||
['name', 'value', 'environments', 'created'],
|
||||
['l', 'l', 'l', 'l', 'l'],
|
||||
[
|
||||
{
|
||||
@@ -84,16 +79,16 @@ function getTable(records: ProjectEnvVariableV5[]) {
|
||||
);
|
||||
}
|
||||
|
||||
function getRow(env: ProjectEnvVariableV5) {
|
||||
function getRow(env: ProjectEnvVariable) {
|
||||
let value: string;
|
||||
if (env.type === 'plain') {
|
||||
if (env.type === ProjectEnvType.Plaintext) {
|
||||
// replace space characters (line-break, etc.) with simple spaces
|
||||
// to make sure the displayed value is a single line
|
||||
const singleLineValue = env.value.replace(/\s/g, ' ');
|
||||
|
||||
value = chalk.gray(ellipsis(singleLineValue, 19));
|
||||
} else if (env.type === 'system') {
|
||||
value = chalk.gray.italic('Populated by System');
|
||||
} else if (env.type === ProjectEnvType.System) {
|
||||
value = chalk.gray.italic(env.value);
|
||||
} else {
|
||||
value = chalk.gray.italic('Encrypted');
|
||||
}
|
||||
@@ -102,7 +97,9 @@ function getRow(env: ProjectEnvVariableV5) {
|
||||
return [
|
||||
chalk.bold(env.key),
|
||||
value,
|
||||
env.target || '',
|
||||
(Array.isArray(env.target) ? env.target : [env.target || ''])
|
||||
.map(title)
|
||||
.join(', '),
|
||||
env.createdAt ? `${ms(now - env.createdAt)} ago` : '',
|
||||
];
|
||||
}
|
||||
|
||||
30
packages/now-cli/src/commands/env/pull.ts
vendored
@@ -1,5 +1,5 @@
|
||||
import chalk from 'chalk';
|
||||
import { ProjectEnvTarget, Project } from '../../types';
|
||||
import { Project } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import confirm from '../../util/input/confirm';
|
||||
import Client from '../../util/client';
|
||||
@@ -12,7 +12,8 @@ import { promises, openSync, closeSync, readSync } from 'fs';
|
||||
import { emoji, prependEmoji } from '../../util/emoji';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
const { writeFile } = promises;
|
||||
import { Env } from '@vercel/build-utils';
|
||||
import exposeSystemEnvs from '../../util/dev/expose-system-envs';
|
||||
import getSystemEnvValues from '../../util/env/get-system-env-values';
|
||||
|
||||
const CONTENTS_PREFIX = '# Created by Vercel CLI\n';
|
||||
|
||||
@@ -84,15 +85,22 @@ export default async function pull(
|
||||
);
|
||||
const pullStamp = stamp();
|
||||
|
||||
const records: Env = await withSpinner(
|
||||
'Downloading',
|
||||
async () =>
|
||||
await getDecryptedEnvRecords(
|
||||
output,
|
||||
client,
|
||||
project,
|
||||
ProjectEnvTarget.Development
|
||||
)
|
||||
const [
|
||||
{ envs: projectEnvs },
|
||||
{ systemEnvValues },
|
||||
] = await withSpinner('Downloading', () =>
|
||||
Promise.all([
|
||||
getDecryptedEnvRecords(output, client, project.id),
|
||||
project.autoExposeSystemEnvs
|
||||
? getSystemEnvValues(output, client, project.id)
|
||||
: { systemEnvValues: [] },
|
||||
])
|
||||
);
|
||||
|
||||
const records = exposeSystemEnvs(
|
||||
projectEnvs,
|
||||
systemEnvValues,
|
||||
project.autoExposeSystemEnvs
|
||||
);
|
||||
|
||||
const contents =
|
||||
|
||||
29
packages/now-cli/src/commands/env/rm.ts
vendored
@@ -1,6 +1,6 @@
|
||||
import chalk from 'chalk';
|
||||
import inquirer from 'inquirer';
|
||||
import { ProjectEnvTarget, Project } from '../../types';
|
||||
import { ProjectEnvTarget, Project, ProjectEnvVariableV5 } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import confirm from '../../util/input/confirm';
|
||||
import removeEnvRecord from '../../util/env/remove-env-record';
|
||||
@@ -30,6 +30,9 @@ export default async function rm(
|
||||
args: string[],
|
||||
output: Output
|
||||
) {
|
||||
// improve the way we show inquirer prompts
|
||||
require('../../util/input/patch-inquirer');
|
||||
|
||||
if (args.length > 2) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${getCommandName(
|
||||
@@ -69,7 +72,20 @@ export default async function rm(
|
||||
envName = inputName;
|
||||
}
|
||||
|
||||
const { envs } = await getEnvVariables(output, client, project.id);
|
||||
const data = await getEnvVariables(output, client, project.id);
|
||||
|
||||
// we expand env vars with multiple targets
|
||||
const envs: ProjectEnvVariableV5[] = [];
|
||||
for (let env of data.envs) {
|
||||
if (Array.isArray(env.target)) {
|
||||
for (let target of env.target) {
|
||||
envs.push({ ...env, target });
|
||||
}
|
||||
} else {
|
||||
envs.push({ ...env, target: env.target });
|
||||
}
|
||||
}
|
||||
|
||||
const existing = new Set(
|
||||
envs.filter(r => r.key === envName).map(r => r.target)
|
||||
);
|
||||
@@ -79,7 +95,7 @@ export default async function rm(
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (envTargets.length === 0) {
|
||||
while (envTargets.length === 0) {
|
||||
const choices = getEnvTargetChoices().filter(c => existing.has(c.value));
|
||||
if (choices.length === 0) {
|
||||
output.error(
|
||||
@@ -97,6 +113,13 @@ export default async function rm(
|
||||
message: `Remove ${envName} from which Environments (select multiple)?`,
|
||||
choices,
|
||||
});
|
||||
|
||||
if (inputTargets.length === 0) {
|
||||
output.error(
|
||||
'Please select an Environment to remove the Environment Variable from.'
|
||||
);
|
||||
}
|
||||
|
||||
envTargets = inputTargets;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,12 +203,16 @@ export enum ProjectEnvTarget {
|
||||
Development = 'development',
|
||||
}
|
||||
|
||||
export type ProjectEnvVariableType = 'system' | 'secret' | 'plain';
|
||||
export enum ProjectEnvType {
|
||||
Plaintext = 'plain',
|
||||
Secret = 'secret',
|
||||
System = 'system',
|
||||
}
|
||||
|
||||
export interface ProjectEnvVariable {
|
||||
key: string;
|
||||
value: string;
|
||||
type: ProjectEnvVariableType;
|
||||
type: ProjectEnvType;
|
||||
configurationId?: string | null;
|
||||
createdAt?: number;
|
||||
updatedAt?: number;
|
||||
@@ -226,6 +230,7 @@ export interface ProjectSettings {
|
||||
buildCommand?: string | null;
|
||||
outputDirectory?: string | null;
|
||||
rootDirectory?: string | null;
|
||||
autoExposeSystemEnvs?: boolean;
|
||||
}
|
||||
|
||||
export interface Project extends ProjectSettings {
|
||||
@@ -239,6 +244,7 @@ export interface Project extends ProjectSettings {
|
||||
framework?: string | null;
|
||||
rootDirectory?: string | null;
|
||||
latestDeployments?: Partial<Deployment>[];
|
||||
autoExposeSystemEnvs?: boolean;
|
||||
}
|
||||
|
||||
export interface Org {
|
||||
|
||||
@@ -149,8 +149,8 @@ export async function executeBuild(
|
||||
filesRemoved,
|
||||
// This env distiniction is only necessary to maintain
|
||||
// backwards compatibility with the `@vercel/next` builder.
|
||||
env: envConfigs.runEnv,
|
||||
buildEnv: envConfigs.buildEnv,
|
||||
env: { ...envConfigs.runEnv },
|
||||
buildEnv: { ...envConfigs.buildEnv },
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
41
packages/now-cli/src/util/dev/expose-system-envs.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { ProjectEnvType, ProjectEnvVariable } from '../../types';
|
||||
import { Env } from '@vercel/build-utils';
|
||||
|
||||
function getSystemEnvValue(
|
||||
systemEnvRef: string,
|
||||
{ vercelUrl }: { vercelUrl?: string }
|
||||
) {
|
||||
if (systemEnvRef === 'VERCEL_URL') {
|
||||
return vercelUrl || '';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
export default function exposeSystemEnvs(
|
||||
projectEnvs: ProjectEnvVariable[],
|
||||
systemEnvValues: string[],
|
||||
autoExposeSystemEnvs: boolean | undefined,
|
||||
vercelUrl?: string
|
||||
) {
|
||||
const envs: Env = {};
|
||||
|
||||
if (autoExposeSystemEnvs) {
|
||||
envs['VERCEL'] = '1';
|
||||
envs['VERCEL_ENV'] = 'development';
|
||||
|
||||
for (const key of systemEnvValues) {
|
||||
envs[key] = getSystemEnvValue(key, { vercelUrl });
|
||||
}
|
||||
}
|
||||
|
||||
for (let env of projectEnvs) {
|
||||
if (env.type === ProjectEnvType.System) {
|
||||
envs[env.key] = getSystemEnvValue(env.value, { vercelUrl });
|
||||
} else {
|
||||
envs[env.key] = env.value;
|
||||
}
|
||||
}
|
||||
|
||||
return envs;
|
||||
}
|
||||
@@ -85,7 +85,8 @@ import {
|
||||
HttpHeadersConfig,
|
||||
EnvConfigs,
|
||||
} from './types';
|
||||
import { ProjectSettings } from '../../types';
|
||||
import { ProjectEnvVariable, ProjectSettings } from '../../types';
|
||||
import exposeSystemEnvs from './expose-system-envs';
|
||||
|
||||
const frameworkList = _frameworks as Framework[];
|
||||
const frontendRuntimeSet = new Set(
|
||||
@@ -149,14 +150,16 @@ export default class DevServer {
|
||||
private updateBuildersTimeout: NodeJS.Timeout | undefined;
|
||||
private startPromise: Promise<void> | null;
|
||||
|
||||
private environmentVars: Env | undefined;
|
||||
private systemEnvValues: string[];
|
||||
private projectEnvs: ProjectEnvVariable[];
|
||||
|
||||
constructor(cwd: string, options: DevServerOptions) {
|
||||
this.cwd = cwd;
|
||||
this.debug = options.debug;
|
||||
this.output = options.output;
|
||||
this.envConfigs = { buildEnv: {}, runEnv: {}, allEnv: {} };
|
||||
this.environmentVars = options.environmentVars;
|
||||
this.systemEnvValues = options.systemEnvValues || [];
|
||||
this.projectEnvs = options.projectEnvs || [];
|
||||
this.files = {};
|
||||
this.address = '';
|
||||
this.devCommand = options.devCommand;
|
||||
@@ -491,7 +494,7 @@ export default class DevServer {
|
||||
const dotenv = await fs.readFile(filePath, 'utf8');
|
||||
this.output.debug(`Using local env: ${filePath}`);
|
||||
env = parseDotenv(dotenv);
|
||||
env = this.populateVercelEnvVars(env);
|
||||
env = this.injectSystemValuesInDotenv(env);
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
@@ -642,10 +645,30 @@ export default class DevServer {
|
||||
|
||||
let allEnv = { ...buildEnv, ...runEnv };
|
||||
|
||||
// If no .env/.build.env is present, fetch and use cloud environment variables
|
||||
// If no .env/.build.env is present, use cloud environment variables
|
||||
if (Object.keys(allEnv).length === 0) {
|
||||
const cloudEnv = this.populateVercelEnvVars(this.environmentVars);
|
||||
allEnv = runEnv = buildEnv = cloudEnv;
|
||||
const cloudEnv = exposeSystemEnvs(
|
||||
this.projectEnvs || [],
|
||||
this.systemEnvValues || [],
|
||||
this.projectSettings && this.projectSettings.autoExposeSystemEnvs,
|
||||
new URL(this.address).host
|
||||
);
|
||||
|
||||
allEnv = { ...cloudEnv };
|
||||
runEnv = { ...cloudEnv };
|
||||
buildEnv = { ...cloudEnv };
|
||||
}
|
||||
|
||||
// legacy NOW_REGION env variable
|
||||
runEnv['NOW_REGION'] = 'dev1';
|
||||
buildEnv['NOW_REGION'] = 'dev1';
|
||||
allEnv['NOW_REGION'] = 'dev1';
|
||||
|
||||
// mirror how VERCEL_REGION is injected in prod/preview
|
||||
// only inject in `runEnvs`, because `allEnvs` is exposed to dev command
|
||||
// and should not contain VERCEL_REGION
|
||||
if (this.projectSettings && this.projectSettings.autoExposeSystemEnvs) {
|
||||
runEnv['VERCEL_REGION'] = 'dev1';
|
||||
}
|
||||
|
||||
this.envConfigs = { buildEnv, runEnv, allEnv };
|
||||
@@ -753,23 +776,15 @@ export default class DevServer {
|
||||
return merged;
|
||||
}
|
||||
|
||||
populateVercelEnvVars(env: Env | undefined): Env {
|
||||
if (!env) {
|
||||
return {};
|
||||
}
|
||||
|
||||
injectSystemValuesInDotenv(env: Env): Env {
|
||||
for (const name of Object.keys(env)) {
|
||||
if (name === 'VERCEL_URL') {
|
||||
const host = new URL(this.address).host;
|
||||
env['VERCEL_URL'] = host;
|
||||
env['VERCEL_URL'] = new URL(this.address).host;
|
||||
} else if (name === 'VERCEL_REGION') {
|
||||
env['VERCEL_REGION'] = 'dev1';
|
||||
}
|
||||
}
|
||||
|
||||
// Always set NOW_REGION to match production
|
||||
env['NOW_REGION'] = 'dev1';
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
@@ -1658,8 +1673,8 @@ export default class DevServer {
|
||||
isDev: true,
|
||||
requestPath,
|
||||
devCacheDir,
|
||||
env: envConfigs.runEnv,
|
||||
buildEnv: envConfigs.buildEnv,
|
||||
env: { ...envConfigs.runEnv },
|
||||
buildEnv: { ...envConfigs.buildEnv },
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
import { NowConfig } from '@vercel/client';
|
||||
import { HandleValue, Route } from '@vercel/routing-utils';
|
||||
import { Output } from '../output';
|
||||
import { ProjectSettings } from '../../types';
|
||||
import { ProjectEnvVariable, ProjectSettings } from '../../types';
|
||||
|
||||
export { NowConfig };
|
||||
|
||||
@@ -27,7 +27,8 @@ export interface DevServerOptions {
|
||||
devCommand?: string;
|
||||
frameworkSlug?: string;
|
||||
projectSettings?: ProjectSettings;
|
||||
environmentVars?: Env;
|
||||
systemEnvValues?: string[];
|
||||
projectEnvs?: ProjectEnvVariable[];
|
||||
}
|
||||
|
||||
export interface EnvConfigs {
|
||||
|
||||
55
packages/now-cli/src/util/env/add-env-record.ts
vendored
@@ -1,60 +1,39 @@
|
||||
import { Output } from '../output';
|
||||
import Client from '../client';
|
||||
import { Secret, ProjectEnvTarget, ProjectEnvVariableV5 } from '../../types';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import slugify from '@sindresorhus/slugify';
|
||||
import {
|
||||
Secret,
|
||||
ProjectEnvTarget,
|
||||
ProjectEnvVariableV5,
|
||||
ProjectEnvType,
|
||||
} from '../../types';
|
||||
|
||||
export default async function addEnvRecord(
|
||||
output: Output,
|
||||
client: Client,
|
||||
projectId: string,
|
||||
envName: string,
|
||||
envValue: string | undefined,
|
||||
type: ProjectEnvType,
|
||||
key: string,
|
||||
envValue: string,
|
||||
targets: ProjectEnvTarget[]
|
||||
): Promise<void> {
|
||||
output.debug(
|
||||
`Adding Environment Variable ${envName} to ${targets.length} targets`
|
||||
`Adding ${type} Environment Variable ${key} to ${targets.length} targets`
|
||||
);
|
||||
|
||||
let values: string[] | undefined;
|
||||
let value = envValue;
|
||||
|
||||
if (envValue) {
|
||||
const secrets = await Promise.all(
|
||||
targets.map(target =>
|
||||
client.fetch<Secret>('/v2/now/secrets', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
name: generateSecretName(envName, target),
|
||||
value: envValue,
|
||||
projectId: projectId,
|
||||
decryptable: target === ProjectEnvTarget.Development,
|
||||
}),
|
||||
})
|
||||
)
|
||||
if (type === ProjectEnvType.Secret) {
|
||||
const secret = await client.fetch<Secret>(
|
||||
`/v2/now/secrets/${encodeURIComponent(envValue)}`
|
||||
);
|
||||
values = secrets.map(secret => secret.uid);
|
||||
value = secret.uid;
|
||||
}
|
||||
|
||||
const body = targets.map((target, i) => ({
|
||||
key: envName,
|
||||
value: values ? values[i] : '',
|
||||
target,
|
||||
}));
|
||||
const body = { type, key, value, target: targets };
|
||||
|
||||
const urlProject = `/v4/projects/${projectId}/env`;
|
||||
const urlProject = `/v6/projects/${projectId}/env`;
|
||||
await client.fetch<ProjectEnvVariableV5>(urlProject, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
}
|
||||
|
||||
const randomSecretSuffix = customAlphabet(
|
||||
'123456789abcdefghijklmnopqrstuvwxyz',
|
||||
4
|
||||
);
|
||||
|
||||
function generateSecretName(envName: string, target: ProjectEnvTarget) {
|
||||
return `${
|
||||
slugify(envName).substring(0, 80) // we truncate because the max secret length is 100
|
||||
}-${target}-${randomSecretSuffix()}`;
|
||||
}
|
||||
|
||||
15
packages/now-cli/src/util/env/env-type.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ProjectEnvType } from '../../types';
|
||||
|
||||
function envTypes(): string[] {
|
||||
return Object.values(ProjectEnvType);
|
||||
}
|
||||
|
||||
export function isValidEnvType(
|
||||
type?: string
|
||||
): type is ProjectEnvType | undefined {
|
||||
return typeof type === 'undefined' || envTypes().includes(type);
|
||||
}
|
||||
|
||||
export function getEnvTypePlaceholder() {
|
||||
return `<${envTypes().join(' | ')}>`;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { Output } from '../output';
|
||||
import Client from '../client';
|
||||
import { Secret } from '../../types';
|
||||
|
||||
export default async function getDecryptedSecret(
|
||||
output: Output,
|
||||
client: Client,
|
||||
secretId: string
|
||||
): Promise<string> {
|
||||
if (!secretId) {
|
||||
return '';
|
||||
}
|
||||
output.debug(`Fetching decrypted secret ${secretId}`);
|
||||
const url = `/v2/now/secrets/${secretId}?decrypt=true`;
|
||||
const secret = await client.fetch<Secret>(url);
|
||||
return secret.value;
|
||||
}
|
||||
12
packages/now-cli/src/util/env/get-system-env-values.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Output } from '../output';
|
||||
import Client from '../client';
|
||||
|
||||
export default async function getSystemEnvValues(
|
||||
output: Output,
|
||||
client: Client,
|
||||
projectId: string
|
||||
) {
|
||||
output.debug(`Fetching System Environment Values of project ${projectId}`);
|
||||
const url = `/v6/projects/${projectId}/system-env-values`;
|
||||
return client.fetch<{ systemEnvValues: string[] }>(url);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Output } from '../output';
|
||||
import Client from '../client';
|
||||
import { ProjectEnvTarget, Secret, ProjectEnvVariableV5 } from '../../types';
|
||||
import { ProjectEnvTarget, ProjectEnvVariableV5 } from '../../types';
|
||||
|
||||
export default async function removeEnvRecord(
|
||||
output: Output,
|
||||
@@ -18,32 +18,7 @@ export default async function removeEnvRecord(
|
||||
envName
|
||||
)}${qs}`;
|
||||
|
||||
const env = await client.fetch<ProjectEnvVariableV5>(urlProject, {
|
||||
await client.fetch<ProjectEnvVariableV5>(urlProject, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (env && env.value) {
|
||||
const idOrName = env.value.startsWith('@') ? env.value.slice(1) : env.value;
|
||||
const urlSecret = `/v2/now/secrets/${idOrName}`;
|
||||
let secret: Secret | undefined;
|
||||
|
||||
try {
|
||||
secret = await client.fetch<Secret>(urlSecret);
|
||||
} catch (error) {
|
||||
if (error && error.status === 404) {
|
||||
// User likely deleted the secret before the env var, so we can still report success
|
||||
output.debug(
|
||||
`Skipped ${env.key} because secret ${idOrName} was already deleted`
|
||||
);
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Since integrations add global secrets, we must only delete if the secret was
|
||||
// specifically added to this project
|
||||
if (secret && secret.projectId === projectId) {
|
||||
await client.fetch<Secret>(urlSecret, { method: 'DELETE' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +1,65 @@
|
||||
import getEnvVariables from './env/get-env-records';
|
||||
import getDecryptedSecret from './env/get-decrypted-secret';
|
||||
import Client from './client';
|
||||
import { Output } from './output/create-output';
|
||||
import { ProjectEnvTarget, Project } from '../types';
|
||||
|
||||
import { Env } from '@vercel/build-utils';
|
||||
import {
|
||||
ProjectEnvTarget,
|
||||
ProjectEnvType,
|
||||
ProjectEnvVariable,
|
||||
Secret,
|
||||
} from '../types';
|
||||
import getEnvRecords from './env/get-env-records';
|
||||
|
||||
export default async function getDecryptedEnvRecords(
|
||||
output: Output,
|
||||
client: Client,
|
||||
project: Project,
|
||||
target: ProjectEnvTarget
|
||||
): Promise<Env> {
|
||||
const { envs } = await getEnvVariables(output, client, project.id, target);
|
||||
const decryptedValues = await Promise.all(
|
||||
envs.map(async env => {
|
||||
if (env.type === 'system') {
|
||||
return { value: '', found: true };
|
||||
} else if (env.type === 'plain') {
|
||||
return { value: env.value, found: true };
|
||||
projectId: string
|
||||
): Promise<{ envs: ProjectEnvVariable[] }> {
|
||||
const { envs } = await getEnvRecords(
|
||||
output,
|
||||
client,
|
||||
projectId,
|
||||
ProjectEnvTarget.Development
|
||||
);
|
||||
|
||||
const envsWithDecryptedSecrets = await Promise.all(
|
||||
envs.map(async ({ type, key, value }) => {
|
||||
// it's not possible to create secret env variables for development
|
||||
// anymore but we keep this because legacy env variables with "decryptable"
|
||||
// secret values still exit in our system
|
||||
if (type === ProjectEnvType.Secret) {
|
||||
try {
|
||||
const secretIdOrName = value;
|
||||
|
||||
if (!secretIdOrName) {
|
||||
return { type, key, value: '', found: true };
|
||||
}
|
||||
|
||||
output.debug(`Fetching decrypted secret ${secretIdOrName}`);
|
||||
const secret = await client.fetch<Secret>(
|
||||
`/v2/now/secrets/${secretIdOrName}?decrypt=true`
|
||||
);
|
||||
|
||||
return { type, key, value: secret.value, found: true };
|
||||
} catch (error) {
|
||||
if (error && error.status === 404) {
|
||||
return { type, key, value: '', found: false };
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const value = await getDecryptedSecret(output, client, env.value);
|
||||
return { value, found: true };
|
||||
} catch (error) {
|
||||
if (error && error.status === 404) {
|
||||
return { value: '', found: false };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return { type, key, value, found: true };
|
||||
})
|
||||
);
|
||||
|
||||
const results: Env = {};
|
||||
for (let i = 0; i < decryptedValues.length; i++) {
|
||||
const { key } = envs[i];
|
||||
const { value, found } = decryptedValues[i];
|
||||
|
||||
if (!found) {
|
||||
for (let env of envsWithDecryptedSecrets) {
|
||||
if (!env.found) {
|
||||
output.print('');
|
||||
output.warn(
|
||||
`Unable to download variable ${key} because associated secret was deleted`
|
||||
`Unable to download variable ${env.key} because associated secret was deleted`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
results[key] = value ? value : '';
|
||||
}
|
||||
return results;
|
||||
|
||||
return { envs: envsWithDecryptedSecrets };
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import chalk from 'chalk';
|
||||
*/
|
||||
|
||||
// adjusted from https://github.com/SBoudrias/Inquirer.js/blob/942908f17319343d1acc7b876f990797c5695918/packages/inquirer/lib/prompts/base.js#L126
|
||||
const getQuestion = function() {
|
||||
const getQuestion = function () {
|
||||
let message = `${chalk.gray('?')} ${this.opt.message} `;
|
||||
|
||||
if (this.opt.type === 'confirm') {
|
||||
@@ -35,7 +35,7 @@ inquirer.prompt.prompts.input.prototype.getQuestion = getQuestion;
|
||||
inquirer.prompt.prompts.confirm.prototype.getQuestion = getQuestion;
|
||||
|
||||
// adjusted from https://github.com/SBoudrias/Inquirer.js/blob/942908f17319343d1acc7b876f990797c5695918/packages/inquirer/lib/prompts/list.js#L80
|
||||
inquirer.prompt.prompts.list.prototype.render = function() {
|
||||
inquirer.prompt.prompts.list.prototype.render = function () {
|
||||
// Render question
|
||||
let message = this.getQuestion();
|
||||
|
||||
@@ -89,11 +89,22 @@ function listRender(choices, pointer) {
|
||||
}
|
||||
|
||||
// adjusted from https://github.com/SBoudrias/Inquirer.js/blob/942908f17319343d1acc7b876f990797c5695918/packages/inquirer/lib/prompts/checkbox.js#L84
|
||||
inquirer.prompt.prompts.checkbox.prototype.render = function(error) {
|
||||
inquirer.prompt.prompts.checkbox.prototype.render = function (error) {
|
||||
// Render question
|
||||
let message = this.getQuestion();
|
||||
let bottomContent = '';
|
||||
|
||||
if (!this.spaceKeyPressed) {
|
||||
message +=
|
||||
'(Press ' +
|
||||
chalk.cyan.bold('<space>') +
|
||||
' to select, ' +
|
||||
chalk.cyan.bold('<a>') +
|
||||
' to toggle all, ' +
|
||||
chalk.cyan.bold('<i>') +
|
||||
' to invert selection)';
|
||||
}
|
||||
|
||||
// Render choices or answer depending on the state
|
||||
if (this.status === 'answered') {
|
||||
message += this.selection.length > 0 ? this.selection.join(', ') : 'None';
|
||||
@@ -118,7 +129,7 @@ function renderChoices(choices, pointer) {
|
||||
let output = '';
|
||||
let separatorOffset = 0;
|
||||
|
||||
choices.forEach(function(choice, i) {
|
||||
choices.forEach(function (choice, i) {
|
||||
if (choice.type === 'separator') {
|
||||
separatorOffset++;
|
||||
output += '' + choice + '\n';
|
||||
@@ -151,7 +162,7 @@ function renderChoices(choices, pointer) {
|
||||
}
|
||||
|
||||
// adjusted from https://github.com/SBoudrias/Inquirer.js/blob/942908f17319343d1acc7b876f990797c5695918/packages/inquirer/lib/prompts/input.js#L44
|
||||
inquirer.prompt.prompts.input.prototype.render = function(error) {
|
||||
inquirer.prompt.prompts.input.prototype.render = function (error) {
|
||||
let bottomContent = '';
|
||||
let appendContent = '';
|
||||
let message = this.getQuestion();
|
||||
@@ -178,7 +189,7 @@ inquirer.prompt.prompts.input.prototype.render = function(error) {
|
||||
};
|
||||
|
||||
// adjusted from https://github.com/SBoudrias/Inquirer.js/blob/942908f17319343d1acc7b876f990797c5695918/packages/inquirer/lib/prompts/confirm.js#L64
|
||||
inquirer.prompt.prompts.confirm.prototype.render = function(answer) {
|
||||
inquirer.prompt.prompts.confirm.prototype.render = function (answer) {
|
||||
let message = this.getQuestion();
|
||||
|
||||
if (this.status === 'answered') {
|
||||
|
||||
4
packages/now-cli/test/dev/fixtures/30-next-image-optimization/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.next
|
||||
!public
|
||||
yarn.lock
|
||||
.vercel
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "^17.0.0",
|
||||
"react-dom": "^17.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import Image from 'next/image';
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<h1>Home Page</h1>
|
||||
<hr />
|
||||
<h2>Optimized</h2>
|
||||
<Image src="/test.png" width="400" height="400" />
|
||||
<hr />
|
||||
<h2>Original</h2>
|
||||
<img src="/test.png" width="400" height="400" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="400.000000pt" height="400.000000pt" viewBox="0 0 400.000000 400.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<g transform="translate(0.000000,400.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M0 2000 l0 -2000 2000 0 2000 0 0 2000 0 2000 -2000 0 -2000 0 0
|
||||
-2000z m2401 118 l396 -693 -398 -3 c-220 -1 -578 -1 -798 0 l-398 3 396 693
|
||||
c217 380 398 692 401 692 3 0 184 -312 401 -692z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 635 B |
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"version": 2,
|
||||
"build": {
|
||||
"env": {
|
||||
"FORCE_BUILDER_TAG": "canary"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,14 +128,13 @@ async function testPath(
|
||||
status,
|
||||
path,
|
||||
expectedText,
|
||||
headers = {},
|
||||
method = 'GET',
|
||||
body = undefined
|
||||
expectedHeaders = {},
|
||||
fetchOpts = {}
|
||||
) {
|
||||
const opts = { redirect: 'manual-dont-change', method, body };
|
||||
const opts = { ...fetchOpts, redirect: 'manual-dont-change' };
|
||||
const url = `${origin}${path}`;
|
||||
const res = await fetch(url, opts);
|
||||
const msg = `Testing response from ${method} ${url}`;
|
||||
const msg = `Testing response from ${fetchOpts.method || 'GET'} ${url}`;
|
||||
console.log(msg);
|
||||
t.is(res.status, status, msg);
|
||||
validateResponseHeaders(t, res);
|
||||
@@ -150,8 +149,8 @@ async function testPath(
|
||||
expectedText.lastIndex = 0; // reset since we test twice
|
||||
t.regex(actualText, expectedText);
|
||||
}
|
||||
if (headers) {
|
||||
Object.entries(headers).forEach(([key, expectedValue]) => {
|
||||
if (expectedHeaders) {
|
||||
Object.entries(expectedHeaders).forEach(([key, expectedValue]) => {
|
||||
let actualValue = res.headers.get(key);
|
||||
if (key.toLowerCase() === 'location' && actualValue === '//') {
|
||||
// HACK: `node-fetch` has strange behavior for location header so fix it
|
||||
@@ -387,9 +386,12 @@ test(
|
||||
async testPath => {
|
||||
await testPath(200, '/', /<div id="redwood-app">/m);
|
||||
await testPath(200, '/about', /<div id="redwood-app">/m);
|
||||
const reqBody = '{"query":"{redwood{version}}"}';
|
||||
const fetchOpts = {
|
||||
method: 'POST',
|
||||
body: '{"query":"{redwood{version}}"}',
|
||||
};
|
||||
const resBody = '{"data":{"redwood":{"version":"0.15.0"}}}';
|
||||
await testPath(200, '/api/graphql', resBody, {}, 'POST', reqBody);
|
||||
await testPath(200, '/api/graphql', resBody, {}, fetchOpts);
|
||||
},
|
||||
{ isExample: true }
|
||||
)
|
||||
@@ -945,12 +947,16 @@ test(
|
||||
'Access-Control-Allow-Methods':
|
||||
'GET, POST, OPTIONS, HEAD, PATCH, PUT, DELETE',
|
||||
};
|
||||
await testPath(200, '/', 'status api', headers, 'GET');
|
||||
await testPath(200, '/', 'status api', headers, 'POST');
|
||||
await testPath(200, '/api/status.js', 'status api', headers, 'GET');
|
||||
await testPath(200, '/api/status.js', 'status api', headers, 'POST');
|
||||
await testPath(204, '/', '', headers, 'OPTIONS');
|
||||
await testPath(204, '/api/status.js', '', headers, 'OPTIONS');
|
||||
await testPath(200, '/', 'status api', headers, { method: 'GET' });
|
||||
await testPath(200, '/', 'status api', headers, { method: 'POST' });
|
||||
await testPath(200, '/api/status.js', 'status api', headers, {
|
||||
method: 'GET',
|
||||
});
|
||||
await testPath(200, '/api/status.js', 'status api', headers, {
|
||||
method: 'POST',
|
||||
});
|
||||
await testPath(204, '/', '', headers, { method: 'OPTIONS' });
|
||||
await testPath(204, '/api/status.js', '', headers, { method: 'OPTIONS' });
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1595,6 +1601,61 @@ test(
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] 30-next-image-optimization',
|
||||
testFixtureStdio('30-next-image-optimization', async testPath => {
|
||||
const toUrl = (url, w, q) => {
|
||||
const query = new URLSearchParams();
|
||||
query.append('url', url);
|
||||
query.append('w', w);
|
||||
query.append('q', q);
|
||||
return `/_next/image?${query}`;
|
||||
};
|
||||
|
||||
const expectHeader = accept => ({
|
||||
'content-type': accept,
|
||||
'cache-control': 'public, max-age=0, must-revalidate',
|
||||
});
|
||||
const fetchOpts = accept => ({ method: 'GET', headers: { accept } });
|
||||
await testPath(200, '/', /Home Page/m);
|
||||
await testPath(
|
||||
200,
|
||||
toUrl('/test.jpg', 64, 100),
|
||||
null,
|
||||
expectHeader('image/webp'),
|
||||
fetchOpts('image/webp')
|
||||
);
|
||||
await testPath(
|
||||
200,
|
||||
toUrl('/test.png', 64, 90),
|
||||
null,
|
||||
expectHeader('image/webp'),
|
||||
fetchOpts('image/webp')
|
||||
);
|
||||
await testPath(
|
||||
200,
|
||||
toUrl('/test.gif', 64, 80),
|
||||
null,
|
||||
expectHeader('image/webp'),
|
||||
fetchOpts('image/webp')
|
||||
);
|
||||
await testPath(
|
||||
200,
|
||||
toUrl('/test.svg', 64, 70),
|
||||
null,
|
||||
expectHeader('image/svg+xml'),
|
||||
fetchOpts('image/webp')
|
||||
);
|
||||
await testPath(
|
||||
200,
|
||||
toUrl('/animated.gif', 64, 60),
|
||||
null,
|
||||
expectHeader('image/gif'),
|
||||
fetchOpts('image/gif')
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] Use `@vercel/python` with Flask requirements.txt',
|
||||
testFixtureStdio('python-flask', async testPath => {
|
||||
|
||||
375
packages/now-cli/test/integration.js
vendored
@@ -422,32 +422,17 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
t.is(exitCode, 0, formatOutput({ stderr, stdout }));
|
||||
}
|
||||
|
||||
function withPlainTextEnv(fn) {
|
||||
return async function (...args) {
|
||||
const link = require(path.join(target, '.vercel/project.json'));
|
||||
const postRes = await apiFetch(`/v6/projects/${link.projectId}/env`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
type: 'plain',
|
||||
key: 'MY_PLAIN_VAR',
|
||||
value: 'hello',
|
||||
target: ['development'],
|
||||
}),
|
||||
});
|
||||
t.is(postRes.status, 200);
|
||||
async function createSecret() {
|
||||
const name = `my-secret${Math.floor(Math.random() * 10000)}`;
|
||||
|
||||
try {
|
||||
return await fn(...args);
|
||||
} finally {
|
||||
const deleteRes = await apiFetch(
|
||||
`/v4/projects/${link.projectId}/env/MY_PLAIN_VAR?target=development`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
}
|
||||
);
|
||||
t.is(deleteRes.status, 200);
|
||||
}
|
||||
};
|
||||
const res = await apiFetch('/v2/now/secrets', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ name, value: 'my secret' }),
|
||||
});
|
||||
|
||||
t.is(res.status, 200);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
async function nowEnvLsIsEmpty() {
|
||||
@@ -464,26 +449,34 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
t.regex(stderr, /No Environment Variables found in Project/gm);
|
||||
}
|
||||
|
||||
async function nowEnvAdd() {
|
||||
async function nowEnvAddPlaintext() {
|
||||
const now = execa(binaryPath, ['env', 'add', ...defaultArgs], {
|
||||
reject: false,
|
||||
cwd: target,
|
||||
});
|
||||
|
||||
await waitForPrompt(now, chunk =>
|
||||
chunk.includes('Which type of Environment Variable do you want to add?')
|
||||
);
|
||||
now.stdin.write('\n'); // select plaintext
|
||||
|
||||
await waitForPrompt(now, chunk =>
|
||||
chunk.includes('What’s the name of the variable?')
|
||||
);
|
||||
now.stdin.write('MY_ENV_VAR\n');
|
||||
now.stdin.write('MY_PLAINTEXT_ENV_VAR\n');
|
||||
await waitForPrompt(
|
||||
now,
|
||||
chunk =>
|
||||
chunk.includes('What’s the value of') && chunk.includes('MY_ENV_VAR')
|
||||
chunk.includes('What’s the value of') &&
|
||||
chunk.includes('MY_PLAINTEXT_ENV_VAR')
|
||||
);
|
||||
now.stdin.write('MY_VALUE\n');
|
||||
now.stdin.write('my plaintext value\n');
|
||||
|
||||
await waitForPrompt(
|
||||
now,
|
||||
chunk =>
|
||||
chunk.includes('which Environments') && chunk.includes('MY_ENV_VAR')
|
||||
chunk.includes('which Environments') &&
|
||||
chunk.includes('MY_PLAINTEXT_ENV_VAR')
|
||||
);
|
||||
now.stdin.write('a\n'); // select all
|
||||
|
||||
@@ -492,10 +485,47 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
t.is(exitCode, 0, formatOutput({ stderr, stdout }));
|
||||
}
|
||||
|
||||
async function nowEnvAddSecret(secretName) {
|
||||
const now = execa(binaryPath, ['env', 'add', ...defaultArgs], {
|
||||
reject: false,
|
||||
cwd: target,
|
||||
});
|
||||
|
||||
await waitForPrompt(now, chunk =>
|
||||
chunk.includes('Which type of Environment Variable do you want to add?')
|
||||
);
|
||||
now.stdin.write('j\n'); // select secret
|
||||
|
||||
await waitForPrompt(now, chunk =>
|
||||
chunk.includes('What’s the name of the variable?')
|
||||
);
|
||||
now.stdin.write('MY_SECRET_ENV_VAR\n');
|
||||
|
||||
await waitForPrompt(
|
||||
now,
|
||||
chunk =>
|
||||
chunk.includes('What’s the value of') &&
|
||||
chunk.includes('MY_SECRET_ENV_VAR')
|
||||
);
|
||||
now.stdin.write(`@${secretName}\n`);
|
||||
|
||||
await waitForPrompt(
|
||||
now,
|
||||
chunk =>
|
||||
chunk.includes('which Environments') &&
|
||||
chunk.includes('MY_SECRET_ENV_VAR')
|
||||
);
|
||||
now.stdin.write('j \n'); // select preview
|
||||
|
||||
const { exitCode, stderr, stdout } = await now;
|
||||
|
||||
t.is(exitCode, 0, formatOutput({ stderr, stdout }));
|
||||
}
|
||||
|
||||
async function nowEnvAddFromStdin() {
|
||||
const now = execa(
|
||||
binaryPath,
|
||||
['env', 'add', 'MY_STDIN_VAR', 'development', ...defaultArgs],
|
||||
['env', 'add', 'plain', 'MY_STDIN_VAR', 'development', ...defaultArgs],
|
||||
{
|
||||
reject: false,
|
||||
cwd: target,
|
||||
@@ -509,13 +539,20 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
async function nowEnvAddSystemEnv() {
|
||||
const now = execa(
|
||||
binaryPath,
|
||||
['env', 'add', 'VERCEL_URL', ...defaultArgs],
|
||||
['env', 'add', 'system', 'NEXT_PUBLIC_VERCEL_URL', ...defaultArgs],
|
||||
{
|
||||
reject: false,
|
||||
cwd: target,
|
||||
}
|
||||
);
|
||||
|
||||
await waitForPrompt(
|
||||
now,
|
||||
chunk =>
|
||||
chunk.includes('What’s the value of') && chunk.includes('VERCEL_URL')
|
||||
);
|
||||
now.stdin.write(`\n`); // select VERCEL_URL
|
||||
|
||||
await waitForPrompt(
|
||||
now,
|
||||
chunk =>
|
||||
@@ -541,28 +578,63 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
t.is(exitCode, 0, formatOutput({ stderr, stdout }));
|
||||
t.regex(stderr, /Environment Variables found in Project/gm);
|
||||
|
||||
console.log(stdout);
|
||||
|
||||
const lines = stdout.split('\n');
|
||||
|
||||
const myEnvVars = lines.filter(line => line.includes('MY_ENV_VAR'));
|
||||
t.is(myEnvVars.length, 3);
|
||||
t.regex(myEnvVars.join('\n'), /development/gm);
|
||||
t.regex(myEnvVars.join('\n'), /preview/gm);
|
||||
t.regex(myEnvVars.join('\n'), /production/gm);
|
||||
const plaintextEnvs = lines.filter(line =>
|
||||
line.includes('MY_PLAINTEXT_ENV_VAR')
|
||||
);
|
||||
t.is(plaintextEnvs.length, 1);
|
||||
t.regex(plaintextEnvs[0], /Production, Preview, Development/gm);
|
||||
|
||||
const myStdinVars = lines.filter(line => line.includes('MY_STDIN_VAR'));
|
||||
t.is(myStdinVars.length, 1);
|
||||
t.regex(myStdinVars.join('\n'), /development/gm);
|
||||
const secretEnvs = lines.filter(line => line.includes('MY_SECRET_ENV_VAR'));
|
||||
t.is(secretEnvs.length, 1);
|
||||
t.regex(secretEnvs[0], /Preview/gm);
|
||||
|
||||
const vercelVars = lines.filter(line => line.includes('VERCEL_URL'));
|
||||
t.is(vercelVars.length, 3);
|
||||
t.regex(vercelVars.join('\n'), /development/gm);
|
||||
t.regex(vercelVars.join('\n'), /preview/gm);
|
||||
t.regex(vercelVars.join('\n'), /production/gm);
|
||||
const stdinEnvs = lines.filter(line => line.includes('MY_STDIN_VAR'));
|
||||
t.is(stdinEnvs.length, 1);
|
||||
t.regex(stdinEnvs[0], /Development/gm);
|
||||
|
||||
const myPlainVars = lines.filter(line => line.includes('MY_PLAIN_VAR'));
|
||||
t.is(myPlainVars.length, 1);
|
||||
t.regex(myPlainVars.join('\n'), /development/gm);
|
||||
t.regex(myPlainVars.join('\n'), /hello/gm);
|
||||
const systemEnvs = lines.filter(line => line.includes('VERCEL_URL'));
|
||||
t.is(systemEnvs.length, 1);
|
||||
t.regex(systemEnvs[0], /VERCEL_URL/gm);
|
||||
t.regex(systemEnvs[0], /Production, Preview, Development/gm);
|
||||
}
|
||||
|
||||
// we create a "legacy" env variable that contains a decryptable secret
|
||||
// to check that vc env pull and vc dev work correctly with decryptable secrets
|
||||
async function createEnvWithDecryptableSecret() {
|
||||
console.log('creating an env variable with a decryptable secret');
|
||||
|
||||
const name = `my-secret${Math.floor(Math.random() * 10000)}`;
|
||||
|
||||
const res = await apiFetch('/v2/now/secrets', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
name,
|
||||
value: 'decryptable value',
|
||||
decryptable: true,
|
||||
}),
|
||||
});
|
||||
|
||||
t.is(res.status, 200);
|
||||
|
||||
const json = await res.json();
|
||||
|
||||
const link = require(path.join(target, '.vercel/project.json'));
|
||||
|
||||
const resEnv = await apiFetch(`/v4/projects/${link.projectId}/env`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
key: 'MY_DECRYPTABLE_SECRET_ENV',
|
||||
value: json.uid,
|
||||
target: ['development'],
|
||||
type: 'secret',
|
||||
}),
|
||||
});
|
||||
|
||||
t.is(resEnv.status, 200);
|
||||
}
|
||||
|
||||
async function nowEnvPull() {
|
||||
@@ -582,10 +654,10 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
t.true(contents.startsWith('# Created by Vercel CLI\n'));
|
||||
|
||||
const lines = new Set(contents.split('\n'));
|
||||
t.true(lines.has('MY_ENV_VAR="MY_VALUE"'));
|
||||
t.true(lines.has('MY_PLAINTEXT_ENV_VAR="my plaintext value"'));
|
||||
t.true(lines.has('MY_STDIN_VAR="{"expect":"quotes"}"'));
|
||||
t.true(lines.has('VERCEL_URL=""'));
|
||||
t.true(lines.has('MY_PLAIN_VAR="hello"'));
|
||||
t.true(lines.has('NEXT_PUBLIC_VERCEL_URL=""'));
|
||||
t.true(lines.has('MY_DECRYPTABLE_SECRET_ENV="decryptable value"'));
|
||||
}
|
||||
|
||||
async function nowEnvPullOverwrite() {
|
||||
@@ -637,16 +709,18 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
const apiRes = await fetch(apiUrl);
|
||||
t.is(apiRes.status, 200, formatOutput({ stderr, stdout }));
|
||||
const apiJson = await apiRes.json();
|
||||
t.is(apiJson['MY_ENV_VAR'], 'MY_VALUE');
|
||||
t.is(apiJson['VERCEL_URL'], host);
|
||||
t.is(apiJson['MY_PLAINTEXT_ENV_VAR'], 'my plaintext value');
|
||||
t.is(apiJson['MY_SECRET_ENV_VAR'], 'my secret');
|
||||
t.is(apiJson['NEXT_PUBLIC_VERCEL_URL'], host);
|
||||
|
||||
const homeUrl = `https://${host}`;
|
||||
console.log({ homeUrl });
|
||||
const homeRes = await fetch(homeUrl);
|
||||
t.is(homeRes.status, 200, formatOutput({ stderr, stdout }));
|
||||
const homeJson = await homeRes.json();
|
||||
t.is(homeJson['MY_ENV_VAR'], 'MY_VALUE');
|
||||
t.is(homeJson['VERCEL_URL'], host);
|
||||
t.is(homeJson['MY_PLAINTEXT_ENV_VAR'], 'my plaintext value');
|
||||
t.is(homeJson['MY_SECRET_ENV_VAR'], 'my secret');
|
||||
t.is(homeJson['NEXT_PUBLIC_VERCEL_URL'], host);
|
||||
}
|
||||
|
||||
async function nowDevWithEnv() {
|
||||
@@ -664,8 +738,6 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
return false;
|
||||
});
|
||||
|
||||
const localhostNoProtocol = localhost[0].slice('http://'.length);
|
||||
|
||||
const apiUrl = `${localhost[0]}/api/get-env`;
|
||||
const apiRes = await fetch(apiUrl);
|
||||
|
||||
@@ -673,15 +745,17 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
|
||||
const apiJson = await apiRes.json();
|
||||
|
||||
t.is(apiJson['MY_ENV_VAR'], 'MY_VALUE');
|
||||
t.is(apiJson['VERCEL_URL'], localhostNoProtocol);
|
||||
t.is(apiJson['MY_PLAINTEXT_ENV_VAR'], 'my plaintext value');
|
||||
t.is(apiJson['NEXT_PUBLIC_VERCEL_URL'], '');
|
||||
t.is(apiJson['MY_DECRYPTABLE_SECRET_ENV'], 'decryptable value');
|
||||
|
||||
const homeUrl = localhost[0];
|
||||
|
||||
const homeRes = await fetch(homeUrl);
|
||||
const homeJson = await homeRes.json();
|
||||
t.is(homeJson['MY_ENV_VAR'], 'MY_VALUE');
|
||||
t.is(homeJson['VERCEL_URL'], localhostNoProtocol);
|
||||
t.is(homeJson['MY_PLAINTEXT_ENV_VAR'], 'my plaintext value');
|
||||
t.is(homeJson['NEXT_PUBLIC_VERCEL_URL'], '');
|
||||
t.is(homeJson['MY_DECRYPTABLE_SECRET_ENV'], 'decryptable value');
|
||||
|
||||
vc.kill('SIGTERM', { forceKillAfterTimeout: 2000 });
|
||||
|
||||
@@ -713,16 +787,105 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
|
||||
const apiJson = await apiRes.json();
|
||||
|
||||
t.is(apiJson['VERCEL_URL'], localhostNoProtocol);
|
||||
t.is(apiJson['MY_ENV_VAR'], 'MY_VALUE');
|
||||
t.is(apiJson['MY_PLAIN_VAR'], 'hello');
|
||||
t.is(apiJson['NEXT_PUBLIC_VERCEL_URL'], localhostNoProtocol);
|
||||
t.is(apiJson['MY_PLAINTEXT_ENV_VAR'], 'my plaintext value');
|
||||
t.is(apiJson['MY_STDIN_VAR'], '{"expect":"quotes"}');
|
||||
t.is(apiJson['MY_DECRYPTABLE_SECRET_ENV'], 'decryptable value');
|
||||
|
||||
const homeUrl = localhost[0];
|
||||
const homeRes = await fetch(homeUrl);
|
||||
const homeJson = await homeRes.json();
|
||||
t.is(homeJson['MY_ENV_VAR'], 'MY_VALUE');
|
||||
t.is(homeJson['MY_PLAINTEXT_ENV_VAR'], 'my plaintext value');
|
||||
t.is(homeJson['NEXT_PUBLIC_VERCEL_URL'], localhostNoProtocol);
|
||||
t.is(homeJson['MY_STDIN_VAR'], '{"expect":"quotes"}');
|
||||
t.is(homeJson['MY_DECRYPTABLE_SECRET_ENV'], 'decryptable value');
|
||||
|
||||
// system env vars are not automatically exposed
|
||||
t.is(apiJson['VERCEL'], undefined);
|
||||
t.is(homeJson['VERCEL'], undefined);
|
||||
|
||||
vc.kill('SIGTERM', { forceKillAfterTimeout: 2000 });
|
||||
|
||||
const { exitCode, stderr, stdout } = await vc;
|
||||
t.is(exitCode, 0, formatOutput({ stderr, stdout }));
|
||||
}
|
||||
|
||||
async function enableAutoExposeSystemEnvs() {
|
||||
const link = require(path.join(target, '.vercel/project.json'));
|
||||
|
||||
const res = await apiFetch(`/v2/projects/${link.projectId}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({ autoExposeSystemEnvs: true }),
|
||||
});
|
||||
|
||||
t.is(res.status, 200);
|
||||
if (res.status === 200) {
|
||||
console.log(
|
||||
`Set autoExposeSystemEnvs=true for project ${link.projectId}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function nowEnvPullFetchSystemVars() {
|
||||
const { exitCode, stderr, stdout } = await execa(
|
||||
binaryPath,
|
||||
['env', 'pull', '-y', ...defaultArgs],
|
||||
{
|
||||
reject: false,
|
||||
cwd: target,
|
||||
}
|
||||
);
|
||||
|
||||
t.is(exitCode, 0, formatOutput({ stderr, stdout }));
|
||||
|
||||
const contents = fs.readFileSync(path.join(target, '.env'), 'utf8');
|
||||
|
||||
const lines = new Set(contents.split('\n'));
|
||||
t.true(lines.has('VERCEL="1"'));
|
||||
t.true(lines.has('VERCEL_URL=""'));
|
||||
t.true(lines.has('NEXT_PUBLIC_VERCEL_URL=""'));
|
||||
t.true(lines.has('VERCEL_ENV="development"'));
|
||||
t.true(lines.has('VERCEL_GIT_PROVIDER=""'));
|
||||
t.true(lines.has('VERCEL_GIT_REPO_SLUG=""'));
|
||||
}
|
||||
|
||||
async function nowDevAndFetchSystemVars() {
|
||||
const vc = execa(binaryPath, ['dev', ...defaultArgs], {
|
||||
reject: false,
|
||||
cwd: target,
|
||||
});
|
||||
|
||||
let localhost = undefined;
|
||||
await waitForPrompt(vc, chunk => {
|
||||
if (chunk.includes('Ready! Available at')) {
|
||||
localhost = /(https?:[^\s]+)/g.exec(chunk);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const apiUrl = `${localhost[0]}/api/get-env`;
|
||||
const apiRes = await fetch(apiUrl);
|
||||
|
||||
const localhostNoProtocol = localhost[0].slice('http://'.length);
|
||||
|
||||
const apiJson = await apiRes.json();
|
||||
t.is(apiJson['VERCEL'], '1');
|
||||
t.is(apiJson['VERCEL_URL'], localhostNoProtocol);
|
||||
t.is(apiJson['VERCEL_ENV'], 'development');
|
||||
t.is(apiJson['VERCEL_REGION'], 'dev1');
|
||||
t.is(apiJson['VERCEL_GIT_PROVIDER'], '');
|
||||
t.is(apiJson['VERCEL_GIT_REPO_SLUG'], '');
|
||||
|
||||
const homeUrl = localhost[0];
|
||||
const homeRes = await fetch(homeUrl);
|
||||
const homeJson = await homeRes.json();
|
||||
t.is(homeJson['VERCEL'], '1');
|
||||
t.is(homeJson['VERCEL_URL'], localhostNoProtocol);
|
||||
t.is(homeJson['MY_PLAIN_VAR'], 'hello');
|
||||
t.is(homeJson['VERCEL_ENV'], 'development');
|
||||
t.is(homeJson['VERCEL_REGION'], undefined);
|
||||
t.is(homeJson['VERCEL_GIT_PROVIDER'], '');
|
||||
t.is(homeJson['VERCEL_GIT_REPO_SLUG'], '');
|
||||
|
||||
vc.kill('SIGTERM', { forceKillAfterTimeout: 2000 });
|
||||
|
||||
@@ -738,12 +901,27 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
await waitForPrompt(now, chunk =>
|
||||
chunk.includes('What’s the name of the variable?')
|
||||
);
|
||||
now.stdin.write('MY_ENV_VAR\n');
|
||||
now.stdin.write('MY_PLAINTEXT_ENV_VAR\n');
|
||||
|
||||
// expect error if no environment is selected
|
||||
await waitForPrompt(
|
||||
now,
|
||||
chunk =>
|
||||
chunk.includes('which Environments') &&
|
||||
chunk.includes('MY_PLAINTEXT_ENV_VAR')
|
||||
);
|
||||
now.stdin.write('\n'); // select none
|
||||
await waitForPrompt(now, chunk =>
|
||||
chunk.includes(
|
||||
'Please select an Environment to remove the Environment Variable from.'
|
||||
)
|
||||
);
|
||||
|
||||
await waitForPrompt(
|
||||
now,
|
||||
chunk =>
|
||||
chunk.includes('which Environments') && chunk.includes('MY_ENV_VAR')
|
||||
chunk.includes('which Environments') &&
|
||||
chunk.includes('MY_PLAINTEXT_ENV_VAR')
|
||||
);
|
||||
now.stdin.write('a\n'); // select all
|
||||
|
||||
@@ -755,7 +933,7 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
async function nowEnvRemoveWithArgs() {
|
||||
const { exitCode, stderr, stdout } = await execa(
|
||||
binaryPath,
|
||||
['env', 'rm', 'MY_STDIN_VAR', 'development', '-y', ...defaultArgs],
|
||||
['env', 'rm', 'MY_SECRET_ENV_VAR', 'preview', '-y', ...defaultArgs],
|
||||
{
|
||||
reject: false,
|
||||
cwd: target,
|
||||
@@ -763,12 +941,49 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
);
|
||||
|
||||
t.is(exitCode, 0, formatOutput({ stderr, stdout }));
|
||||
|
||||
const {
|
||||
exitCode: exitCode2,
|
||||
stderr: stderr2,
|
||||
stdout: stdout2,
|
||||
} = await execa(
|
||||
binaryPath,
|
||||
['env', 'rm', 'MY_STDIN_VAR', 'development', '-y', ...defaultArgs],
|
||||
{
|
||||
reject: false,
|
||||
cwd: target,
|
||||
}
|
||||
);
|
||||
|
||||
t.is(exitCode2, 0, formatOutput({ stderr2, stdout2 }));
|
||||
|
||||
const {
|
||||
exitCode: exitCode3,
|
||||
stderr: stderr3,
|
||||
stdout: stdout3,
|
||||
} = await execa(
|
||||
binaryPath,
|
||||
[
|
||||
'env',
|
||||
'rm',
|
||||
'MY_DECRYPTABLE_SECRET_ENV',
|
||||
'development',
|
||||
'-y',
|
||||
...defaultArgs,
|
||||
],
|
||||
{
|
||||
reject: false,
|
||||
cwd: target,
|
||||
}
|
||||
);
|
||||
|
||||
t.is(exitCode3, 0, formatOutput({ stderr3, stdout3 }));
|
||||
}
|
||||
|
||||
async function nowEnvRemoveWithNameOnly() {
|
||||
const vc = execa(
|
||||
binaryPath,
|
||||
['env', 'rm', 'VERCEL_URL', '-y', ...defaultArgs],
|
||||
['env', 'rm', 'NEXT_PUBLIC_VERCEL_URL', '-y', ...defaultArgs],
|
||||
{
|
||||
reject: false,
|
||||
cwd: target,
|
||||
@@ -778,7 +993,8 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
await waitForPrompt(
|
||||
vc,
|
||||
chunk =>
|
||||
chunk.includes('which Environments') && chunk.includes('VERCEL_URL')
|
||||
chunk.includes('which Environments') &&
|
||||
chunk.includes('NEXT_PUBLIC_VERCEL_URL')
|
||||
);
|
||||
vc.stdin.write('a\n'); // select all
|
||||
|
||||
@@ -787,18 +1003,25 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
|
||||
}
|
||||
|
||||
await nowDeploy();
|
||||
const secretName = await createSecret();
|
||||
await nowEnvLsIsEmpty();
|
||||
await nowEnvAdd();
|
||||
await nowEnvAddPlaintext();
|
||||
await nowEnvAddSecret(secretName);
|
||||
await nowEnvAddFromStdin();
|
||||
await nowEnvAddSystemEnv();
|
||||
await withPlainTextEnv(nowEnvLsIncludesVar)();
|
||||
await withPlainTextEnv(nowEnvPull)();
|
||||
await nowEnvLsIncludesVar();
|
||||
await createEnvWithDecryptableSecret();
|
||||
await nowEnvPull();
|
||||
await nowEnvPullOverwrite();
|
||||
await nowEnvPullConfirm();
|
||||
await nowDeployWithVar();
|
||||
await nowDevWithEnv();
|
||||
fs.unlinkSync(path.join(target, '.env'));
|
||||
await withPlainTextEnv(nowDevAndFetchCloudVars)();
|
||||
await nowDevAndFetchCloudVars();
|
||||
await enableAutoExposeSystemEnvs();
|
||||
await nowEnvPullFetchSystemVars();
|
||||
fs.unlinkSync(path.join(target, '.env'));
|
||||
await nowDevAndFetchSystemVars();
|
||||
await nowEnvRemove();
|
||||
await nowEnvRemoveWithArgs();
|
||||
await nowEnvRemoveWithNameOnly();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "9.0.4-canary.0",
|
||||
"version": "9.0.5-canary.0",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"homepage": "https://vercel.com",
|
||||
@@ -37,7 +37,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.5.5-canary.0",
|
||||
"@vercel/build-utils": "2.6.1-canary.0",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "3.0.0",
|
||||
|
||||
@@ -136,6 +136,7 @@ export async function getVercelIgnore(
|
||||
'__pycache__',
|
||||
'venv',
|
||||
'CVS',
|
||||
'.vercel_build_output',
|
||||
];
|
||||
|
||||
const cwds = Array.isArray(cwd) ? cwd : [cwd];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/next",
|
||||
"version": "2.6.36",
|
||||
"version": "2.7.5-canary.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
|
||||
@@ -26,7 +26,7 @@
|
||||
"@types/resolve-from": "5.0.1",
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/yazl": "2.4.1",
|
||||
"@vercel/nft": "0.9.2",
|
||||
"@vercel/nft": "0.9.4",
|
||||
"async-sema": "3.0.1",
|
||||
"buffer-crc32": "0.2.13",
|
||||
"escape-string-regexp": "2.0.0",
|
||||
|
||||
@@ -214,7 +214,7 @@ function startDevServer(entryPath: string, runtimeEnv: EnvConfig) {
|
||||
return { forked, getUrl };
|
||||
}
|
||||
|
||||
export const build = async ({
|
||||
export async function build({
|
||||
files,
|
||||
workPath,
|
||||
repoRootPath,
|
||||
@@ -231,7 +231,7 @@ export const build = async ({
|
||||
}>;
|
||||
watch?: string[];
|
||||
childProcesses: ChildProcess[];
|
||||
}> => {
|
||||
}> {
|
||||
validateEntrypoint(entrypoint);
|
||||
|
||||
// Limit for max size each lambda can be, 50 MB if no custom limit
|
||||
@@ -352,7 +352,7 @@ export const build = async ({
|
||||
'now-build',
|
||||
'build',
|
||||
]);
|
||||
const { buildCommand } = config;
|
||||
const { installCommand, buildCommand } = config;
|
||||
|
||||
if (!buildScriptName && !buildCommand) {
|
||||
console.log(
|
||||
@@ -376,8 +376,22 @@ export const build = async ({
|
||||
await writeNpmRc(entryPath, process.env.NPM_AUTH_TOKEN);
|
||||
}
|
||||
|
||||
console.log('Installing dependencies...');
|
||||
await runNpmInstall(entryPath, [], spawnOpts, meta);
|
||||
if (typeof installCommand === 'string') {
|
||||
if (installCommand.trim()) {
|
||||
console.log(`Running "install" command: \`${installCommand}\`...`);
|
||||
await execCommand(installCommand, {
|
||||
...spawnOpts,
|
||||
cwd: entryPath,
|
||||
});
|
||||
} else {
|
||||
console.log(`Skipping "install" command...`);
|
||||
}
|
||||
} else {
|
||||
console.log('Installing dependencies...');
|
||||
const installTime = Date.now();
|
||||
await runNpmInstall(entryPath, [], spawnOpts, meta);
|
||||
debug(`Install complete [${Date.now() - installTime}ms]`);
|
||||
}
|
||||
|
||||
// Refetch Next version now that dependencies are installed.
|
||||
// This will now resolve the actual installed Next version,
|
||||
@@ -421,7 +435,7 @@ export const build = async ({
|
||||
});
|
||||
}
|
||||
|
||||
const appMountPrefixNoTrailingSlash = path.posix
|
||||
let appMountPrefixNoTrailingSlash = path.posix
|
||||
.join('/', entryDirectory)
|
||||
.replace(/\/+$/, '');
|
||||
|
||||
@@ -434,7 +448,7 @@ export const build = async ({
|
||||
const prerenderManifest = await getPrerenderManifest(entryPath);
|
||||
const headers: Route[] = [];
|
||||
const rewrites: Route[] = [];
|
||||
const redirects: Route[] = [];
|
||||
let redirects: Route[] = [];
|
||||
const dataRoutes: Route[] = [];
|
||||
let dynamicRoutes: Route[] = [];
|
||||
// whether they have enabled pages/404.js as the custom 404 page
|
||||
@@ -470,6 +484,30 @@ export const build = async ({
|
||||
headers.push(...convertHeaders(routesManifest.headers));
|
||||
}
|
||||
|
||||
if (routesManifest.basePath && routesManifest.basePath !== '/') {
|
||||
const nextBasePath = routesManifest.basePath;
|
||||
|
||||
if (!nextBasePath.startsWith('/')) {
|
||||
throw new NowBuildError({
|
||||
code: 'NEXT_BASEPATH_STARTING_SLASH',
|
||||
message:
|
||||
'basePath must start with `/`. Please upgrade your `@vercel/next` builder and try again. Contact support if this continues to happen.',
|
||||
});
|
||||
}
|
||||
if (nextBasePath.endsWith('/')) {
|
||||
throw new NowBuildError({
|
||||
code: 'NEXT_BASEPATH_TRAILING_SLASH',
|
||||
message:
|
||||
'basePath must not end with `/`. Please upgrade your `@vercel/next` builder and try again. Contact support if this continues to happen.',
|
||||
});
|
||||
}
|
||||
|
||||
entryDirectory = path.join(entryDirectory, nextBasePath);
|
||||
appMountPrefixNoTrailingSlash = path.posix
|
||||
.join('/', entryDirectory)
|
||||
.replace(/\/+$/, '');
|
||||
}
|
||||
|
||||
if (routesManifest.dataRoutes) {
|
||||
// Load the /_next/data routes for both dynamic SSG and SSP pages.
|
||||
// These must be combined and sorted to prevent conflicts
|
||||
@@ -554,26 +592,6 @@ export const build = async ({
|
||||
hasPages404 = true;
|
||||
}
|
||||
|
||||
if (routesManifest.basePath && routesManifest.basePath !== '/') {
|
||||
const nextBasePath = routesManifest.basePath;
|
||||
|
||||
if (!nextBasePath.startsWith('/')) {
|
||||
throw new NowBuildError({
|
||||
code: 'NEXT_BASEPATH_STARTING_SLASH',
|
||||
message:
|
||||
'basePath must start with `/`. Please upgrade your `@vercel/next` builder and try again. Contact support if this continues to happen.',
|
||||
});
|
||||
}
|
||||
if (nextBasePath.endsWith('/')) {
|
||||
throw new NowBuildError({
|
||||
code: 'NEXT_BASEPATH_TRAILING_SLASH',
|
||||
message:
|
||||
'basePath must not end with `/`. Please upgrade your `@vercel/next` builder and try again. Contact support if this continues to happen.',
|
||||
});
|
||||
}
|
||||
|
||||
entryDirectory = path.join(entryDirectory, nextBasePath);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -700,6 +718,11 @@ export const build = async ({
|
||||
// with that routing section
|
||||
...rewrites,
|
||||
|
||||
// make sure 404 page is used when a directory is matched without
|
||||
// an index page
|
||||
{ handle: 'resource' },
|
||||
{ src: path.join('/', entryDirectory, '.*'), status: 404 },
|
||||
|
||||
// We need to make sure to 404 for /_next after handle: miss since
|
||||
// handle: miss is called before rewrites and to prevent rewriting
|
||||
// /_next
|
||||
@@ -728,7 +751,7 @@ export const build = async ({
|
||||
src: path.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
'_next/static/(?:[^/]+/pages|pages|chunks|runtime|css|media)/.+'
|
||||
`_next/static/(?:[^/]+/pages|pages|chunks|runtime|css|media|${escapedBuildId})/.+`
|
||||
),
|
||||
// Next.js assets contain a hash or entropy in their filenames, so they
|
||||
// are guaranteed to be unique and cacheable indefinitely.
|
||||
@@ -1411,17 +1434,23 @@ export const build = async ({
|
||||
const isFallback = prerenderManifest.fallbackRoutes[pathname!];
|
||||
const isBlocking =
|
||||
prerenderManifest.blockingFallbackRoutes[pathname!];
|
||||
const isAutoExport =
|
||||
staticPages[
|
||||
addLocaleOrDefault(pathname!, routesManifest).substr(1)
|
||||
];
|
||||
|
||||
const isLocalePrefixed = isFallback || isBlocking || isAutoExport;
|
||||
|
||||
route.src = route.src.replace(
|
||||
'^',
|
||||
`^${dynamicPrefix ? `${dynamicPrefix}[/]?` : '[/]?'}(?${
|
||||
isFallback || isBlocking ? '<nextLocale>' : ':'
|
||||
isLocalePrefixed ? '<nextLocale>' : ':'
|
||||
}${i18n.locales
|
||||
.map(locale => escapeStringRegexp(locale))
|
||||
.join('|')})?`
|
||||
);
|
||||
|
||||
if (isFallback || isBlocking) {
|
||||
if (isLocalePrefixed) {
|
||||
// ensure destination has locale prefix to match prerender output
|
||||
// path so that the prerender object is used
|
||||
route.dest = route.dest!.replace(
|
||||
@@ -1745,12 +1774,7 @@ export const build = async ({
|
||||
if (nonDynamicSsg || isFallback) {
|
||||
outputPathData = outputPathData.replace(
|
||||
new RegExp(`${escapeStringRegexp(origRouteFileNoExt)}.json$`),
|
||||
`${routeFileNoExt}${
|
||||
routeFileNoExt !== origRouteFileNoExt &&
|
||||
origRouteFileNoExt === '/index'
|
||||
? '/index'
|
||||
: ''
|
||||
}.json`
|
||||
`${routeFileNoExt}.json`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1996,6 +2020,29 @@ export const build = async ({
|
||||
|
||||
const { i18n } = routesManifest || {};
|
||||
|
||||
const trailingSlashRedirects: Route[] = [];
|
||||
|
||||
redirects = redirects.filter(_redir => {
|
||||
const redir = _redir as Source;
|
||||
// detect the trailing slash redirect and make sure it's
|
||||
// kept above the wildcard mapping to prevent erroneous redirects
|
||||
// since non-continue routes come after continue the $wildcard
|
||||
// route will come before the redirect otherwise and if the
|
||||
// redirect is triggered it breaks locale mapping
|
||||
|
||||
const location =
|
||||
redir.headers && (redir.headers.location || redir.headers.Location);
|
||||
|
||||
if (redir.status === 308 && (location === '/$1' || location === '/$1/')) {
|
||||
// we set continue here to prevent the redirect from
|
||||
// moving underneath i18n routes
|
||||
redir.continue = true;
|
||||
trailingSlashRedirects.push(redir);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
return {
|
||||
output: {
|
||||
...publicDirectoryFiles,
|
||||
@@ -2035,36 +2082,15 @@ export const build = async ({
|
||||
- Builder rewrites
|
||||
*/
|
||||
routes: [
|
||||
// headers
|
||||
...headers,
|
||||
|
||||
// redirects
|
||||
...redirects.map(_redir => {
|
||||
if (i18n) {
|
||||
const redir = _redir as Source;
|
||||
// detect the trailing slash redirect and make sure it's
|
||||
// kept above the wildcard mapping to prevent erroneous redirects
|
||||
// since non-continue routes come after continue the $wildcard
|
||||
// route will come before the redirect otherwise and if the
|
||||
// redirect is triggered it breaks locale mapping
|
||||
|
||||
const location =
|
||||
redir.headers && (redir.headers.location || redir.headers.Location);
|
||||
|
||||
if (
|
||||
redir.status === 308 &&
|
||||
(location === '/$1' || location === '/$1/')
|
||||
) {
|
||||
// we set continue true
|
||||
redir.continue = true;
|
||||
}
|
||||
}
|
||||
return _redir;
|
||||
}),
|
||||
// force trailingSlashRedirect to the very top so it doesn't
|
||||
// conflict with i18n routes that don't have or don't have the
|
||||
// trailing slash
|
||||
...trailingSlashRedirects,
|
||||
|
||||
...(i18n
|
||||
? [
|
||||
// Handle auto-adding current default locale to path based on $wildcard
|
||||
// Handle auto-adding current default locale to path based on
|
||||
// $wildcard
|
||||
{
|
||||
src: `^${path.join(
|
||||
'/',
|
||||
@@ -2073,8 +2099,8 @@ export const build = async ({
|
||||
)}(?!(?:_next/.*|${i18n.locales
|
||||
.map(locale => escapeStringRegexp(locale))
|
||||
.join('|')})(?:/.*|$))(.*)$`,
|
||||
// TODO: this needs to contain or not contain a trailing slash
|
||||
// to prevent the trailing slash redirect from being triggered
|
||||
// we aren't able to ensure trailing slash mode here
|
||||
// so ensure this comes after the trailing slash redirect
|
||||
dest: '$wildcard/$1',
|
||||
continue: true,
|
||||
},
|
||||
@@ -2083,8 +2109,6 @@ export const build = async ({
|
||||
...(i18n.domains && i18n.localeDetection !== false
|
||||
? [
|
||||
{
|
||||
// TODO: enable redirecting between domains, will require
|
||||
// updating the src with the desired locales to redirect
|
||||
src: `^${path.join(
|
||||
'/',
|
||||
entryDirectory
|
||||
@@ -2120,10 +2144,10 @@ export const build = async ({
|
||||
...(i18n.localeDetection !== false
|
||||
? [
|
||||
{
|
||||
// TODO: if default locale is included in this src it won't be
|
||||
// visitable by users who prefer another language since a
|
||||
// cookie isn't set signaling the default locale is preferred
|
||||
// on redirect currently, investigate adding this
|
||||
// TODO: if default locale is included in this src it won't
|
||||
// be visitable by users who prefer another language since a
|
||||
// cookie isn't set signaling the default locale is
|
||||
// preferred on redirect currently, investigate adding this
|
||||
src: '/',
|
||||
locale: {
|
||||
redirect: i18n.locales.reduce(
|
||||
@@ -2166,6 +2190,12 @@ export const build = async ({
|
||||
]
|
||||
: []),
|
||||
|
||||
// headers
|
||||
...headers,
|
||||
|
||||
// redirects
|
||||
...redirects,
|
||||
|
||||
// Make sure to 404 for the /404 path itself
|
||||
...(i18n
|
||||
? [
|
||||
@@ -2215,6 +2245,11 @@ export const build = async ({
|
||||
// with that routing section
|
||||
...rewrites,
|
||||
|
||||
// make sure 404 page is used when a directory is matched without
|
||||
// an index page
|
||||
{ handle: 'resource' },
|
||||
{ src: path.join('/', entryDirectory, '.*'), status: 404 },
|
||||
|
||||
// We need to make sure to 404 for /_next after handle: miss since
|
||||
// handle: miss is called before rewrites and to prevent rewriting /_next
|
||||
{ handle: 'miss' },
|
||||
@@ -2229,16 +2264,16 @@ export const build = async ({
|
||||
dest: '$0',
|
||||
},
|
||||
|
||||
// remove default locale prefix to check public files
|
||||
// remove locale prefixes to check public files
|
||||
...(i18n
|
||||
? [
|
||||
{
|
||||
src: `${path.join(
|
||||
src: `^${path.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
i18n.defaultLocale,
|
||||
'/'
|
||||
)}(.*)`,
|
||||
entryDirectory
|
||||
)}/?(?:${i18n.locales
|
||||
.map(locale => escapeStringRegexp(locale))
|
||||
.join('|')})/(.*)`,
|
||||
dest: `${path.join('/', entryDirectory, '/')}$1`,
|
||||
check: true,
|
||||
},
|
||||
@@ -2286,7 +2321,7 @@ export const build = async ({
|
||||
src: path.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
'_next/static/(?:[^/]+/pages|pages|chunks|runtime|css|media)/.+'
|
||||
`_next/static/(?:[^/]+/pages|pages|chunks|runtime|css|media|${escapedBuildId})/.+`
|
||||
),
|
||||
// Next.js assets contain a hash or entropy in their filenames, so they
|
||||
// are guaranteed to be unique and cacheable indefinitely.
|
||||
@@ -2362,7 +2397,7 @@ export const build = async ({
|
||||
watch: [],
|
||||
childProcesses: [],
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const prepareCache = async ({
|
||||
workPath,
|
||||
|
||||
@@ -161,6 +161,47 @@
|
||||
"mustContain": "hello world!"
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "dynamic page"
|
||||
},
|
||||
{
|
||||
"path": "/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "\"en-US\""
|
||||
},
|
||||
{
|
||||
"path": "/en/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "dynamic page"
|
||||
},
|
||||
{
|
||||
"path": "/en/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "\"en\""
|
||||
},
|
||||
{
|
||||
"path": "/nl/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "dynamic page"
|
||||
},
|
||||
{
|
||||
"path": "/nl/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "\"nl\""
|
||||
},
|
||||
{
|
||||
"path": "/fr/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "dynamic page"
|
||||
},
|
||||
{
|
||||
"path": "/fr/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "\"fr\""
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/gsp",
|
||||
"status": 200,
|
||||
@@ -304,17 +345,21 @@
|
||||
"mustContain": "slug\":\"first\""
|
||||
},
|
||||
|
||||
// TODO: update when directory listing is disabled
|
||||
// and these are proper 404s
|
||||
{
|
||||
"path": "/en/not-found",
|
||||
"status": 200,
|
||||
"mustContain": "Index of"
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"path": "/en/not-found",
|
||||
"mustContain": "lang=\"en\""
|
||||
},
|
||||
{
|
||||
"path": "/nl/not-found",
|
||||
"status": 200,
|
||||
"mustContain": "Index of"
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"path": "/nl/not-found",
|
||||
"mustContain": "lang=\"nl\""
|
||||
},
|
||||
{
|
||||
"path": "/en-US/not-found",
|
||||
@@ -386,22 +431,22 @@
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/en-US/index.json",
|
||||
"path": "/_next/data/testing-build-id/en-US.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"locale\":\"en-US\""
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/en/index.json",
|
||||
"path": "/_next/data/testing-build-id/en.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"locale\":\"en\""
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/fr/index.json",
|
||||
"path": "/_next/data/testing-build-id/fr.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"locale\":\"fr\""
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/nl/index.json",
|
||||
"path": "/_next/data/testing-build-id/nl.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"locale\":\"nl\""
|
||||
},
|
||||
|
||||
12
packages/now-next/test/fixtures/00-i18n-support-no-locale-detection/pages/dynamic/[slug].js
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Dynamic(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>dynamic page</p>
|
||||
<p id="query">{JSON.stringify(router.query)}</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -172,6 +172,47 @@
|
||||
"mustContain": "hello world!"
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "dynamic page"
|
||||
},
|
||||
{
|
||||
"path": "/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "\"en-US\""
|
||||
},
|
||||
{
|
||||
"path": "/en/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "dynamic page"
|
||||
},
|
||||
{
|
||||
"path": "/en/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "\"en\""
|
||||
},
|
||||
{
|
||||
"path": "/nl/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "dynamic page"
|
||||
},
|
||||
{
|
||||
"path": "/nl/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "\"nl\""
|
||||
},
|
||||
{
|
||||
"path": "/fr/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "dynamic page"
|
||||
},
|
||||
{
|
||||
"path": "/fr/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "\"fr\""
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/gsp",
|
||||
"status": 200,
|
||||
|
||||
12
packages/now-next/test/fixtures/00-i18n-support-no-shared-lambdas/pages/dynamic/[slug].js
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Dynamic(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>dynamic page</p>
|
||||
<p id="query">{JSON.stringify(router.query)}</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -4,13 +4,6 @@ const cheerio = require('cheerio');
|
||||
|
||||
module.exports = function (ctx) {
|
||||
it('should revalidate content properly from /', async () => {
|
||||
// we have to hit the _next/data URL first
|
||||
const dataRes = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/en-US.json`
|
||||
);
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/`);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
@@ -20,7 +13,7 @@ module.exports = function (ctx) {
|
||||
expect($('#router-locale').text()).toBe('en-US');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -32,13 +25,6 @@ module.exports = function (ctx) {
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /fr', async () => {
|
||||
// we have to hit the _next/data URL first
|
||||
const dataRes = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/fr.json`
|
||||
);
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/fr`);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
@@ -48,7 +34,7 @@ module.exports = function (ctx) {
|
||||
expect($('#router-locale').text()).toBe('fr');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/fr`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -60,13 +46,6 @@ module.exports = function (ctx) {
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /nl-NL', async () => {
|
||||
// we have to hit the _next/data URL first
|
||||
const dataRes = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/nl-NL/index.json`
|
||||
);
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/nl-NL`);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
@@ -76,7 +55,7 @@ module.exports = function (ctx) {
|
||||
expect($('#router-locale').text()).toBe('nl-NL');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/nl-NL`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -88,15 +67,6 @@ module.exports = function (ctx) {
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /second', async () => {
|
||||
// we have to hit the _next/data URL first
|
||||
const dataRes = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/en-US/second.json`
|
||||
);
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/second`);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
@@ -107,7 +77,7 @@ module.exports = function (ctx) {
|
||||
expect($('#router-locale').text()).toBe('en-US');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/second`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -119,15 +89,6 @@ module.exports = function (ctx) {
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /fr/second', async () => {
|
||||
// we have to hit the _next/data URL first
|
||||
const dataRes = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/fr/second.json`
|
||||
);
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/fr/second`);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
@@ -138,7 +99,7 @@ module.exports = function (ctx) {
|
||||
expect($('#router-locale').text()).toBe('fr');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/fr/second`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -150,15 +111,6 @@ module.exports = function (ctx) {
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /nl-NL/second', async () => {
|
||||
// we have to hit the _next/data URL first
|
||||
const dataRes = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/nl-NL/second.json`
|
||||
);
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/nl-NL/second`);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
@@ -169,7 +121,7 @@ module.exports = function (ctx) {
|
||||
expect($('#router-locale').text()).toBe('nl-NL');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/nl-NL/second`);
|
||||
expect(res2.status).toBe(200);
|
||||
|
||||
@@ -5,12 +5,12 @@ const cheerio = require('cheerio');
|
||||
module.exports = function (ctx) {
|
||||
it('should revalidate content properly from /', async () => {
|
||||
const dataRes = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/en-US/index.json`
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/en-US.json`
|
||||
);
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -22,7 +22,7 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({});
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -36,12 +36,12 @@ module.exports = function (ctx) {
|
||||
|
||||
it('should revalidate content properly from /fr', async () => {
|
||||
const dataRes = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/fr/index.json`
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/fr.json`
|
||||
);
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/fr`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -53,7 +53,7 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({});
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/fr`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -67,12 +67,12 @@ module.exports = function (ctx) {
|
||||
|
||||
it('should revalidate content properly from /nl-NL', async () => {
|
||||
const dataRes = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/nl-NL/index.json`
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/nl-NL.json`
|
||||
);
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/nl-NL`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -84,7 +84,7 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({});
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/nl-NL`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -104,7 +104,7 @@ module.exports = function (ctx) {
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/gsp/fallback/first`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -118,7 +118,7 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/gsp/fallback/first`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -139,7 +139,7 @@ module.exports = function (ctx) {
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/fr/gsp/fallback/first`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -153,7 +153,7 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/fr/gsp/fallback/first`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -174,7 +174,7 @@ module.exports = function (ctx) {
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/nl-NL/gsp/fallback/first`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -188,7 +188,7 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/nl-NL/gsp/fallback/first`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -212,7 +212,7 @@ module.exports = function (ctx) {
|
||||
const initRes = await fetch(`${ctx.deploymentUrl}/gsp/fallback/new-page`);
|
||||
expect(initRes.status).toBe(200);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/gsp/fallback/new-page`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -226,7 +226,7 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'new-page' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/gsp/fallback/new-page`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -246,7 +246,7 @@ module.exports = function (ctx) {
|
||||
);
|
||||
expect(dataRes.status).toBe(200);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/fr/gsp/fallback/new-page`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -260,7 +260,7 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'new-page' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/fr/gsp/fallback/new-page`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -278,7 +278,7 @@ module.exports = function (ctx) {
|
||||
);
|
||||
expect(dataRes.status).toBe(200);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/nl-NL/gsp/fallback/new-page`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -292,7 +292,7 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'new-page' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(
|
||||
`${ctx.deploymentUrl}/nl-NL/gsp/fallback/new-page`
|
||||
@@ -314,7 +314,7 @@ module.exports = function (ctx) {
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/gsp/no-fallback/first`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -327,7 +327,7 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/gsp/no-fallback/first`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -347,7 +347,7 @@ module.exports = function (ctx) {
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/fr/gsp/no-fallback/first`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -360,7 +360,7 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'first' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/fr/gsp/no-fallback/first`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -380,7 +380,7 @@ module.exports = function (ctx) {
|
||||
expect(dataRes.status).toBe(200);
|
||||
await dataRes.json();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}/nl-NL/gsp/no-fallback/second`
|
||||
@@ -395,7 +395,7 @@ module.exports = function (ctx) {
|
||||
expect(JSON.parse($('#router-query').text())).toEqual({ slug: 'second' });
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(
|
||||
`${ctx.deploymentUrl}/nl-NL/gsp/no-fallback/second`
|
||||
|
||||
@@ -18,4 +18,81 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
},
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: '/en-US/redirect-1',
|
||||
destination: '/somewhere-else',
|
||||
permanent: false,
|
||||
locale: false,
|
||||
},
|
||||
{
|
||||
source: '/nl/redirect-2',
|
||||
destination: '/somewhere-else',
|
||||
permanent: false,
|
||||
locale: false,
|
||||
},
|
||||
{
|
||||
source: '/redirect-3',
|
||||
destination: '/somewhere-else',
|
||||
permanent: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/en-US/rewrite-1',
|
||||
destination: '/another',
|
||||
locale: false,
|
||||
},
|
||||
{
|
||||
source: '/nl/rewrite-2',
|
||||
destination: '/nl/another',
|
||||
locale: false,
|
||||
},
|
||||
{
|
||||
source: '/fr/rewrite-3',
|
||||
destination: '/nl/another',
|
||||
locale: false,
|
||||
},
|
||||
{
|
||||
source: '/rewrite-4',
|
||||
destination: '/another',
|
||||
},
|
||||
];
|
||||
},
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/en-US/add-header-1',
|
||||
locale: false,
|
||||
headers: [
|
||||
{
|
||||
key: 'x-hello',
|
||||
value: 'world',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/nl/add-header-2',
|
||||
locale: false,
|
||||
headers: [
|
||||
{
|
||||
key: 'x-hello',
|
||||
value: 'world',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/add-header-3',
|
||||
headers: [
|
||||
{
|
||||
key: 'x-hello',
|
||||
value: 'world',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
@@ -169,6 +169,47 @@
|
||||
"mustContain": "hello world!"
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "dynamic page"
|
||||
},
|
||||
{
|
||||
"path": "/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "\"en-US\""
|
||||
},
|
||||
{
|
||||
"path": "/en/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "dynamic page"
|
||||
},
|
||||
{
|
||||
"path": "/en/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "\"en\""
|
||||
},
|
||||
{
|
||||
"path": "/nl/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "dynamic page"
|
||||
},
|
||||
{
|
||||
"path": "/nl/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "\"nl\""
|
||||
},
|
||||
{
|
||||
"path": "/fr/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "dynamic page"
|
||||
},
|
||||
{
|
||||
"path": "/fr/dynamic/hello",
|
||||
"status": 200,
|
||||
"mustContain": "\"fr\""
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/gsp",
|
||||
"status": 200,
|
||||
@@ -312,17 +353,21 @@
|
||||
"mustContain": "slug\":\"first\""
|
||||
},
|
||||
|
||||
// TODO: update when directory listing is disabled
|
||||
// and these are proper 404s
|
||||
{
|
||||
"path": "/en/not-found",
|
||||
"status": 200,
|
||||
"mustContain": "Index of"
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"path": "/en/not-found",
|
||||
"mustContain": "lang=\"en\""
|
||||
},
|
||||
{
|
||||
"path": "/nl/not-found",
|
||||
"status": 200,
|
||||
"mustContain": "Index of"
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"path": "/nl/not-found",
|
||||
"mustContain": "lang=\"nl\""
|
||||
},
|
||||
{
|
||||
"path": "/en-US/not-found",
|
||||
@@ -394,22 +439,22 @@
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/en-US/index.json",
|
||||
"path": "/_next/data/testing-build-id/en-US.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"locale\":\"en-US\""
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/en/index.json",
|
||||
"path": "/_next/data/testing-build-id/en.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"locale\":\"en\""
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/fr/index.json",
|
||||
"path": "/_next/data/testing-build-id/fr.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"locale\":\"fr\""
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/nl/index.json",
|
||||
"path": "/_next/data/testing-build-id/nl.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"locale\":\"nl\""
|
||||
},
|
||||
@@ -479,6 +524,280 @@
|
||||
"path": "/_next/data/testing-build-id/fr/gsp/blocking/first.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"catchall\":\"yes\""
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/en-US/redirect-1",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//somewhere-else/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/en/redirect-1",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"path": "/nl/redirect-2",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//somewhere-else/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/en-US/redirect-2",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"path": "/redirect-3",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//somewhere-else/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/en-US/redirect-3",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//somewhere-else/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/fr/redirect-3",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//somewhere-else/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/nl-NL/redirect-3",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"location": "//somewhere-else/"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/en-US/rewrite-1",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 200,
|
||||
"mustContain": "another page"
|
||||
},
|
||||
{
|
||||
"path": "/en-US/rewrite-1",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 200,
|
||||
"mustContain": "lang=\"en-US\""
|
||||
},
|
||||
{
|
||||
"path": "/nl/rewrite-1",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"path": "/nl/rewrite-2",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 200,
|
||||
"mustContain": "another page"
|
||||
},
|
||||
{
|
||||
"path": "/nl/rewrite-2",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 200,
|
||||
"mustContain": "lang=\"nl\""
|
||||
},
|
||||
{
|
||||
"path": "/rewrite-2",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"path": "/fr/rewrite-3",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 200,
|
||||
"mustContain": "another page"
|
||||
},
|
||||
{
|
||||
"path": "/fr/rewrite-3",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 200,
|
||||
"mustContain": "lang=\"nl\""
|
||||
},
|
||||
{
|
||||
"path": "/rewrite-3",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"path": "/rewrite-4",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 200,
|
||||
"mustContain": "another page"
|
||||
},
|
||||
{
|
||||
"path": "/rewrite-4",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 200,
|
||||
"mustContain": "lang=\"en-US\""
|
||||
},
|
||||
{
|
||||
"path": "/en/rewrite-4",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 200,
|
||||
"mustContain": "another page"
|
||||
},
|
||||
{
|
||||
"path": "/en/rewrite-4",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 200,
|
||||
"mustContain": "lang=\"en\""
|
||||
},
|
||||
{
|
||||
"path": "/fr/rewrite-4",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 200,
|
||||
"mustContain": "another page"
|
||||
},
|
||||
{
|
||||
"path": "/fr/rewrite-4",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 200,
|
||||
"mustContain": "lang=\"fr\""
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/en-US/add-header-1",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 404,
|
||||
"responseHeaders": {
|
||||
"x-hello": "world"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/en/add-header-1",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 404,
|
||||
"responseHeaders": {
|
||||
"x-hello": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/nl/add-header-2",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 404,
|
||||
"responseHeaders": {
|
||||
"x-hello": "world"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/en-US/add-header-2",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 404,
|
||||
"responseHeaders": {
|
||||
"x-hello": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/add-header-3",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 404,
|
||||
"responseHeaders": {
|
||||
"x-hello": "world"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/en-US/add-header-3",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 404,
|
||||
"responseHeaders": {
|
||||
"x-hello": "world"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/fr/add-header-3",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 404,
|
||||
"responseHeaders": {
|
||||
"x-hello": "world"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/nl-NL/add-header-3",
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"status": 404,
|
||||
"responseHeaders": {
|
||||
"x-hello": "world"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
12
packages/now-next/test/fixtures/00-i18n-support/pages/dynamic/[slug].js
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function Dynamic(props) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>dynamic page</p>
|
||||
<p id="query">{JSON.stringify(router.query)}</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -18,7 +18,7 @@ module.exports = function (ctx) {
|
||||
const props = await getProps('/', { params: {} });
|
||||
expect(props.params).toEqual({});
|
||||
|
||||
await waitFor(2000);
|
||||
await waitFor(4000);
|
||||
await getProps('/');
|
||||
|
||||
const newProps = await getProps('/', { params: {} });
|
||||
@@ -30,7 +30,7 @@ module.exports = function (ctx) {
|
||||
const props = await getProps('/a');
|
||||
expect(props.params).toEqual({ slug: ['a'] });
|
||||
|
||||
await waitFor(2000);
|
||||
await waitFor(4000);
|
||||
await getProps('/a');
|
||||
|
||||
const newProps = await getProps('/a');
|
||||
@@ -42,7 +42,7 @@ module.exports = function (ctx) {
|
||||
const props = await getProps('/hello/world');
|
||||
expect(props.params).toEqual({ slug: ['hello', 'world'] });
|
||||
|
||||
await waitFor(2000);
|
||||
await waitFor(4000);
|
||||
await getProps('/hello/world');
|
||||
|
||||
const newProps = await getProps('/hello/world');
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
"cache-control": "public,max-age=31536000,immutable"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/static/testing-build-id/_buildManifest.js",
|
||||
"responseHeaders": {
|
||||
"cache-control": "public,max-age=31536000,immutable"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/static/invalid-build-id/pages/non-existent.js",
|
||||
"notResponseHeaders": {
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
"cache-control": "public,max-age=31536000,immutable"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/static/testing-build-id/_buildManifest.js",
|
||||
"responseHeaders": {
|
||||
"cache-control": "public,max-age=31536000,immutable"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"mustContain": "nextExport\":true"
|
||||
|
||||
@@ -55,6 +55,26 @@
|
||||
{
|
||||
"path": "/docs/blog/post-1/comments",
|
||||
"mustContain": "comments post: post-1"
|
||||
},
|
||||
{
|
||||
"path": "/docs/_next/data/testing-build-id/blog/post-1.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"post\""
|
||||
},
|
||||
{
|
||||
"path": "/docs/_next/data/testing-build-id/blog/post-2/comments.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"post\""
|
||||
},
|
||||
{
|
||||
"path": "/docs/_next/data/testing-build-id/blog-ssg/post-1.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"post\""
|
||||
},
|
||||
{
|
||||
"path": "/docs/_next/data/testing-build-id/blog-ssg/post-2/comments.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"post\""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
16
packages/now-next/test/fixtures/16-base-path/pages/blog-ssg/[post]/comments.js
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
export const getStaticProps = ({ params }) => ({
|
||||
props: {
|
||||
post: params.post,
|
||||
},
|
||||
});
|
||||
|
||||
export const getStaticPaths = () => {
|
||||
return {
|
||||
paths: [{ params: { post: 'post-1' } }, { params: { post: 'post-2' } }],
|
||||
fallback: true,
|
||||
};
|
||||
};
|
||||
|
||||
export default function Comment({ post }) {
|
||||
return `comments post: ${post}`;
|
||||
}
|
||||
16
packages/now-next/test/fixtures/16-base-path/pages/blog-ssg/[post]/index.js
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
export const getStaticProps = ({ params }) => ({
|
||||
props: {
|
||||
post: params.post,
|
||||
},
|
||||
});
|
||||
|
||||
export const getStaticPaths = () => {
|
||||
return {
|
||||
paths: [{ params: { post: 'post-1' } }, { params: { post: 'post-2' } }],
|
||||
fallback: true,
|
||||
};
|
||||
};
|
||||
|
||||
export default function Post({ post }) {
|
||||
return `index post: ${post}`;
|
||||
}
|
||||
@@ -13,7 +13,7 @@ module.exports = function (ctx) {
|
||||
expect($('#hello').text()).toBe('hello: world');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/another`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -34,7 +34,7 @@ module.exports = function (ctx) {
|
||||
expect($('#post').text()).toBe('Post: post-123');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/blog/post-123`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -56,7 +56,7 @@ module.exports = function (ctx) {
|
||||
expect($('#comment').text()).toBe('Comment: comment-321');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/blog/post-123/comment-321`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -82,7 +82,7 @@ module.exports = function (ctx) {
|
||||
expect(isNaN(initialRandom)).toBe(false);
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/another.json`
|
||||
@@ -111,7 +111,7 @@ module.exports = function (ctx) {
|
||||
expect(isNaN(initialRandom)).toBe(false);
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/blog/post-123.json`
|
||||
@@ -141,7 +141,7 @@ module.exports = function (ctx) {
|
||||
expect(isNaN(initialRandom)).toBe(false);
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/blog/post-123/comment-321.json`
|
||||
|
||||
@@ -5,7 +5,7 @@ const cheerio = require('cheerio');
|
||||
module.exports = function (ctx) {
|
||||
it('should revalidate content properly from dynamic pathname', async () => {
|
||||
// wait for revalidation to expire
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/regenerated/blue`);
|
||||
expect(res.status).toBe(200);
|
||||
@@ -15,7 +15,7 @@ module.exports = function (ctx) {
|
||||
expect($('#slug').text()).toBe('blue');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/regenerated/blue`);
|
||||
expect(res2.status).toBe(200);
|
||||
@@ -27,7 +27,7 @@ module.exports = function (ctx) {
|
||||
|
||||
it('should revalidate content properly from /_next/data dynamic pathname', async () => {
|
||||
// wait for revalidation to expire
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/regenerated/blue.json`
|
||||
@@ -40,7 +40,7 @@ module.exports = function (ctx) {
|
||||
expect(isNaN(initialTime)).toBe(false);
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
|
||||
const res2 = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/regenerated/blue.json`
|
||||
|
||||
1
packages/now-next/test/fixtures/32-custom-install-command/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
install.txt
|
||||
19
packages/now-next/test/fixtures/32-custom-install-command/install.js
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
const path = require('path');
|
||||
const { promises: fs } = require('fs');
|
||||
|
||||
async function main() {
|
||||
console.log('installCommand...');
|
||||
|
||||
await fs.writeFile(path.join(__dirname, 'install.txt'), `installCommand`);
|
||||
|
||||
console.log('Finished installing...');
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => {
|
||||
process.exit(0);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
3
packages/now-next/test/fixtures/32-custom-install-command/next.config.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
target: 'serverless',
|
||||
};
|
||||
10
packages/now-next/test/fixtures/32-custom-install-command/package.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "next build"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
15
packages/now-next/test/fixtures/32-custom-install-command/pages/index.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import path from 'path';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
export async function getStaticProps(context) {
|
||||
console.log(process.cwd());
|
||||
const installPath = path.join(process.cwd(), 'install.txt');
|
||||
const install = await fs.readFile(installPath, 'utf8');
|
||||
return {
|
||||
props: { install },
|
||||
};
|
||||
}
|
||||
|
||||
export default function ({ install }) {
|
||||
return <div>{install}</div>;
|
||||
}
|
||||
20
packages/now-next/test/fixtures/32-custom-install-command/vercel.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next",
|
||||
"config": {
|
||||
"zeroConfig": true,
|
||||
"installCommand": "node install.js && yarn",
|
||||
"buildCommand": "next build"
|
||||
}
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/",
|
||||
"status": 200,
|
||||
"mustContain": "installCommand"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/node",
|
||||
"version": "1.8.4",
|
||||
"version": "1.8.6-canary.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
||||
@@ -33,7 +33,7 @@
|
||||
"@types/etag": "1.8.0",
|
||||
"@types/test-listen": "1.1.0",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/nft": "0.9.2",
|
||||
"@vercel/nft": "0.9.4",
|
||||
"content-type": "1.0.4",
|
||||
"cookie": "0.4.0",
|
||||
"etag": "1.8.1",
|
||||
|
||||
@@ -104,12 +104,7 @@ async function downloadInstallAndBundle({
|
||||
} else {
|
||||
const installTime = Date.now();
|
||||
console.log('Installing dependencies...');
|
||||
await runNpmInstall(
|
||||
entrypointFsDirname,
|
||||
['--prefer-offline'],
|
||||
spawnOpts,
|
||||
meta
|
||||
);
|
||||
await runNpmInstall(entrypointFsDirname, [], spawnOpts, meta);
|
||||
debug(`Install complete [${Date.now() - installTime}ms]`);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/routing-utils",
|
||||
"version": "1.9.1-canary.1",
|
||||
"version": "1.9.2-canary.2",
|
||||
"description": "Vercel routing utilities",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -89,6 +89,7 @@ export function mergeRoutes({ userRoutes, builds }: MergeRoutesProps): Route[] {
|
||||
|
||||
const outputRoutes: Route[] = [];
|
||||
const uniqueHandleValues = new Set([
|
||||
null,
|
||||
...userHandleMap.keys(),
|
||||
...builderHandleMap.keys(),
|
||||
]);
|
||||
|
||||
@@ -80,6 +80,9 @@ export const routesSchema = {
|
||||
check: {
|
||||
type: 'boolean',
|
||||
},
|
||||
important: {
|
||||
type: 'boolean',
|
||||
},
|
||||
status: {
|
||||
type: 'integer',
|
||||
minimum: 100,
|
||||
|
||||
@@ -119,6 +119,9 @@ export function convertHeaders(headers: NowHeader[]): Route[] {
|
||||
export function convertTrailingSlash(enable: boolean, status = 308): Route[] {
|
||||
const routes: Route[] = [];
|
||||
if (enable) {
|
||||
routes.push({
|
||||
src: '^/\\.well-known(?:/.*)?$'
|
||||
});
|
||||
routes.push({
|
||||
src: '^/((?:[^/]+/)*[^/\\.]+)$',
|
||||
headers: { Location: '/$1/' },
|
||||
|
||||
@@ -72,6 +72,7 @@ describe('normalizeRoutes', () => {
|
||||
src: '^/missed-me$',
|
||||
headers: { 'Cache-Control': 'max-age=10' },
|
||||
continue: true,
|
||||
important: true,
|
||||
},
|
||||
{ handle: 'rewrite' },
|
||||
{ src: '^.*$', dest: '/somewhere' },
|
||||
|
||||
82
packages/now-routing-utils/test/merge.spec.js
vendored
@@ -1,4 +1,4 @@
|
||||
const { deepEqual } = require('assert');
|
||||
const { deepStrictEqual } = require('assert');
|
||||
const { mergeRoutes } = require('../dist/merge');
|
||||
|
||||
test('mergeRoutes simple', () => {
|
||||
@@ -10,7 +10,10 @@ test('mergeRoutes simple', () => {
|
||||
{
|
||||
use: '@now/node',
|
||||
entrypoint: 'api/home.js',
|
||||
routes: [{ src: '/node1', dest: '/n1' }, { src: '/node2', dest: '/n2' }],
|
||||
routes: [
|
||||
{ src: '/node1', dest: '/n1' },
|
||||
{ src: '/node2', dest: '/n2' },
|
||||
],
|
||||
},
|
||||
{
|
||||
use: '@now/python',
|
||||
@@ -30,7 +33,7 @@ test('mergeRoutes simple', () => {
|
||||
{ dest: '/py1', src: '/python1' },
|
||||
{ dest: '/py2', src: '/python2' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes handle filesystem user routes', () => {
|
||||
@@ -43,7 +46,10 @@ test('mergeRoutes handle filesystem user routes', () => {
|
||||
{
|
||||
use: '@now/node',
|
||||
entrypoint: 'api/home.js',
|
||||
routes: [{ src: '/node1', dest: '/n1' }, { src: '/node2', dest: '/n2' }],
|
||||
routes: [
|
||||
{ src: '/node1', dest: '/n1' },
|
||||
{ src: '/node2', dest: '/n2' },
|
||||
],
|
||||
},
|
||||
{
|
||||
use: '@now/python',
|
||||
@@ -64,7 +70,7 @@ test('mergeRoutes handle filesystem user routes', () => {
|
||||
{ handle: 'filesystem' },
|
||||
{ dest: '/u2', src: '/user2' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes handle filesystem build routes', () => {
|
||||
@@ -102,7 +108,7 @@ test('mergeRoutes handle filesystem build routes', () => {
|
||||
{ dest: '/n2', src: '/node2' },
|
||||
{ dest: '/py2', src: '/python2' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes handle filesystem both user and builds', () => {
|
||||
@@ -141,7 +147,7 @@ test('mergeRoutes handle filesystem both user and builds', () => {
|
||||
{ dest: '/n2', src: '/node2' },
|
||||
{ dest: '/py2', src: '/python2' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes continue true', () => {
|
||||
@@ -182,7 +188,7 @@ test('mergeRoutes continue true', () => {
|
||||
{ dest: '/py1', src: '/python1' },
|
||||
{ dest: '/py3', src: '/python3' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes check true', () => {
|
||||
@@ -223,7 +229,7 @@ test('mergeRoutes check true', () => {
|
||||
{ dest: '/py1', src: '/python1' },
|
||||
{ dest: '/py3', src: '/python3' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes check true, continue true, handle filesystem middle', () => {
|
||||
@@ -268,7 +274,7 @@ test('mergeRoutes check true, continue true, handle filesystem middle', () => {
|
||||
{ dest: '/n3', src: '/node3' },
|
||||
{ dest: '/py3', src: '/python3' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes check true, continue true, handle filesystem top', () => {
|
||||
@@ -306,7 +312,7 @@ test('mergeRoutes check true, continue true, handle filesystem top', () => {
|
||||
{ dest: '/n1', src: '/node1' },
|
||||
{ dest: '/py1', src: '/python1' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes multiple handle values', () => {
|
||||
@@ -359,5 +365,57 @@ test('mergeRoutes multiple handle values', () => {
|
||||
{ dest: '/u3', src: '/user3' },
|
||||
{ check: true, dest: '/py2', src: '/python2' },
|
||||
];
|
||||
deepEqual(actual, expected);
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('mergeRoutes ensure `handle: error` comes last', () => {
|
||||
const userRoutes = [];
|
||||
const builds = [
|
||||
{
|
||||
use: '@vercel/static-build',
|
||||
entrypoint: 'packge.json',
|
||||
routes: [
|
||||
{
|
||||
src: '^/home$',
|
||||
status: 301,
|
||||
headers: {
|
||||
Location: '/',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
use: '@vercel/zero-config-routes',
|
||||
entrypoint: '/',
|
||||
routes: [
|
||||
{
|
||||
handle: 'error',
|
||||
},
|
||||
{
|
||||
status: 404,
|
||||
src: '^/(?!.*api).*$',
|
||||
dest: '404.html',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const actual = mergeRoutes({ userRoutes, builds });
|
||||
const expected = [
|
||||
{
|
||||
status: 301,
|
||||
src: '^/home$',
|
||||
headers: {
|
||||
Location: '/',
|
||||
},
|
||||
},
|
||||
{
|
||||
handle: 'error',
|
||||
},
|
||||
{
|
||||
status: 404,
|
||||
src: '^/(?!.*api).*$',
|
||||
dest: '404.html',
|
||||
},
|
||||
];
|
||||
deepStrictEqual(actual, expected);
|
||||
});
|
||||
|
||||
@@ -671,6 +671,7 @@ test('convertHeaders', () => {
|
||||
test('convertTrailingSlash enabled', () => {
|
||||
const actual = convertTrailingSlash(true);
|
||||
const expected = [
|
||||
{ src: '^/\\.well-known(?:/.*)?$' },
|
||||
{
|
||||
src: '^/((?:[^/]+/)*[^/\\.]+)$',
|
||||
headers: { Location: '/$1/' },
|
||||
@@ -685,11 +686,23 @@ test('convertTrailingSlash enabled', () => {
|
||||
deepEqual(actual, expected);
|
||||
|
||||
const mustMatch = [
|
||||
[
|
||||
'/.well-known',
|
||||
'/.well-known/',
|
||||
'/.well-known/asdf',
|
||||
'/.well-known/asdf/',
|
||||
],
|
||||
['/dir', '/dir/foo', '/dir/foo/bar'],
|
||||
['/foo.html/', '/dir/foo.html/', '/dir/foo/bar.css/', '/dir/about.map.js/'],
|
||||
];
|
||||
|
||||
const mustNotMatch = [
|
||||
[
|
||||
'/swell-known',
|
||||
'/swell-known/',
|
||||
'/swell-known/asdf',
|
||||
'/swell-known/asdf/',
|
||||
],
|
||||
[
|
||||
'/',
|
||||
'/index.html',
|
||||
|
||||
3
packages/now-static-build/.gitignore
vendored
@@ -3,3 +3,6 @@ dist/
|
||||
# bypass all ignored files for the cache fixtures
|
||||
# because they contain node_modules and package-lock.json files
|
||||
!test/cache-fixtures/**
|
||||
|
||||
/src/bridge.ts
|
||||
/src/launcher.ts
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Copy shared dependencies
|
||||
bridge_defs="$(dirname $(pwd))/now-node-bridge/src/bridge.ts"
|
||||
launcher_defs="$(dirname $(pwd))/now-node/src/launcher.ts"
|
||||
|
||||
cp -v "$bridge_defs" src
|
||||
cp -v "$launcher_defs" src
|
||||
|
||||
# Start fresh
|
||||
rm -rf dist
|
||||
|
||||
tsc
|
||||
|
||||
# Build with `ncc`
|
||||
ncc build src/index.ts -e @vercel/build-utils -e @now/build-utils -o dist
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/static-build",
|
||||
"version": "0.17.10",
|
||||
"version": "0.18.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/build-step",
|
||||
@@ -19,6 +19,7 @@
|
||||
"prepublishOnly": "./build.sh"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/aws-lambda": "8.10.64",
|
||||
"@types/cross-spawn": "6.0.0",
|
||||
"@types/ms": "0.7.31",
|
||||
"@types/node-fetch": "2.5.4",
|
||||
|
||||
@@ -33,6 +33,7 @@ const {
|
||||
NowBuildError,
|
||||
} = buildUtils;
|
||||
import { Route, Source } from '@vercel/routing-utils';
|
||||
import { readBuildOutputDirectory } from './utils/read-build-output';
|
||||
import * as GatsbyUtils from './utils/gatsby';
|
||||
|
||||
const sleep = (n: number) => new Promise(resolve => setTimeout(resolve, n));
|
||||
@@ -110,7 +111,7 @@ function getScriptName(pkg: PackageJson, cmd: string, { zeroConfig }: Config) {
|
||||
}
|
||||
|
||||
function getCommand(
|
||||
name: 'build' | 'dev',
|
||||
name: 'install' | 'build' | 'dev',
|
||||
pkg: PackageJson | null,
|
||||
config: Config,
|
||||
framework: Framework | undefined
|
||||
@@ -119,7 +120,7 @@ function getCommand(
|
||||
return null;
|
||||
}
|
||||
|
||||
const propName = name === 'build' ? 'buildCommand' : 'devCommand';
|
||||
const propName = `${name}Command` as keyof Config;
|
||||
|
||||
if (typeof config[propName] === 'string') {
|
||||
return config[propName];
|
||||
@@ -134,7 +135,7 @@ function getCommand(
|
||||
}
|
||||
|
||||
if (framework) {
|
||||
return framework[propName] || null;
|
||||
return (framework as any)[propName] || null;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -269,6 +270,7 @@ export async function build({
|
||||
|
||||
const devCommand = getCommand('dev', pkg, config, framework);
|
||||
const buildCommand = getCommand('build', pkg, config, framework);
|
||||
const installCommand = getCommand('install', pkg, config, framework);
|
||||
|
||||
if (pkg || buildCommand) {
|
||||
const gemfilePath = path.join(workPath, 'Gemfile');
|
||||
@@ -279,18 +281,20 @@ export async function build({
|
||||
const routes: Route[] = [];
|
||||
|
||||
if (config.zeroConfig) {
|
||||
if (existsSync(gemfilePath) && !meta.isDev) {
|
||||
debug('Detected Gemfile, executing bundle install...');
|
||||
await runBundleInstall(workPath, [], undefined, meta);
|
||||
}
|
||||
if (existsSync(requirementsPath) && !meta.isDev) {
|
||||
debug('Detected requirements.txt, executing pip install...');
|
||||
await runPipInstall(
|
||||
workPath,
|
||||
['-r', requirementsPath],
|
||||
undefined,
|
||||
meta
|
||||
);
|
||||
if (typeof installCommand !== 'string') {
|
||||
if (existsSync(gemfilePath) && !meta.isDev) {
|
||||
debug('Detected Gemfile, executing bundle install...');
|
||||
await runBundleInstall(workPath, [], undefined, meta);
|
||||
}
|
||||
if (existsSync(requirementsPath) && !meta.isDev) {
|
||||
debug('Detected requirements.txt, executing pip install...');
|
||||
await runPipInstall(
|
||||
workPath,
|
||||
['-r', requirementsPath],
|
||||
undefined,
|
||||
meta
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const { HUGO_VERSION, ZOLA_VERSION, GUTENBERG_VERSION } = process.env;
|
||||
@@ -357,10 +361,22 @@ export async function build({
|
||||
if (meta.isDev) {
|
||||
debug('Skipping dependency installation because dev mode is enabled');
|
||||
} else {
|
||||
const installTime = Date.now();
|
||||
console.log('Installing dependencies...');
|
||||
await runNpmInstall(entrypointDir, ['--prefer-offline'], spawnOpts, meta);
|
||||
debug(`Install complete [${Date.now() - installTime}ms]`);
|
||||
if (typeof installCommand === 'string') {
|
||||
if (installCommand.trim()) {
|
||||
console.log(`Running "install" command: \`${installCommand}\`...`);
|
||||
await execCommand(installCommand, {
|
||||
...spawnOpts,
|
||||
cwd: entrypointDir,
|
||||
});
|
||||
} else {
|
||||
console.log(`Skipping "install" command...`);
|
||||
}
|
||||
} else {
|
||||
console.log('Installing dependencies...');
|
||||
const installTime = Date.now();
|
||||
await runNpmInstall(entrypointDir, [], spawnOpts, meta);
|
||||
debug(`Install complete [${Date.now() - installTime}ms]`);
|
||||
}
|
||||
}
|
||||
|
||||
if (pkg && (buildCommand || devCommand)) {
|
||||
@@ -375,7 +391,7 @@ export async function build({
|
||||
};
|
||||
|
||||
debug(
|
||||
`Added "${nodeBinPath}" to PATH env because a package.json file was found.`
|
||||
`Added "${nodeBinPath}" to PATH env because a package.json file was found`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -441,7 +457,7 @@ export async function build({
|
||||
);
|
||||
} else {
|
||||
if (meta.isDev) {
|
||||
debug(`WARN: A dev script is missing.`);
|
||||
debug(`WARN: A dev script is missing`);
|
||||
}
|
||||
|
||||
if (buildCommand) {
|
||||
@@ -489,31 +505,50 @@ export async function build({
|
||||
}
|
||||
}
|
||||
|
||||
validateDistDir(distPath);
|
||||
const extraOutputs = await readBuildOutputDirectory({ workPath });
|
||||
|
||||
if (framework) {
|
||||
const frameworkRoutes = await getFrameworkRoutes(
|
||||
framework,
|
||||
outputDirPrefix
|
||||
if (extraOutputs.routes) {
|
||||
routes.push(...extraOutputs.routes);
|
||||
}
|
||||
|
||||
if (extraOutputs.staticFiles) {
|
||||
output = Object.assign(
|
||||
{},
|
||||
extraOutputs.staticFiles,
|
||||
extraOutputs.functions
|
||||
);
|
||||
routes.push(...frameworkRoutes);
|
||||
}
|
||||
} else {
|
||||
// No need to verify the dist dir if there are other output files.
|
||||
if (!extraOutputs.functions) {
|
||||
validateDistDir(distPath);
|
||||
}
|
||||
|
||||
let ignore: string[] = [];
|
||||
if (config.zeroConfig && config.outputDirectory === '.') {
|
||||
ignore = [
|
||||
'.env',
|
||||
'.env.*',
|
||||
'.git/**',
|
||||
'.vercel/**',
|
||||
'node_modules/**',
|
||||
'yarn.lock',
|
||||
'package-lock.json',
|
||||
'package.json',
|
||||
];
|
||||
debug(`Using ignore: ${JSON.stringify(ignore)}`);
|
||||
if (framework && !extraOutputs.routes) {
|
||||
const frameworkRoutes = await getFrameworkRoutes(
|
||||
framework,
|
||||
outputDirPrefix
|
||||
);
|
||||
routes.push(...frameworkRoutes);
|
||||
}
|
||||
|
||||
let ignore: string[] = [];
|
||||
if (config.zeroConfig && config.outputDirectory === '.') {
|
||||
ignore = [
|
||||
'.env',
|
||||
'.env.*',
|
||||
'.git/**',
|
||||
'.vercel/**',
|
||||
'node_modules/**',
|
||||
'yarn.lock',
|
||||
'package-lock.json',
|
||||
'package.json',
|
||||
'.vercel_build_output',
|
||||
];
|
||||
debug(`Using ignore: ${JSON.stringify(ignore)}`);
|
||||
}
|
||||
output = await glob('**', { cwd: distPath, ignore }, mountpoint);
|
||||
Object.assign(output, extraOutputs.functions);
|
||||
}
|
||||
output = await glob('**', { cwd: distPath, ignore }, mountpoint);
|
||||
}
|
||||
|
||||
const watch = [path.join(mountpoint.replace(/^\.\/?/, ''), '**/*')];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PackageJson } from '@vercel/build-utils';
|
||||
import { constants, PathLike, promises as fs } from 'fs';
|
||||
import * as path from 'path';
|
||||
import { PackageJson } from '@vercel/build-utils';
|
||||
import path from 'path';
|
||||
|
||||
export type DeepWriteable<T> = {
|
||||
-readonly [P in keyof T]: DeepWriteable<T[P]>;
|
||||
@@ -38,3 +38,11 @@ export async function writePackageJson(
|
||||
JSON.stringify(packageJson, null, 2)
|
||||
);
|
||||
}
|
||||
|
||||
export function isObjectEmpty(object: { [key: string]: unknown }) {
|
||||
for (const _prop in object) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,15 @@ export async function injectVercelAnalyticsPlugin(dir: string) {
|
||||
gatsbyConfigPath,
|
||||
`const userConfig = require("./gatsby-config.js.__vercel_builder_backup__.js");
|
||||
|
||||
const vercelConfig = Object.assign({}, userConfig);
|
||||
// https://github.com/gatsbyjs/gatsby/blob/354003fb2908e02ff12109ca3a02978a5a6e608c/packages/gatsby/src/bootstrap/prefer-default.ts
|
||||
const preferDefault = m => (m && m.default) || m;
|
||||
|
||||
const vercelConfig = Object.assign(
|
||||
{},
|
||||
|
||||
// https://github.com/gatsbyjs/gatsby/blob/a6ecfb2b01d761e8a3612b8ea132c698659923d9/packages/gatsby/src/services/initialize.ts#L113-L117
|
||||
preferDefault(userConfig)
|
||||
);
|
||||
if (!vercelConfig.plugins) {
|
||||
vercelConfig.plugins = [];
|
||||
}
|
||||
|
||||
157
packages/now-static-build/src/utils/read-build-output.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import { FileBlob, Files, Lambda } from '@vercel/build-utils';
|
||||
import { isObjectEmpty } from './_shared';
|
||||
import { makeNowLauncher } from '../launcher';
|
||||
import { promises as fs } from 'fs';
|
||||
import { Route } from '@vercel/routing-utils';
|
||||
import buildUtils from '../build-utils';
|
||||
import path from 'path';
|
||||
|
||||
const { createLambda, debug, getLatestNodeVersion, glob } = buildUtils;
|
||||
|
||||
/**
|
||||
* Reads the .vercel_build_output directory and returns and object
|
||||
* that should be merged with the build outputs.
|
||||
*
|
||||
* At the moment only `functions/node` is supported for functions.
|
||||
*/
|
||||
export async function readBuildOutputDirectory({
|
||||
workPath,
|
||||
}: {
|
||||
workPath: string;
|
||||
}) {
|
||||
const functions: { [key: string]: Lambda } = {};
|
||||
const functionsMountPath = path.join('.vercel', 'functions');
|
||||
|
||||
Object.assign(
|
||||
functions,
|
||||
await readNodeFunctions({ workPath, functionsMountPath })
|
||||
);
|
||||
const staticFiles = await readStaticFiles({ workPath });
|
||||
|
||||
const routes = await readRoutesConfig({ workPath });
|
||||
|
||||
const outputs = {
|
||||
staticFiles: isObjectEmpty(staticFiles) ? null : staticFiles,
|
||||
functions: isObjectEmpty(functions) ? null : functions,
|
||||
routes: routes.length ? routes : null,
|
||||
};
|
||||
|
||||
if (outputs.functions) {
|
||||
console.log(
|
||||
`Detected Serverless Functions in ".vercel_build_output/functions"`
|
||||
);
|
||||
}
|
||||
|
||||
if (outputs.staticFiles) {
|
||||
console.log(`Detected Static Assets in ".vercel_build_output/static"`);
|
||||
}
|
||||
|
||||
if (outputs.routes) {
|
||||
console.log(`Detected Configuration in ".vercel_build_output/config"`);
|
||||
}
|
||||
|
||||
return outputs;
|
||||
}
|
||||
|
||||
async function readStaticFiles({
|
||||
workPath,
|
||||
}: {
|
||||
workPath: string;
|
||||
}): Promise<Files> {
|
||||
const staticFilePath = path.join(workPath, '.vercel_build_output', 'static');
|
||||
const staticFiles = await glob('**', {
|
||||
cwd: staticFilePath,
|
||||
});
|
||||
|
||||
return staticFiles;
|
||||
}
|
||||
|
||||
async function readNodeFunctions({
|
||||
workPath,
|
||||
functionsMountPath,
|
||||
}: {
|
||||
workPath: string;
|
||||
functionsMountPath: string;
|
||||
}) {
|
||||
const output: { [key: string]: Lambda } = {};
|
||||
const nodeFunctionPath = path.join(
|
||||
workPath,
|
||||
'.vercel_build_output',
|
||||
'functions',
|
||||
'node'
|
||||
);
|
||||
const nodeFunctionFiles = await glob('*/index.js', {
|
||||
cwd: nodeFunctionPath,
|
||||
});
|
||||
const nodeBridgeData = await fs.readFile(path.join(__dirname, 'bridge.js'));
|
||||
|
||||
for (const fileName of Object.keys(nodeFunctionFiles)) {
|
||||
const launcherFileName = '___now_launcher';
|
||||
const bridgeFileName = '___now_bridge';
|
||||
|
||||
const launcherFiles: Files = {
|
||||
[`${launcherFileName}.js`]: new FileBlob({
|
||||
data: makeNowLauncher({
|
||||
entrypointPath: `./index.js`,
|
||||
bridgePath: `./${bridgeFileName}`,
|
||||
helpersPath: '',
|
||||
sourcemapSupportPath: '',
|
||||
shouldAddHelpers: false,
|
||||
shouldAddSourcemapSupport: false,
|
||||
}),
|
||||
}),
|
||||
[`${bridgeFileName}.js`]: new FileBlob({
|
||||
data: nodeBridgeData,
|
||||
}),
|
||||
};
|
||||
|
||||
const requiredFiles = await glob('**', {
|
||||
cwd: path.join(nodeFunctionPath, path.dirname(fileName)),
|
||||
});
|
||||
|
||||
const lambda = await createLambda({
|
||||
files: {
|
||||
...requiredFiles,
|
||||
...launcherFiles,
|
||||
},
|
||||
handler: `${launcherFileName}.launcher`,
|
||||
runtime: getLatestNodeVersion().runtime,
|
||||
});
|
||||
|
||||
const parsed = path.parse(fileName);
|
||||
const newPath = path.join(functionsMountPath, parsed.dir, parsed.name);
|
||||
output[newPath] = lambda;
|
||||
|
||||
debug(
|
||||
`Created Lambda "${newPath}" from "${path.join(
|
||||
nodeFunctionPath,
|
||||
fileName
|
||||
)}".`
|
||||
);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
async function readRoutesConfig({
|
||||
workPath,
|
||||
}: {
|
||||
workPath: string;
|
||||
}): Promise<Route[]> {
|
||||
const routesConfigPath = path.join(
|
||||
workPath,
|
||||
'.vercel_build_output',
|
||||
'config',
|
||||
'routes.json'
|
||||
);
|
||||
|
||||
try {
|
||||
return JSON.parse(await fs.readFile(routesConfigPath, 'utf8')) || [];
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
return [];
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
69
packages/now-static-build/test/build-fixtures/06-gatsby-export-default/.gitignore
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# dotenv environment variable files
|
||||
.env*
|
||||
|
||||
# gatsby files
|
||||
.cache/
|
||||
public
|
||||
|
||||
# Mac files
|
||||
.DS_Store
|
||||
|
||||
# Yarn
|
||||
yarn-error.log
|
||||
.pnp/
|
||||
.pnp.js
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
@@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
default: {
|
||||
siteMetadata: {
|
||||
title: `Gatsby Default Starter`,
|
||||
description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
|
||||
author: `@gatsbyjs`,
|
||||
},
|
||||
plugins: [`gatsby-plugin-react-helmet`],
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "gatsby-starter-default",
|
||||
"private": true,
|
||||
"description": "A simple starter to get up and developing quickly with Gatsby",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"gatsby": "^2.24.91",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"gatsby-plugin-react-helmet": "3.3.14",
|
||||
"react-helmet": "^6.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gatsby build",
|
||||
"develop": "gatsby develop",
|
||||
"start": "npm run develop",
|
||||
"serve": "gatsby serve",
|
||||
"clean": "gatsby clean"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
const NotFoundPage = () => <h1>404: Not Found</h1>;
|
||||
|
||||
export default NotFoundPage;
|
||||