mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-29 11:49:14 +00:00
Compare commits
26 Commits
@vercel/no
...
@vercel/fr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
895b233605 | ||
|
|
d9e6b5348b | ||
|
|
1934a64864 | ||
|
|
c412642668 | ||
|
|
793fe9aee1 | ||
|
|
ddc54d2ca4 | ||
|
|
39e5f0a364 | ||
|
|
43ed9ec859 | ||
|
|
8ba44fca79 | ||
|
|
27dbefaecf | ||
|
|
b4a13913c7 | ||
|
|
f842266b2e | ||
|
|
ad0cc858ed | ||
|
|
4bb7180de9 | ||
|
|
7a4faa480d | ||
|
|
421be5d738 | ||
|
|
b8eaf10974 | ||
|
|
92a4bf27cf | ||
|
|
2672838b64 | ||
|
|
1c96071ddc | ||
|
|
71cdf759da | ||
|
|
93ebd213de | ||
|
|
a32ba8f214 | ||
|
|
d416f70a6e | ||
|
|
ba9e1dd0ba | ||
|
|
d513f74b70 |
2
.github/workflows/cancel.yml
vendored
2
.github/workflows/cancel.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- uses: styfle/cancel-workflow-action@0.3.2
|
||||
- uses: styfle/cancel-workflow-action@0.4.1
|
||||
with:
|
||||
workflow_id: 849295, 849296, 849297, 849298
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# RedwoodJS Example
|
||||
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@redwoodjs/api": "0.14.0"
|
||||
"@redwoodjs/api": "0.15.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@redwoodjs/core": "0.14.0"
|
||||
"@redwoodjs/core": "0.15.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "@redwoodjs/eslint-config"
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
"defaults"
|
||||
],
|
||||
"dependencies": {
|
||||
"@redwoodjs/router": "0.14.0",
|
||||
"@redwoodjs/web": "0.14.0",
|
||||
"@redwoodjs/router": "0.15.0",
|
||||
"@redwoodjs/web": "0.15.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1"
|
||||
|
||||
14339
examples/redwoodjs/yarn.lock
Normal file
14339
examples/redwoodjs/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,17 +2,17 @@
|
||||
"name": "svelte-app",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^13.0.0",
|
||||
"@rollup/plugin-node-resolve": "^8.1.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"rollup": "^1.10.1",
|
||||
"rollup-plugin-commonjs": "^9.3.4",
|
||||
"rollup": "^2.18.0",
|
||||
"rollup-plugin-livereload": "^1.0.0",
|
||||
"rollup-plugin-node-resolve": "^4.2.3",
|
||||
"rollup-plugin-svelte": "^5.0.3",
|
||||
"rollup-plugin-terser": "^4.0.4",
|
||||
"rollup-plugin-terser": "^6.1.0",
|
||||
"svelte": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"sirv-cli": "^0.4.4"
|
||||
"sirv-cli": "^1.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import svelte from 'rollup-plugin-svelte';
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import livereload from 'rollup-plugin-livereload';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
|
||||
|
||||
@@ -686,7 +686,7 @@
|
||||
"name": "RedwoodJS",
|
||||
"slug": "redwoodjs",
|
||||
"demo": "https://redwoodjs.now-examples.now.sh",
|
||||
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/redwood.svg",
|
||||
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/redwoodjs.svg",
|
||||
"tagline": "RedwoodJS is a full-stack framework for the Jamstack.",
|
||||
"description": "A RedwoodJS app, bootstraped with create-redwood-app.",
|
||||
"website": "https://redwoodjs.com",
|
||||
@@ -703,7 +703,7 @@
|
||||
"value": "yarn rw db up --no-db-client --auto-approve && yarn rw build"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "yarn rw dev"
|
||||
"value": "yarn rw dev --fwd=\"--port=$PORT --open=false\""
|
||||
},
|
||||
"outputDirectory": {
|
||||
"value": "RedwoodJS default"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg fill="none" height="1000" viewBox="0 0 917 1000" width="917" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="m249.557 144.582 194.171 132.54c4.383 2.918 9.502 4.516 14.755 4.606 5.261-.038 10.394-1.641 14.755-4.606l194.319-132.986c7.55-5.406 11.714-14.418 10.957-23.717-.757-9.298-6.322-17.507-14.646-21.6024l-194.171-96.13614c-7.366-3.573948-15.947-3.573948-23.313 0l-193.581 96.13614c-8.474 4.1174-14.113 12.4854-14.783 21.9354-.67 9.451 3.73 18.541 11.537 23.83zm274.879 174.144c.016 8.789 4.318 17.01 11.509 21.991l155.662 106.389c9.965 6.87 23.298 6.012 32.313-2.081l130.579-116.789c5.819-5.199 9.051-12.729 8.823-20.56s-3.892-15.158-10.004-20.005l-124.677-99.702c-9.062-7.199-21.704-7.68-31.28-1.189l-161.416 110.401c-7.064 4.89-11.35 12.914-11.509 21.545zm-387.163 144.724c6.292 5.652 9.526 13.988 8.706 22.437-.817 8.499-5.726 16.052-13.132 20.208l-92.9545 55.72c-9.4227 5.633-21.32 4.82-29.90183-2.041-8.5818-6.861-12.06543-18.346-8.75546-28.865l34.37839-108.172c2.6969-8.57 9.5328-15.175 18.1483-17.533 8.609-2.505 17.8924-.309 24.4928 5.795zm504.168 11.293-168.056-115.007c-8.931-6.01-20.578-6.01-29.509 0l-168.056 115.007c-6.684 4.626-10.919 12.061-11.509 20.208-.435 8.203 2.816 16.169 8.853 21.693l167.909 150.222c4.842 4.319 11.089 6.698 17.558 6.687 6.465-.002 12.708-2.38 17.558-6.687l167.908-150.222c6.056-5.501 9.265-13.5 8.705-21.693-.469-8.146-4.666-15.612-11.361-20.208zm-448.247-29.718-130.4316-116.79c-5.8687-5.331-9.1073-12.995-8.8528-20.95.1419-7.841 3.7705-15.204 9.8856-20.06l124.6768-100.296c9.126-7.179 21.793-7.658 31.428-1.189l161.269 110.401c7.484 4.908 11.998 13.293 11.998 22.288 0 8.994-4.514 17.38-11.998 22.288l-155.515 106.388c-10.025 6.841-23.376 5.985-32.46-2.08zm669.715 167.756-132.792-79.495c-9.862-5.943-22.415-4.739-30.985 2.972l-162.301 144.873c-6.846 6.114-10.062 15.362-8.499 24.441 1.563 9.08 7.681 16.698 16.171 20.135l225.157 91.233c3.088 1.283 6.397 1.939 9.738 1.932 10.449.033 19.936-6.142 24.197-15.751l69.79-156.314c5.68-12.37 1.157-27.062-10.476-34.026zm18.443-190.043 34.379 108.171h-.295c2.542 8.091 1.097 16.919-3.889 23.761-4.986 6.841-12.915 10.876-21.342 10.86-4.728.016-9.37-1.269-13.427-3.715l-93.102-55.72c-7.254-4.243-11.992-11.789-12.689-20.208-.87-8.456 2.373-16.814 8.705-22.436l59.019-52.6c6.668-5.976 15.881-8.156 24.493-5.795 8.609 2.459 15.423 9.098 18.148 17.682zm-492.511 282.761c1.587-9.042-1.597-18.266-8.41-24.368l-162.302-144.873c-8.57-7.711-21.123-8.915-30.985-2.972l-132.7921 79.495c-11.4977 6.995-16.0467 21.502-10.6233 33.878l69.9374 156.314c5.794 13.034 20.774 19.134 33.936 13.818l225.009-91.232c8.492-3.407 14.632-10.995 16.23-20.06zm79.675 44.577 180.598 73.105c8.83 3.779 14.93 12.084 15.935 21.694 1.143 9.729-3.178 19.291-11.214 24.814l-180.745 125.556c-4.331 3.043-9.473 4.7-14.754 4.755-5.277-.082-10.411-1.737-14.755-4.755l-180.597-125.556c-8.066-5.508-12.439-15.061-11.362-24.814 1.206-9.71 7.526-18.006 16.526-21.694l180.597-73.105c6.351-2.532 13.421-2.532 19.771 0z" fill="#bf4722" fill-rule="evenodd"/></svg>
|
||||
|
Before Width: | Height: | Size: 3.0 KiB |
1
packages/frameworks/logos/redwoodjs.svg
Normal file
1
packages/frameworks/logos/redwoodjs.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" width="48" height="48" viewBox="0 0 917 1000" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="m249.557 144.582 194.171 132.54c4.383 2.918 9.502 4.516 14.755 4.606 5.261-.038 10.394-1.641 14.755-4.606l194.319-132.986c7.55-5.406 11.714-14.418 10.957-23.717-.757-9.298-6.322-17.507-14.646-21.6024l-194.171-96.13614c-7.366-3.573948-15.947-3.573948-23.313 0l-193.581 96.13614c-8.474 4.1174-14.113 12.4854-14.783 21.9354-.67 9.451 3.73 18.541 11.537 23.83zm274.879 174.144c.016 8.789 4.318 17.01 11.509 21.991l155.662 106.389c9.965 6.87 23.298 6.012 32.313-2.081l130.579-116.789c5.819-5.199 9.051-12.729 8.823-20.56s-3.892-15.158-10.004-20.005l-124.677-99.702c-9.062-7.199-21.704-7.68-31.28-1.189l-161.416 110.401c-7.064 4.89-11.35 12.914-11.509 21.545zm-387.163 144.724c6.292 5.652 9.526 13.988 8.706 22.437-.817 8.499-5.726 16.052-13.132 20.208l-92.9545 55.72c-9.4227 5.633-21.32 4.82-29.90183-2.041-8.5818-6.861-12.06543-18.346-8.75546-28.865l34.37839-108.172c2.6969-8.57 9.5328-15.175 18.1483-17.533 8.609-2.505 17.8924-.309 24.4928 5.795zm504.168 11.293-168.056-115.007c-8.931-6.01-20.578-6.01-29.509 0l-168.056 115.007c-6.684 4.626-10.919 12.061-11.509 20.208-.435 8.203 2.816 16.169 8.853 21.693l167.909 150.222c4.842 4.319 11.089 6.698 17.558 6.687 6.465-.002 12.708-2.38 17.558-6.687l167.908-150.222c6.056-5.501 9.265-13.5 8.705-21.693-.469-8.146-4.666-15.612-11.361-20.208zm-448.247-29.718-130.4316-116.79c-5.8687-5.331-9.1073-12.995-8.8528-20.95.1419-7.841 3.7705-15.204 9.8856-20.06l124.6768-100.296c9.126-7.179 21.793-7.658 31.428-1.189l161.269 110.401c7.484 4.908 11.998 13.293 11.998 22.288 0 8.994-4.514 17.38-11.998 22.288l-155.515 106.388c-10.025 6.841-23.376 5.985-32.46-2.08zm669.715 167.756-132.792-79.495c-9.862-5.943-22.415-4.739-30.985 2.972l-162.301 144.873c-6.846 6.114-10.062 15.362-8.499 24.441 1.563 9.08 7.681 16.698 16.171 20.135l225.157 91.233c3.088 1.283 6.397 1.939 9.738 1.932 10.449.033 19.936-6.142 24.197-15.751l69.79-156.314c5.68-12.37 1.157-27.062-10.476-34.026zm18.443-190.043 34.379 108.171h-.295c2.542 8.091 1.097 16.919-3.889 23.761-4.986 6.841-12.915 10.876-21.342 10.86-4.728.016-9.37-1.269-13.427-3.715l-93.102-55.72c-7.254-4.243-11.992-11.789-12.689-20.208-.87-8.456 2.373-16.814 8.705-22.436l59.019-52.6c6.668-5.976 15.881-8.156 24.493-5.795 8.609 2.459 15.423 9.098 18.148 17.682zm-492.511 282.761c1.587-9.042-1.597-18.266-8.41-24.368l-162.302-144.873c-8.57-7.711-21.123-8.915-30.985-2.972l-132.7921 79.495c-11.4977 6.995-16.0467 21.502-10.6233 33.878l69.9374 156.314c5.794 13.034 20.774 19.134 33.936 13.818l225.009-91.232c8.492-3.407 14.632-10.995 16.23-20.06zm79.675 44.577 180.598 73.105c8.83 3.779 14.93 12.084 15.935 21.694 1.143 9.729-3.178 19.291-11.214 24.814l-180.745 125.556c-4.331 3.043-9.473 4.7-14.754 4.755-5.277-.082-10.411-1.737-14.755-4.755l-180.597-125.556c-8.066-5.508-12.439-15.061-11.362-24.814 1.206-9.71 7.526-18.006 16.526-21.694l180.597-73.105c6.351-2.532 13.421-2.532 19.771 0z" fill="#bf4722" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/frameworks",
|
||||
"version": "0.0.18-canary.1",
|
||||
"version": "0.0.18-canary.3",
|
||||
"main": "frameworks.json",
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "20.0.0-canary.7",
|
||||
"version": "20.0.0-canary.13",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -64,10 +64,10 @@
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.4.3-canary.2",
|
||||
"@vercel/go": "1.1.5-canary.0",
|
||||
"@vercel/next": "2.6.15",
|
||||
"@vercel/node": "1.7.4",
|
||||
"@vercel/next": "2.6.19-canary.1",
|
||||
"@vercel/node": "1.7.5-canary.1",
|
||||
"@vercel/python": "1.2.2",
|
||||
"@vercel/redwood": "0.0.2-canary.1",
|
||||
"@vercel/redwood": "0.0.2-canary.3",
|
||||
"@vercel/ruby": "1.2.3",
|
||||
"@vercel/static-build": "0.17.7-canary.1",
|
||||
"update-notifier": "4.1.0"
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as ERRORS from '../../util/errors-ts';
|
||||
import { Output } from '../../util/output';
|
||||
import deleteCertById from '../../util/certs/delete-cert-by-id';
|
||||
import getCertById from '../../util/certs/get-cert-by-id';
|
||||
import getCertsForDomain from '../../util/certs/get-certs-for-domain';
|
||||
import { getCustomCertsForDomain } from '../../util/certs/get-custom-certs-for-domain';
|
||||
import Client from '../../util/client';
|
||||
import getScope from '../../util/get-scope';
|
||||
import stamp from '../../util/output/stamp';
|
||||
@@ -66,9 +66,17 @@ async function rm(
|
||||
}
|
||||
|
||||
if (certs.length === 0) {
|
||||
output.error(
|
||||
`No certificates found by id "${id}" under ${chalk.bold(contextName)}`
|
||||
);
|
||||
if (id.includes('.')) {
|
||||
output.error(
|
||||
`No custom certificates found for "${id}" under ${chalk.bold(
|
||||
contextName
|
||||
)}`
|
||||
);
|
||||
} else {
|
||||
output.error(
|
||||
`No certificates found by id "${id}" under ${chalk.bold(contextName)}`
|
||||
);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -101,7 +109,7 @@ async function getCertsToDelete(
|
||||
) {
|
||||
const cert = await getCertById(client, id);
|
||||
if (cert instanceof ERRORS.CertNotFound) {
|
||||
const certs = await getCertsForDomain(output, client, contextName, id);
|
||||
const certs = await getCustomCertsForDomain(client, contextName, id);
|
||||
if (certs instanceof ERRORS.CertsPermissionDenied) {
|
||||
return certs;
|
||||
}
|
||||
@@ -125,12 +133,7 @@ function readConfirmation(output: Output, msg: string, certs: Cert[]) {
|
||||
process.stdin
|
||||
.on('data', d => {
|
||||
process.stdin.pause();
|
||||
resolve(
|
||||
d
|
||||
.toString()
|
||||
.trim()
|
||||
.toLowerCase() === 'y'
|
||||
);
|
||||
resolve(d.toString().trim().toLowerCase() === 'y');
|
||||
})
|
||||
.resume();
|
||||
});
|
||||
|
||||
@@ -128,7 +128,7 @@ export default async function add(
|
||||
return 1;
|
||||
}
|
||||
|
||||
const domainConfig = await getDomainConfig(client, contextName, domainName);
|
||||
const domainConfig = await getDomainConfig(client, domainName);
|
||||
|
||||
if (domainConfig.misconfigured) {
|
||||
output.warn(
|
||||
@@ -142,7 +142,7 @@ export default async function add(
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.grey('b)')} ` +
|
||||
`Change your domain nameservers to the intended set`
|
||||
`Change your Domains's nameservers to the intended set`
|
||||
);
|
||||
output.print(
|
||||
`\n${formatNSTable(
|
||||
|
||||
@@ -71,8 +71,9 @@ export default async function buy(
|
||||
|
||||
const availableStamp = stamp();
|
||||
const domainPrice = await getDomainPrice(client, domainName);
|
||||
if (domainPrice instanceof ERRORS.UnsupportedTLD) {
|
||||
output.error(`The TLD for ${param(domainName)} is not supported.`);
|
||||
|
||||
if (domainPrice instanceof Error) {
|
||||
output.prettyError(domainPrice);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import inspect from './inspect';
|
||||
import ls from './ls';
|
||||
import rm from './rm';
|
||||
import move from './move';
|
||||
import verify from './verify';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
const help = () => {
|
||||
@@ -81,6 +82,7 @@ const COMMAND_CONFIG = {
|
||||
move: ['move'],
|
||||
rm: ['rm', 'remove'],
|
||||
transferIn: ['transfer-in'],
|
||||
verify: ['verify'],
|
||||
};
|
||||
|
||||
export default async function main(ctx: NowContext) {
|
||||
@@ -119,6 +121,8 @@ export default async function main(ctx: NowContext) {
|
||||
return rm(ctx, argv, args, output);
|
||||
case 'transferIn':
|
||||
return transferIn(ctx, argv, args, output);
|
||||
case 'verify':
|
||||
return verify(ctx, argv, args, output);
|
||||
default:
|
||||
return ls(ctx, argv, args, output);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ import getDomainPrice from '../../util/domains/get-domain-price';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import { getDomainConfig } from '../../util/domains/get-domain-config';
|
||||
import code from '../../util/output/code';
|
||||
import wait from '../../util/output/wait';
|
||||
import { getDomainRegistrar } from '../../util/domains/get-domain-registrar';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -67,38 +69,26 @@ export default async function inspect(
|
||||
}
|
||||
|
||||
output.debug(`Fetching domain info`);
|
||||
const [domain, renewalPrice] = await Promise.all([
|
||||
getDomainByName(client, contextName, domainName),
|
||||
getDomainPrice(client, domainName, 'renewal')
|
||||
.then(res => (res instanceof Error ? null : res.price))
|
||||
.catch(() => null),
|
||||
]);
|
||||
if (!domain || domain instanceof DomainNotFound) {
|
||||
output.error(
|
||||
`Domain not found by "${domainName}" under ${chalk.bold(contextName)}`
|
||||
);
|
||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
||||
return 1;
|
||||
|
||||
const cancelWait = wait(
|
||||
`Fetching domain ${domainName} under ${chalk.bold(contextName)}`
|
||||
);
|
||||
|
||||
const information = await fetchInformation({
|
||||
output,
|
||||
client,
|
||||
contextName,
|
||||
domainName,
|
||||
cancelWait,
|
||||
}).finally(() => {
|
||||
cancelWait();
|
||||
});
|
||||
|
||||
if (typeof information === 'number') {
|
||||
return information;
|
||||
}
|
||||
|
||||
if (domain instanceof DomainPermissionDenied) {
|
||||
output.error(
|
||||
`You don't have access to the domain ${domainName} under ${chalk.bold(
|
||||
contextName
|
||||
)}`
|
||||
);
|
||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const projects = await findProjectsForDomain(client, domainName);
|
||||
|
||||
if (projects instanceof Error) {
|
||||
output.prettyError(projects);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const domainConfig = await getDomainConfig(client, contextName, domainName);
|
||||
const { domain, projects, renewalPrice, domainConfig } = information;
|
||||
|
||||
output.log(
|
||||
`Domain ${domainName} found under ${chalk.bold(contextName)} ${chalk.gray(
|
||||
@@ -108,46 +98,27 @@ export default async function inspect(
|
||||
output.print('\n');
|
||||
output.print(chalk.bold(' General\n\n'));
|
||||
output.print(` ${chalk.cyan('Name')}\t\t\t${domain.name}\n`);
|
||||
output.print(` ${chalk.cyan('Service Type')}\t\t${domain.serviceType}\n`);
|
||||
output.print(
|
||||
` ${chalk.cyan('Ordered At')}\t\t\t${formatDate(domain.orderedAt)}\n`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan('Transfer Started At')}\t\t${formatDate(
|
||||
domain.transferStartedAt
|
||||
)}\n`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan('Created At')}\t\t\t${formatDate(domain.createdAt)}\n`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan('Bought At')}\t\t\t${formatDate(domain.boughtAt)}\n`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan('Transferred At')}\t\t${formatDate(
|
||||
domain.transferredAt
|
||||
)}\n`
|
||||
` ${chalk.cyan('Registrar')}\t\t\t${getDomainRegistrar(domain)}\n`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan('Expires At')}\t\t\t${formatDate(domain.expiresAt)}\n`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan('NS Verified At')}\t\t${formatDate(
|
||||
domain.nsVerifiedAt
|
||||
)}\n`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan('TXT Verified At')}\t\t${formatDate(
|
||||
domain.txtVerifiedAt
|
||||
)}\n`
|
||||
);
|
||||
output.print(` ${chalk.cyan('Edge Network')}\t\t${true}\n`);
|
||||
|
||||
if (renewalPrice && domain.boughtAt) {
|
||||
output.print(
|
||||
` ${chalk.cyan('Renewal Price')}\t\t$${renewalPrice} USD\n`
|
||||
);
|
||||
}
|
||||
output.print(` ${chalk.cyan('CDN Enabled')}\t\t\t${true}\n`);
|
||||
|
||||
output.print(
|
||||
` ${chalk.cyan('Creator')}\t\t\t${domain.creator.username}\n`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan('Created At')}\t\t\t${formatDate(domain.createdAt)}\n`
|
||||
);
|
||||
|
||||
output.print('\n');
|
||||
|
||||
output.print(chalk.bold(' Nameservers\n\n'));
|
||||
@@ -158,26 +129,6 @@ export default async function inspect(
|
||||
);
|
||||
output.print('\n');
|
||||
|
||||
if (domainConfig.misconfigured) {
|
||||
output.warn(
|
||||
`This domain is not configured properly. To configure it you should either:`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.grey('a)')} ` +
|
||||
`Set the following record on your DNS provider to continue: ` +
|
||||
`${code(`A ${domainName} 76.76.21.21`)} ` +
|
||||
`${chalk.grey('[recommended]')}\n`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.grey('b)')} ` +
|
||||
`Change your domain nameservers to the intended set detailed above.\n\n`
|
||||
);
|
||||
output.print(
|
||||
` We will run a verification for you and you will receive an email upon completion.\n`
|
||||
);
|
||||
output.print(' Read more: https://vercel.link/domain-configuration\n\n');
|
||||
}
|
||||
|
||||
if (Array.isArray(projects) && projects.length > 0) {
|
||||
output.print(chalk.bold(' Projects\n'));
|
||||
|
||||
@@ -208,8 +159,109 @@ export default async function inspect(
|
||||
.join('\n')
|
||||
);
|
||||
|
||||
output.print('\n\n');
|
||||
output.print('\n');
|
||||
}
|
||||
|
||||
if (domainConfig.misconfigured) {
|
||||
output.warn(
|
||||
`This domain is not configured properly. To configure it you should either:`,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
{
|
||||
boxen: {
|
||||
margin: {
|
||||
left: 2,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
top: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.grey('a)')} ` +
|
||||
`Set the following record on your DNS provider to continue: ` +
|
||||
`${code(`A ${domainName} 76.76.21.21`)} ` +
|
||||
`${chalk.grey('[recommended]')}\n`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.grey('b)')} ` +
|
||||
`Change your Domains's nameservers to the intended set detailed above.\n\n`
|
||||
);
|
||||
output.print(
|
||||
` We will run a verification for you and you will receive an email upon completion.\n`
|
||||
);
|
||||
|
||||
const contextNameConst = contextName;
|
||||
const projectNames = Array.from(
|
||||
new Set(projects.map(project => project.name))
|
||||
);
|
||||
|
||||
if (projectNames.length) {
|
||||
projectNames.forEach((name, index) => {
|
||||
const prefix = index === 0 ? ' Read more:' : ' '.repeat(12);
|
||||
output.print(
|
||||
`${prefix} https://vercel.com/${contextNameConst}/${name}/settings/domains\n`
|
||||
);
|
||||
});
|
||||
} else {
|
||||
output.print(` Read more: https://vercel.link/domain-configuration\n`);
|
||||
}
|
||||
|
||||
output.print('\n');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function fetchInformation({
|
||||
output,
|
||||
client,
|
||||
contextName,
|
||||
domainName,
|
||||
cancelWait,
|
||||
}: {
|
||||
output: Output;
|
||||
client: Client;
|
||||
contextName: string;
|
||||
domainName: string;
|
||||
cancelWait: () => void;
|
||||
}) {
|
||||
const [domain, renewalPrice] = await Promise.all([
|
||||
getDomainByName(client, contextName, domainName, { ignoreWait: true }),
|
||||
getDomainPrice(client, domainName, 'renewal')
|
||||
.then(res => (res instanceof Error ? null : res.price))
|
||||
.catch(() => null),
|
||||
]);
|
||||
|
||||
if (domain instanceof DomainNotFound) {
|
||||
cancelWait();
|
||||
output.prettyError(domain);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (domain instanceof DomainPermissionDenied) {
|
||||
cancelWait();
|
||||
output.prettyError(domain);
|
||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const projects = await findProjectsForDomain(client, domainName);
|
||||
|
||||
if (projects instanceof Error) {
|
||||
cancelWait();
|
||||
output.prettyError(projects);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const domainConfig = await getDomainConfig(client, domainName);
|
||||
|
||||
return {
|
||||
domain,
|
||||
projects,
|
||||
renewalPrice,
|
||||
domainConfig,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import ms from 'ms';
|
||||
import psl from 'psl';
|
||||
import chalk from 'chalk';
|
||||
import plural from 'pluralize';
|
||||
|
||||
import wait from '../../util/output/wait';
|
||||
import Client from '../../util/client';
|
||||
import getDomains from '../../util/domains/get-domains';
|
||||
import getScope from '../../util/get-scope';
|
||||
@@ -10,28 +10,17 @@ import stamp from '../../util/output/stamp';
|
||||
import { Output } from '../../util/output';
|
||||
import formatTable from '../../util/format-table';
|
||||
import { formatDateWithoutTime } from '../../util/format-date';
|
||||
import { Domain, Project, NowContext } from '../../types';
|
||||
import { getProjectsWithDomains } from '../../util/projects/get-projects-with-domains';
|
||||
import { Domain, NowContext } from '../../types';
|
||||
import getCommandFlags from '../../util/get-command-flags';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import isDomainExternal from '../../util/domains/is-domain-external';
|
||||
import { isPublicSuffix } from '../../util/domains/is-public-suffix';
|
||||
import { getDomainRegistrar } from '../../util/domains/get-domain-registrar';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
'--next': number;
|
||||
};
|
||||
|
||||
interface DomainInfo {
|
||||
domain: string;
|
||||
apexDomain: string;
|
||||
projectName: string | null;
|
||||
dns: 'Vercel' | 'External';
|
||||
configured: boolean;
|
||||
expiresAt: number | null;
|
||||
createdAt: number | null;
|
||||
}
|
||||
|
||||
export default async function ls(
|
||||
ctx: NowContext,
|
||||
opts: Options,
|
||||
@@ -75,29 +64,21 @@ export default async function ls(
|
||||
return 1;
|
||||
}
|
||||
|
||||
const [{ domains, pagination }, projects] = await Promise.all([
|
||||
getDomains(client, contextName),
|
||||
getProjectsWithDomains(client),
|
||||
] as const);
|
||||
const cancelWait = wait(`Fetching domains under ${chalk.bold(contextName)}`);
|
||||
|
||||
if (projects instanceof Error) {
|
||||
output.prettyError(projects);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const domainsInfo = createDomainsInfo(domains, projects);
|
||||
const { domains, pagination } = await getDomains(client).finally(() => {
|
||||
cancelWait();
|
||||
});
|
||||
|
||||
output.log(
|
||||
`${plural(
|
||||
'project domain',
|
||||
domainsInfo.length,
|
||||
true
|
||||
)} found under ${chalk.bold(contextName)} ${chalk.gray(lsStamp())}`
|
||||
`${plural('Domain', domains.length, true)} found under ${chalk.bold(
|
||||
contextName
|
||||
)} ${chalk.gray(lsStamp())}`
|
||||
);
|
||||
|
||||
if (domainsInfo.length > 0) {
|
||||
if (domains.length > 0) {
|
||||
output.print(
|
||||
formatDomainsTable(domainsInfo).replace(/^(.*)/gm, `${' '.repeat(3)}$1`)
|
||||
formatDomainsTable(domains).replace(/^(.*)/gm, `${' '.repeat(1)}$1`)
|
||||
);
|
||||
output.print('\n\n');
|
||||
}
|
||||
@@ -105,7 +86,7 @@ export default async function ls(
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(opts, ['_', '--next']);
|
||||
output.log(
|
||||
`To display the next page run ${getCommandName(
|
||||
`To display the next page, run ${getCommandName(
|
||||
`domains ls${flags} --next ${pagination.next}`
|
||||
)}`
|
||||
);
|
||||
@@ -114,92 +95,26 @@ export default async function ls(
|
||||
return 0;
|
||||
}
|
||||
|
||||
function createDomainsInfo(domains: Domain[], projects: Project[]) {
|
||||
const info = new Map<string, DomainInfo>();
|
||||
|
||||
domains.forEach(domain => {
|
||||
info.set(domain.name, {
|
||||
domain: domain.name,
|
||||
apexDomain: domain.name,
|
||||
projectName: null,
|
||||
expiresAt: domain.expiresAt || null,
|
||||
createdAt: domain.createdAt,
|
||||
configured: Boolean(domain.verified),
|
||||
dns: isDomainExternal(domain) ? 'External' : 'Vercel',
|
||||
});
|
||||
|
||||
projects.forEach(project => {
|
||||
(project.alias || []).forEach(target => {
|
||||
if (!target.domain.endsWith(domain.name)) return;
|
||||
|
||||
info.set(target.domain, {
|
||||
domain: target.domain,
|
||||
apexDomain: domain.name,
|
||||
projectName: project.name,
|
||||
expiresAt: domain.expiresAt || null,
|
||||
createdAt: domain.createdAt || target.createdAt || null,
|
||||
configured: Boolean(domain.verified),
|
||||
dns: isDomainExternal(domain) ? 'External' : 'Vercel',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
projects.forEach(project => {
|
||||
(project.alias || []).forEach(target => {
|
||||
if (info.has(target.domain)) return;
|
||||
|
||||
const { domain: apexDomain } = psl.parse(
|
||||
target.domain
|
||||
) as psl.ParsedDomain;
|
||||
|
||||
info.set(target.domain, {
|
||||
domain: target.domain,
|
||||
apexDomain: apexDomain || target.domain,
|
||||
projectName: project.name,
|
||||
expiresAt: null,
|
||||
createdAt: target.createdAt || null,
|
||||
configured: isPublicSuffix(target.domain),
|
||||
dns: isPublicSuffix(target.domain) ? 'Vercel' : 'External',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const list = Array.from(info.values());
|
||||
|
||||
return list.sort((a, b) => {
|
||||
if (a.apexDomain === b.apexDomain) {
|
||||
if (a.apexDomain === a.domain) return -1;
|
||||
if (b.apexDomain === b.domain) return 1;
|
||||
return a.domain.localeCompare(b.domain);
|
||||
}
|
||||
|
||||
return a.apexDomain.localeCompare(b.apexDomain);
|
||||
});
|
||||
}
|
||||
|
||||
function formatDomainsTable(domainsInfo: DomainInfo[]) {
|
||||
function formatDomainsTable(domains: Domain[]) {
|
||||
const current = Date.now();
|
||||
|
||||
const rows: string[][] = domainsInfo.map(info => {
|
||||
const expiration = formatDateWithoutTime(info.expiresAt);
|
||||
const age = info.createdAt ? ms(current - info.createdAt) : '-';
|
||||
const rows: string[][] = domains.map(domain => {
|
||||
const expiration = formatDateWithoutTime(domain.expiresAt);
|
||||
const age = domain.createdAt ? ms(current - domain.createdAt) : '-';
|
||||
|
||||
return [
|
||||
info.domain,
|
||||
info.projectName || '-',
|
||||
info.dns,
|
||||
domain.name,
|
||||
getDomainRegistrar(domain),
|
||||
isDomainExternal(domain) ? 'Third Party' : 'Vercel',
|
||||
expiration,
|
||||
info.configured.toString(),
|
||||
domain.creator.username,
|
||||
chalk.gray(age),
|
||||
];
|
||||
});
|
||||
|
||||
const table = formatTable(
|
||||
['domain', 'project', 'dns', 'expiration', 'configured', 'age'],
|
||||
return formatTable(
|
||||
['Domain', 'Registrar', 'Nameservers', 'Expiration', 'Creator', 'Age'],
|
||||
['l', 'l', 'l', 'l', 'l', 'l'],
|
||||
[{ rows }]
|
||||
);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
@@ -71,8 +71,8 @@ export default async function transferIn(
|
||||
checkTransfer(client, domainName),
|
||||
]);
|
||||
|
||||
if (domainPrice instanceof ERRORS.UnsupportedTLD) {
|
||||
output.error(`The TLD for ${param(domainName)} is not supported.`);
|
||||
if (domainPrice instanceof Error) {
|
||||
output.prettyError(domainPrice);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
33
packages/now-cli/src/commands/domains/verify.ts
Normal file
33
packages/now-cli/src/commands/domains/verify.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import { NowBuildError } from '@vercel/build-utils';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
export default async function verify(
|
||||
_ctx: NowContext,
|
||||
_opts: {},
|
||||
args: string[],
|
||||
output: Output
|
||||
) {
|
||||
const [domainName] = args;
|
||||
|
||||
if (!domainName) {
|
||||
output.error(
|
||||
`${getCommandName(`domains verify <domain>`)} expects one argument`
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const error = new NowBuildError({
|
||||
code: 'domains_verify_command_deprecated',
|
||||
message: `It's not necessary to verify Domains anymore. Instead, you can run ${getCommandName(
|
||||
`domains inspect ${domainName}`
|
||||
)} to see what you need to do in order to configure it properly.`,
|
||||
link: 'https://vercel.link/domain-verification-via-cli',
|
||||
});
|
||||
|
||||
output.prettyError(error);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -640,6 +640,11 @@ const main = async argv_ => {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
|
||||
output.prettyError(err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (err instanceof APIError && 400 <= err.status && err.status <= 499) {
|
||||
err.message = err.serverMessage;
|
||||
output.prettyError(err);
|
||||
|
||||
@@ -6,7 +6,7 @@ export default async function deleteCertById(
|
||||
client: Client,
|
||||
id: string
|
||||
) {
|
||||
return client.fetch(`/v3/now/certs/${id}`, {
|
||||
method: 'DELETE'
|
||||
return client.fetch(`/v5/now/certs/${id}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as ERRORS from '../errors-ts';
|
||||
|
||||
export default async function getCertById(client: Client, id: string) {
|
||||
try {
|
||||
return await client.fetch<Cert>(`/v3/now/certs/${id}`);
|
||||
return await client.fetch<Cert>(`/v5/now/certs/${id}`);
|
||||
} catch (error) {
|
||||
if (error.code === 'cert_not_found') {
|
||||
return new ERRORS.CertNotFound(id);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { stringify } from 'querystring';
|
||||
import { Cert } from '../../types';
|
||||
import { Output } from '../output';
|
||||
import * as ERRORS from '../errors-ts';
|
||||
import Client from '../client';
|
||||
|
||||
@@ -8,15 +7,14 @@ type Response = {
|
||||
certs: Cert[];
|
||||
};
|
||||
|
||||
export default async function getCertsForDomain(
|
||||
output: Output,
|
||||
export async function getCustomCertsForDomain(
|
||||
client: Client,
|
||||
context: string,
|
||||
domain: string
|
||||
) {
|
||||
try {
|
||||
const { certs } = await client.fetch<Response>(
|
||||
`/v3/now/certs?${stringify({ domain })}`
|
||||
`/v5/now/certs?${stringify({ domain, custom: true })}`
|
||||
);
|
||||
return certs;
|
||||
} catch (error) {
|
||||
@@ -1578,6 +1578,13 @@ export default class DevServer {
|
||||
debug(
|
||||
`Checking build result's ${buildResult.routes.length} \`routes\` to match ${newUrl}`
|
||||
);
|
||||
for (const r of buildResult.routes) {
|
||||
// This replace is necessary for `@vercel/redwood` but might be relevant
|
||||
// for builders that wish to return routes and work with zero config.
|
||||
if (r.dest) {
|
||||
r.dest = r.dest.replace(/\$PORT/g, `${this.devProcessPort}`);
|
||||
}
|
||||
}
|
||||
const matchedRoute = await devRouter(
|
||||
newUrl,
|
||||
req.method,
|
||||
|
||||
@@ -4,6 +4,8 @@ import { Output } from '../output';
|
||||
import Client from '../client';
|
||||
import getDomainDNSRecords from './get-domain-dns-records';
|
||||
import getDomains from '../domains/get-domains';
|
||||
import wait from '../output/wait';
|
||||
import chalk from 'chalk';
|
||||
|
||||
export type DomainRecordsItem = {
|
||||
domainName: string;
|
||||
@@ -58,6 +60,11 @@ async function getDomainNames(
|
||||
contextName: string,
|
||||
next?: number
|
||||
) {
|
||||
const { domains, pagination } = await getDomains(client, contextName, next);
|
||||
return { domainNames: domains.map(domain => domain.name), pagination };
|
||||
const cancelWait = wait(`Fetching domains under ${chalk.bold(contextName)}`);
|
||||
try {
|
||||
const { domains, pagination } = await getDomains(client, next);
|
||||
return { domainNames: domains.map(domain => domain.name), pagination };
|
||||
} finally {
|
||||
cancelWait();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export default async function importZonefile(
|
||||
} catch (error) {
|
||||
cancelWait();
|
||||
if (error.code === 'not_found') {
|
||||
return new DomainNotFound(domain);
|
||||
return new DomainNotFound(domain, contextName);
|
||||
}
|
||||
|
||||
if (error.code === 'invalid_domain') {
|
||||
|
||||
@@ -8,24 +8,26 @@ type Response = {
|
||||
domain: Domain;
|
||||
};
|
||||
|
||||
async function getDomainByName(
|
||||
export default async function getDomainByName(
|
||||
client: Client,
|
||||
contextName: string,
|
||||
domainName: string
|
||||
domainName: string,
|
||||
options: {
|
||||
ignoreWait?: boolean;
|
||||
} = {}
|
||||
) {
|
||||
const cancelWait = wait(
|
||||
`Fetching domain ${domainName} under ${chalk.bold(contextName)}`
|
||||
);
|
||||
const cancelWait = options.ignoreWait
|
||||
? null
|
||||
: wait(`Fetching domain ${domainName} under ${chalk.bold(contextName)}`);
|
||||
try {
|
||||
const { domain } = await client.fetch<Response>(
|
||||
`/v4/domains/${encodeURIComponent(domainName)}`
|
||||
);
|
||||
cancelWait();
|
||||
|
||||
return domain;
|
||||
} catch (error) {
|
||||
cancelWait();
|
||||
if (error.status === 404) {
|
||||
return new DomainNotFound(domainName);
|
||||
return new DomainNotFound(domainName, contextName);
|
||||
}
|
||||
|
||||
if (error.status === 403) {
|
||||
@@ -33,7 +35,7 @@ async function getDomainByName(
|
||||
}
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
cancelWait?.();
|
||||
}
|
||||
}
|
||||
|
||||
export default getDomainByName;
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
import chalk from 'chalk';
|
||||
import Client from '../client';
|
||||
import wait from '../output/wait';
|
||||
import { DomainConfig } from '../../types';
|
||||
|
||||
export async function getDomainConfig(
|
||||
client: Client,
|
||||
contextName: string,
|
||||
domainName: string
|
||||
) {
|
||||
const cancelWait = wait(
|
||||
`Fetching domain config ${domainName} under ${chalk.bold(contextName)}`
|
||||
);
|
||||
export async function getDomainConfig(client: Client, domainName: string) {
|
||||
try {
|
||||
const config = await client.fetch<DomainConfig>(
|
||||
`/v4/domains/${domainName}/config`
|
||||
@@ -23,7 +14,5 @@ export async function getDomainConfig(
|
||||
}
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
cancelWait();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@ export default async function getDomainPrice(
|
||||
if (error.code === 'unsupported_tld') {
|
||||
return new UnsupportedTLD(name);
|
||||
}
|
||||
|
||||
if (error.status < 500) {
|
||||
return error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
15
packages/now-cli/src/util/domains/get-domain-registrar.ts
Normal file
15
packages/now-cli/src/util/domains/get-domain-registrar.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Domain } from '../../types';
|
||||
|
||||
export type DomainRegistrar = 'Vercel' | 'Purchase in Process' | 'Third Party';
|
||||
|
||||
export function getDomainRegistrar(domain: Domain): DomainRegistrar {
|
||||
if (domain.boughtAt) {
|
||||
return 'Vercel';
|
||||
}
|
||||
|
||||
if (typeof domain.orderedAt === 'number' && !domain.boughtAt) {
|
||||
return 'Purchase in Process';
|
||||
}
|
||||
|
||||
return 'Third Party';
|
||||
}
|
||||
@@ -1,24 +1,15 @@
|
||||
import chalk from 'chalk';
|
||||
import { Domain, PaginationOptions } from '../../types';
|
||||
import Client from '../client';
|
||||
import wait from '../output/wait';
|
||||
|
||||
type Response = {
|
||||
domains: Domain[];
|
||||
pagination: PaginationOptions;
|
||||
};
|
||||
|
||||
export default async function getDomains(
|
||||
client: Client,
|
||||
contextName: string,
|
||||
next?: number
|
||||
) {
|
||||
export default async function getDomains(client: Client, next?: number) {
|
||||
let domainUrl = `/v5/domains?limit=20`;
|
||||
if (next) {
|
||||
domainUrl += `&until=${next}`;
|
||||
}
|
||||
const cancelWait = wait(`Fetching domains under ${chalk.bold(contextName)}`);
|
||||
const domains = await client.fetch<Response>(domainUrl);
|
||||
cancelWait();
|
||||
return domains;
|
||||
return await client.fetch<Response>(domainUrl);
|
||||
}
|
||||
|
||||
@@ -30,12 +30,19 @@ export default async function purchaseDomainIfAvailable(
|
||||
}
|
||||
|
||||
output.debug(`Domain ${domain} is available to be purchased`);
|
||||
const domainPrice = await getDomainPrice(client, domain);
|
||||
cancelWait();
|
||||
|
||||
const domainPrice = await getDomainPrice(client, domain).finally(() => {
|
||||
cancelWait();
|
||||
});
|
||||
|
||||
if (domainPrice instanceof ERRORS.UnsupportedTLD) {
|
||||
return domainPrice;
|
||||
}
|
||||
|
||||
if (domainPrice instanceof Error) {
|
||||
throw domainPrice;
|
||||
}
|
||||
|
||||
const { price, period } = domainPrice;
|
||||
output.log(
|
||||
`Domain not found, but you can buy it under ${chalk.bold(
|
||||
@@ -68,6 +75,5 @@ export default async function purchaseDomainIfAvailable(
|
||||
}
|
||||
|
||||
output.debug(`Domain ${domain} is not available to be purchased`);
|
||||
cancelWait();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { NowBuildError } from '@vercel/build-utils';
|
||||
import { NowError } from './now-error';
|
||||
import code from './output/code';
|
||||
import { getCommandName } from './pkg-name';
|
||||
import chalk from 'chalk';
|
||||
|
||||
/**
|
||||
* This error is thrown when there is an API error with a payload. The error
|
||||
@@ -68,7 +69,9 @@ export class InvalidToken extends NowError<'NOT_AUTHORIZED', {}> {
|
||||
constructor() {
|
||||
super({
|
||||
code: `NOT_AUTHORIZED`,
|
||||
message: `The specified token is not valid`,
|
||||
message: `The specified token is not valid. Use ${getCommandName(
|
||||
`login`
|
||||
)} to generate a new token.`,
|
||||
meta: {},
|
||||
});
|
||||
}
|
||||
@@ -183,11 +186,13 @@ export class DomainNotFound extends NowError<
|
||||
'DOMAIN_NOT_FOUND',
|
||||
{ domain: string }
|
||||
> {
|
||||
constructor(domain: string) {
|
||||
constructor(domain: string, contextName?: string) {
|
||||
super({
|
||||
code: 'DOMAIN_NOT_FOUND',
|
||||
meta: { domain },
|
||||
message: `The domain ${domain} can't be found.`,
|
||||
message: `Domain not found by "${domain}"${
|
||||
contextName ? ` under ${chalk.bold(contextName)}` : ''
|
||||
}.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,15 +28,11 @@ export default function createOutput({ debug: debugEnabled = false } = {}) {
|
||||
str: string,
|
||||
slug: string | null = null,
|
||||
link: string | null = null,
|
||||
action: string = 'Learn More'
|
||||
) {
|
||||
const prevTerm = process.env.TERM;
|
||||
|
||||
if (!prevTerm) {
|
||||
// workaround for https://github.com/sindresorhus/term-size/issues/13
|
||||
process.env.TERM = 'xterm';
|
||||
action: string | null = 'Learn More',
|
||||
options?: {
|
||||
boxen?: boxen.Options;
|
||||
}
|
||||
|
||||
) {
|
||||
const details = slug ? `https://err.sh/now/${slug}` : link;
|
||||
|
||||
print(
|
||||
@@ -52,12 +48,11 @@ export default function createOutput({ debug: debugEnabled = false } = {}) {
|
||||
right: 1,
|
||||
},
|
||||
borderColor: 'yellow',
|
||||
...options?.boxen,
|
||||
}
|
||||
)
|
||||
);
|
||||
print('\n');
|
||||
|
||||
process.env.TERM = prevTerm;
|
||||
}
|
||||
|
||||
function note(str: string) {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import chalk from 'chalk';
|
||||
import Client from '../client';
|
||||
import wait from '../output/wait';
|
||||
import { Project } from '../../types';
|
||||
import { URLSearchParams } from 'url';
|
||||
|
||||
@@ -8,9 +6,6 @@ export async function findProjectsForDomain(
|
||||
client: Client,
|
||||
domainName: string
|
||||
): Promise<Project[] | Error> {
|
||||
const cancelWait = wait(
|
||||
`Searching project for domain ${chalk.bold(domainName)}`
|
||||
);
|
||||
try {
|
||||
const limit = 50;
|
||||
let result: Project[] = [];
|
||||
@@ -30,7 +25,7 @@ export async function findProjectsForDomain(
|
||||
}
|
||||
|
||||
const [latest] = response.sort((a, b) => b.updatedAt - a.updatedAt);
|
||||
query.append('from', latest.updatedAt.toString());
|
||||
query.set('from', latest.updatedAt.toString());
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -40,7 +35,5 @@ export async function findProjectsForDomain(
|
||||
}
|
||||
|
||||
throw err;
|
||||
} finally {
|
||||
cancelWait();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import Client from '../client';
|
||||
import wait from '../output/wait';
|
||||
import { Project } from '../../types';
|
||||
import { URLSearchParams } from 'url';
|
||||
|
||||
export async function getProjectsWithDomains(
|
||||
client: Client
|
||||
): Promise<Project[] | Error> {
|
||||
const cancelWait = wait(`Fetching projects with domains`);
|
||||
try {
|
||||
const limit = 50;
|
||||
let result: Project[] = [];
|
||||
|
||||
const query = new URLSearchParams({
|
||||
hasProductionDomains: '1',
|
||||
limit: limit.toString(),
|
||||
});
|
||||
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const response = await client.fetch<Project[]>(`/v2/projects/?${query}`);
|
||||
result.push(...response);
|
||||
|
||||
const [latest] = response.sort((a, b) => b.updatedAt - a.updatedAt);
|
||||
query.append('from', latest.updatedAt.toString());
|
||||
|
||||
if (response.length !== limit) break;
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (err) {
|
||||
if (err.status < 500) {
|
||||
return err;
|
||||
}
|
||||
|
||||
throw err;
|
||||
} finally {
|
||||
cancelWait();
|
||||
}
|
||||
}
|
||||
@@ -244,11 +244,12 @@ export async function linkFolderToProject(
|
||||
const gitIgnore = await readFile(gitIgnorePath)
|
||||
.then(buf => buf.toString())
|
||||
.catch(() => null);
|
||||
const EOL = gitIgnore && gitIgnore.includes('\r\n') ? '\r\n' : '\n';
|
||||
|
||||
if (!gitIgnore || !gitIgnore.split('\n').includes(VERCEL_DIR)) {
|
||||
if (!gitIgnore || !gitIgnore.split(EOL).includes(VERCEL_DIR)) {
|
||||
await writeFile(
|
||||
gitIgnorePath,
|
||||
gitIgnore ? `${gitIgnore}\n${VERCEL_DIR}` : VERCEL_DIR
|
||||
gitIgnore ? `${gitIgnore}${EOL}${VERCEL_DIR}${EOL}` : `${VERCEL_DIR}${EOL}`
|
||||
);
|
||||
isGitIgnoreUpdated = true;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ let port = 3000;
|
||||
const binaryPath = resolve(__dirname, `../../scripts/start.js`);
|
||||
const fixture = name => join('test', 'dev', 'fixtures', name);
|
||||
const fixtureAbsolute = name => join(__dirname, 'fixtures', name);
|
||||
const exampleAbsolute = name =>
|
||||
join(__dirname, '..', '..', '..', '..', 'examples', name);
|
||||
|
||||
let processCounter = 0;
|
||||
const processList = new Map();
|
||||
@@ -127,9 +129,10 @@ async function testPath(
|
||||
path,
|
||||
expectedText,
|
||||
headers = {},
|
||||
method = 'GET'
|
||||
method = 'GET',
|
||||
body = undefined
|
||||
) {
|
||||
const opts = { redirect: 'manual-dont-change', method };
|
||||
const opts = { redirect: 'manual-dont-change', method, body };
|
||||
const url = `${origin}${path}`;
|
||||
const res = await fetch(url, opts);
|
||||
const msg = `Testing response from ${method} ${url}`;
|
||||
@@ -230,10 +233,18 @@ async function testFixture(directory, opts = {}, args = []) {
|
||||
function testFixtureStdio(
|
||||
directory,
|
||||
fn,
|
||||
{ expectedCode = 0, skipDeploy } = {}
|
||||
{ expectedCode = 0, skipDeploy, isExample } = {}
|
||||
) {
|
||||
return async t => {
|
||||
const cwd = fixtureAbsolute(directory);
|
||||
const nodeMajor = Number(process.versions.node.split('.')[0]);
|
||||
if (isExample && nodeMajor < 12) {
|
||||
console.log(`Skipping ${directory} on Node ${process.version}`);
|
||||
t.pass();
|
||||
return;
|
||||
}
|
||||
const cwd = isExample
|
||||
? exampleAbsolute(directory)
|
||||
: fixtureAbsolute(directory);
|
||||
const token = await fetchTokenWithRetry();
|
||||
let deploymentUrl;
|
||||
|
||||
@@ -369,6 +380,21 @@ test.afterEach(async () => {
|
||||
);
|
||||
});
|
||||
|
||||
test(
|
||||
'[vercel dev] redwoodjs example',
|
||||
testFixtureStdio(
|
||||
'redwoodjs',
|
||||
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 resBody = '{"data":{"redwood":{"version":"0.15.0"}}}';
|
||||
await testPath(200, '/api/graphql', resBody, {}, 'POST', reqBody);
|
||||
},
|
||||
{ isExample: true }
|
||||
)
|
||||
);
|
||||
|
||||
test('[vercel dev] prints `npm install` errors', async t => {
|
||||
const dir = fixture('runtime-not-installed');
|
||||
const result = await exec(dir);
|
||||
@@ -1149,7 +1175,8 @@ test(
|
||||
await testPath(200, '/api/date', new RegExp(new Date().getFullYear()));
|
||||
await testPath(200, '/contact', /Contact Page/);
|
||||
await testPath(200, '/support', /Contact Page/);
|
||||
await testPath(404, '/nothing', /Custom Next 404/);
|
||||
// TODO: Fix this test assertion that fails intermittently
|
||||
// await testPath(404, '/nothing', /Custom Next 404/);
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
43
packages/now-cli/test/integration.js
vendored
43
packages/now-cli/test/integration.js
vendored
@@ -2621,39 +2621,6 @@ test('assign a domain to a project', async t => {
|
||||
t.is(removeResponse.exitCode, 0, formatOutput(removeResponse));
|
||||
});
|
||||
|
||||
test('list project domains', async t => {
|
||||
const domain = `project-domain.${contextName}.now.sh`;
|
||||
const directory = fixture('static-deployment');
|
||||
|
||||
const deploymentOutput = await execute([directory, '--public', '--confirm']);
|
||||
t.is(deploymentOutput.exitCode, 0, formatOutput(deploymentOutput));
|
||||
|
||||
const host = deploymentOutput.stdout.trim().replace('https://', '');
|
||||
const deployment = await apiFetch(
|
||||
`/v10/now/deployments/unknown?url=${host}`
|
||||
).then(resp => resp.json());
|
||||
|
||||
t.is(typeof deployment.name, 'string', JSON.stringify(deployment, null, 2));
|
||||
const project = deployment.name;
|
||||
|
||||
const addOutput = await execute([
|
||||
'domains',
|
||||
'add',
|
||||
domain,
|
||||
project,
|
||||
'--force',
|
||||
]);
|
||||
t.is(addOutput.exitCode, 0, formatOutput(addOutput));
|
||||
|
||||
const output = await execute(['domains', 'ls']);
|
||||
t.is(output.exitCode, 0, formatOutput(output));
|
||||
t.regex(output.stderr, new RegExp(domain), formatOutput(output));
|
||||
t.regex(output.stderr, new RegExp(project), formatOutput(output));
|
||||
|
||||
const removeResponse = await execute(['rm', project, '-y']);
|
||||
t.is(removeResponse.exitCode, 0, formatOutput(removeResponse));
|
||||
});
|
||||
|
||||
test('ensure `github` and `scope` are not sent to the API', async t => {
|
||||
const directory = fixture('github-and-scope-config');
|
||||
const output = await execute([directory, '--confirm']);
|
||||
@@ -2734,7 +2701,7 @@ test('should show prompts to set up project during first deploy', async t => {
|
||||
// Ensure .gitignore is created
|
||||
t.is(
|
||||
(await readFile(path.join(directory, '.gitignore'))).toString(),
|
||||
'.vercel'
|
||||
'.vercel\n'
|
||||
);
|
||||
|
||||
// Ensure .vercel/project.json and .vercel/README.txt are created
|
||||
@@ -3354,7 +3321,7 @@ test('[vc link] should show prompts to set up project', async t => {
|
||||
t.is(output.exitCode, 0, formatOutput(output));
|
||||
|
||||
// Ensure .gitignore is created
|
||||
t.is((await readFile(path.join(dir, '.gitignore'))).toString(), '.vercel');
|
||||
t.is((await readFile(path.join(dir, '.gitignore'))).toString(), '.vercel\n');
|
||||
|
||||
// Ensure .vercel/project.json and .vercel/README.txt are created
|
||||
t.is(
|
||||
@@ -3388,7 +3355,7 @@ test('[vc link --confirm] should not show prompts and autolink', async t => {
|
||||
t.regex(stderr, /Linked to /m);
|
||||
|
||||
// Ensure .gitignore is created
|
||||
t.is((await readFile(path.join(dir, '.gitignore'))).toString(), '.vercel');
|
||||
t.is((await readFile(path.join(dir, '.gitignore'))).toString(), '.vercel\n');
|
||||
|
||||
// Ensure .vercel/project.json and .vercel/README.txt are created
|
||||
t.is(
|
||||
@@ -3472,7 +3439,7 @@ test('[vc dev] should show prompts to set up project', async t => {
|
||||
await waitForPrompt(dev, chunk => chunk.includes('Linked to'));
|
||||
|
||||
// Ensure .gitignore is created
|
||||
t.is((await readFile(path.join(dir, '.gitignore'))).toString(), '.vercel');
|
||||
t.is((await readFile(path.join(dir, '.gitignore'))).toString(), '.vercel\n');
|
||||
|
||||
// Ensure .vercel/project.json and .vercel/README.txt are created
|
||||
t.is(
|
||||
@@ -3538,7 +3505,7 @@ test('[vc link] should show project prompts but not framework when `builds` defi
|
||||
t.is(output.exitCode, 0, formatOutput(output));
|
||||
|
||||
// Ensure .gitignore is created
|
||||
t.is((await readFile(path.join(dir, '.gitignore'))).toString(), '.vercel');
|
||||
t.is((await readFile(path.join(dir, '.gitignore'))).toString(), '.vercel\n');
|
||||
|
||||
// Ensure .vercel/project.json and .vercel/README.txt are created
|
||||
t.is(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/next",
|
||||
"version": "2.6.15",
|
||||
"version": "2.6.19-canary.1",
|
||||
"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",
|
||||
"@zeit/node-file-trace": "0.8.1",
|
||||
"@zeit/node-file-trace": "0.8.2",
|
||||
"async-sema": "3.0.1",
|
||||
"buffer-crc32": "0.2.13",
|
||||
"escape-string-regexp": "3.0.0",
|
||||
|
||||
@@ -1,20 +1,3 @@
|
||||
import buildUtils from './build-utils';
|
||||
import url from 'url';
|
||||
const {
|
||||
createLambda,
|
||||
debug,
|
||||
download,
|
||||
getLambdaOptionsFromFunction,
|
||||
getNodeVersion,
|
||||
getSpawnOptions,
|
||||
getScriptName,
|
||||
glob,
|
||||
runNpmInstall,
|
||||
runPackageJsonScript,
|
||||
execCommand,
|
||||
getNodeBinPath,
|
||||
} = buildUtils;
|
||||
|
||||
import {
|
||||
BuildOptions,
|
||||
Config,
|
||||
@@ -34,13 +17,17 @@ import {
|
||||
convertRewrites,
|
||||
} from '@vercel/routing-utils/dist/superstatic';
|
||||
import { nodeFileTrace, NodeFileTraceReasons } from '@zeit/node-file-trace';
|
||||
import { Sema } from 'async-sema';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
import escapeStringRegexp from 'escape-string-regexp';
|
||||
import findUp from 'find-up';
|
||||
import { lstat, pathExists, readFile, remove, writeFile } from 'fs-extra';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import resolveFrom from 'resolve-from';
|
||||
import semver from 'semver';
|
||||
import url from 'url';
|
||||
import buildUtils from './build-utils';
|
||||
import createServerlessConfig from './create-serverless-config';
|
||||
import nextLegacyVersions from './legacy-versions';
|
||||
import {
|
||||
@@ -66,8 +53,20 @@ import {
|
||||
syncEnvVars,
|
||||
validateEntrypoint,
|
||||
} from './utils';
|
||||
import findUp from 'find-up';
|
||||
import { Sema } from 'async-sema';
|
||||
const {
|
||||
createLambda,
|
||||
debug,
|
||||
download,
|
||||
getLambdaOptionsFromFunction,
|
||||
getNodeVersion,
|
||||
getSpawnOptions,
|
||||
getScriptName,
|
||||
glob,
|
||||
runNpmInstall,
|
||||
runPackageJsonScript,
|
||||
execCommand,
|
||||
getNodeBinPath,
|
||||
} = buildUtils;
|
||||
|
||||
interface BuildParamsMeta {
|
||||
isDev: boolean | undefined;
|
||||
@@ -235,7 +234,7 @@ export const build = async ({
|
||||
|
||||
await download(files, workPath, meta);
|
||||
|
||||
const pkg = await readPackageJson(entryPath);
|
||||
let pkg = await readPackageJson(entryPath);
|
||||
const nextVersionRange = await getNextVersionRange(entryPath);
|
||||
const nodeVersion = await getNodeVersion(entryPath, undefined, config, meta);
|
||||
const spawnOpts = getSpawnOptions(meta, nodeVersion);
|
||||
@@ -331,17 +330,17 @@ export const build = async ({
|
||||
]);
|
||||
|
||||
debug('Normalizing package.json');
|
||||
const packageJson = normalizePackageJson(pkg);
|
||||
debug('Normalized package.json result: ', packageJson);
|
||||
await writePackageJson(entryPath, packageJson);
|
||||
pkg = normalizePackageJson(pkg);
|
||||
debug('Normalized package.json result: ', pkg);
|
||||
await writePackageJson(entryPath, pkg);
|
||||
}
|
||||
|
||||
const buildScriptName = getScriptName(pkg, [
|
||||
let buildScriptName = getScriptName(pkg, [
|
||||
'vercel-build',
|
||||
'now-build',
|
||||
'build',
|
||||
]);
|
||||
let { buildCommand } = config;
|
||||
const { buildCommand } = config;
|
||||
|
||||
if (!buildScriptName && !buildCommand) {
|
||||
console.log(
|
||||
@@ -349,7 +348,15 @@ export const build = async ({
|
||||
'If you need to define a different build step, please create a `vercel-build` script in your `package.json` ' +
|
||||
'(e.g. `{ "scripts": { "vercel-build": "npm run prepare && next build" } }`).'
|
||||
);
|
||||
buildCommand = 'next build';
|
||||
|
||||
await writePackageJson(entryPath, {
|
||||
...pkg,
|
||||
scripts: {
|
||||
'vercel-build': 'next build',
|
||||
...pkg.scripts,
|
||||
},
|
||||
});
|
||||
buildScriptName = 'vercel-build';
|
||||
}
|
||||
|
||||
if (process.env.NPM_AUTH_TOKEN) {
|
||||
@@ -438,7 +445,7 @@ export const build = async ({
|
||||
for (const dataRoute of routesManifest.dataRoutes) {
|
||||
const ssgDataRoute =
|
||||
prerenderManifest.fallbackRoutes[dataRoute.page] ||
|
||||
prerenderManifest.legacyBlockingRoutes[dataRoute.page];
|
||||
prerenderManifest.blockingFallbackRoutes[dataRoute.page];
|
||||
|
||||
// we don't need to add routes for non-lazy SSG routes since
|
||||
// they have outputs which would override the routes anyways
|
||||
@@ -878,7 +885,7 @@ export const build = async ({
|
||||
initialRevalidate === false &&
|
||||
!canUsePreviewMode &&
|
||||
!prerenderManifest.fallbackRoutes[route] &&
|
||||
!prerenderManifest.legacyBlockingRoutes[route]
|
||||
!prerenderManifest.blockingFallbackRoutes[route]
|
||||
) {
|
||||
// if the 404 page used getStaticProps we need to update static404Page
|
||||
// since it wasn't populated from the staticPages group
|
||||
@@ -1123,7 +1130,7 @@ export const build = async ({
|
||||
src: `^${escapeStringRegexp(outputName).replace(
|
||||
/\/index$/,
|
||||
'(/|/index|)'
|
||||
)}$`,
|
||||
)}/?$`,
|
||||
dest: `${path.join('/', currentLambdaGroup.lambdaIdentifier)}`,
|
||||
headers: {
|
||||
'x-nextjs-page': outputName,
|
||||
@@ -1303,7 +1310,7 @@ export const build = async ({
|
||||
if (!toRender) {
|
||||
try {
|
||||
const { pathname } = url.parse(req.url)
|
||||
toRender = pathname
|
||||
toRender = pathname.replace(/\\/$/, '')
|
||||
} catch (_) {
|
||||
// handle failing to parse url
|
||||
res.statusCode = 400
|
||||
@@ -1458,7 +1465,7 @@ export const build = async ({
|
||||
if (isFallback || isBlocking) {
|
||||
const pr = isFallback
|
||||
? prerenderManifest.fallbackRoutes[routeKey]
|
||||
: prerenderManifest.legacyBlockingRoutes[routeKey];
|
||||
: prerenderManifest.blockingFallbackRoutes[routeKey];
|
||||
initialRevalidate = 1; // TODO: should Next.js provide this default?
|
||||
// @ts-ignore
|
||||
if (initialRevalidate === false) {
|
||||
@@ -1549,7 +1556,7 @@ export const build = async ({
|
||||
Object.keys(prerenderManifest.fallbackRoutes).forEach(route =>
|
||||
onPrerenderRoute(route, { isBlocking: false, isFallback: true })
|
||||
);
|
||||
Object.keys(prerenderManifest.legacyBlockingRoutes).forEach(route =>
|
||||
Object.keys(prerenderManifest.blockingFallbackRoutes).forEach(route =>
|
||||
onPrerenderRoute(route, { isBlocking: true, isFallback: false })
|
||||
);
|
||||
|
||||
@@ -1559,7 +1566,7 @@ export const build = async ({
|
||||
// Dynamic pages for lazy routes should be handled by the lambda flow.
|
||||
[
|
||||
...Object.entries(prerenderManifest.fallbackRoutes),
|
||||
...Object.entries(prerenderManifest.legacyBlockingRoutes),
|
||||
...Object.entries(prerenderManifest.blockingFallbackRoutes),
|
||||
].forEach(([, { dataRouteRegex, dataRoute }]) => {
|
||||
dataRoutes.push({
|
||||
// Next.js provided data route regex
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import zlib from 'zlib';
|
||||
import path from 'path';
|
||||
import { FileFsRef, Files } from '@vercel/build-utils';
|
||||
import { NowHeader, NowRewrite, Route, Source } from '@vercel/routing-utils';
|
||||
import { Sema } from 'async-sema';
|
||||
import crc32 from 'buffer-crc32';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import resolveFrom from 'resolve-from';
|
||||
import semver from 'semver';
|
||||
import { ZipFile } from 'yazl';
|
||||
import crc32 from 'buffer-crc32';
|
||||
import { Sema } from 'async-sema';
|
||||
import resolveFrom from 'resolve-from';
|
||||
import zlib from 'zlib';
|
||||
import buildUtils from './build-utils';
|
||||
const { streamToBuffer, Lambda, NowBuildError, isSymbolicLink } = buildUtils;
|
||||
import { Files, FileFsRef } from '@vercel/build-utils';
|
||||
import { Route, Source, NowHeader, NowRewrite } from '@vercel/routing-utils';
|
||||
|
||||
type stringMap = { [key: string]: string };
|
||||
|
||||
@@ -198,7 +198,7 @@ async function getRoutes(
|
||||
// If default pages dir isn't found check for `src/pages`
|
||||
if (
|
||||
!pagesDir &&
|
||||
fileKeys.some((file) =>
|
||||
fileKeys.some(file =>
|
||||
file.startsWith(path.join(entryDirectory, 'src/pages'))
|
||||
)
|
||||
) {
|
||||
@@ -260,7 +260,7 @@ async function getRoutes(
|
||||
entryDirectory,
|
||||
dynamicPages,
|
||||
true
|
||||
).then((arr) =>
|
||||
).then(arr =>
|
||||
arr.map((route: Source) => {
|
||||
// convert to make entire RegExp match as one group
|
||||
route.src = route.src
|
||||
@@ -287,7 +287,7 @@ async function getRoutes(
|
||||
};
|
||||
|
||||
// Only add the route if a page is not already using it
|
||||
if (!routes.some((r) => (r as Source).src === route.src)) {
|
||||
if (!routes.some(r => (r as Source).src === route.src)) {
|
||||
routes.push(route);
|
||||
}
|
||||
}
|
||||
@@ -420,7 +420,7 @@ export async function getDynamicRoutes(
|
||||
dest: `${!isDev ? path.join('/', entryDirectory, page) : page}${
|
||||
routeKeys
|
||||
? `?${Object.keys(routeKeys)
|
||||
.map((key) => `${routeKeys[key]}=$${key}`)
|
||||
.map(key => `${routeKeys[key]}=$${key}`)
|
||||
.join('&')}`
|
||||
: ''
|
||||
}`,
|
||||
@@ -479,13 +479,13 @@ export async function getDynamicRoutes(
|
||||
});
|
||||
}
|
||||
|
||||
const pageMatchers = getSortedRoutes(dynamicPages).map((pageName) => ({
|
||||
const pageMatchers = getSortedRoutes(dynamicPages).map(pageName => ({
|
||||
pageName,
|
||||
matcher: getRouteRegex && getRouteRegex(pageName).re,
|
||||
}));
|
||||
|
||||
const routes: Source[] = [];
|
||||
pageMatchers.forEach((pageMatcher) => {
|
||||
pageMatchers.forEach(pageMatcher => {
|
||||
// in `vercel dev` we don't need to prefix the destination
|
||||
const dest = !isDev
|
||||
? path.join('/', entryDirectory, pageMatcher.pageName)
|
||||
@@ -693,7 +693,7 @@ export type NextPrerenderedRoutes = {
|
||||
};
|
||||
};
|
||||
|
||||
legacyBlockingRoutes: {
|
||||
blockingFallbackRoutes: {
|
||||
[route: string]: {
|
||||
routeRegex: string;
|
||||
dataRoute: string;
|
||||
@@ -797,7 +797,7 @@ export async function getPrerenderManifest(
|
||||
if (!hasManifest) {
|
||||
return {
|
||||
staticRoutes: {},
|
||||
legacyBlockingRoutes: {},
|
||||
blockingFallbackRoutes: {},
|
||||
fallbackRoutes: {},
|
||||
bypassToken: null,
|
||||
omittedRoutes: [],
|
||||
@@ -855,14 +855,14 @@ export async function getPrerenderManifest(
|
||||
|
||||
const ret: NextPrerenderedRoutes = {
|
||||
staticRoutes: {},
|
||||
legacyBlockingRoutes: {},
|
||||
blockingFallbackRoutes: {},
|
||||
fallbackRoutes: {},
|
||||
bypassToken:
|
||||
(manifest.preview && manifest.preview.previewModeId) || null,
|
||||
omittedRoutes: [],
|
||||
};
|
||||
|
||||
routes.forEach((route) => {
|
||||
routes.forEach(route => {
|
||||
const {
|
||||
initialRevalidateSeconds,
|
||||
dataRoute,
|
||||
@@ -878,7 +878,7 @@ export async function getPrerenderManifest(
|
||||
};
|
||||
});
|
||||
|
||||
lazyRoutes.forEach((lazyRoute) => {
|
||||
lazyRoutes.forEach(lazyRoute => {
|
||||
const {
|
||||
routeRegex,
|
||||
fallback,
|
||||
@@ -894,7 +894,7 @@ export async function getPrerenderManifest(
|
||||
dataRouteRegex,
|
||||
};
|
||||
} else {
|
||||
ret.legacyBlockingRoutes[lazyRoute] = {
|
||||
ret.blockingFallbackRoutes[lazyRoute] = {
|
||||
routeRegex,
|
||||
dataRoute,
|
||||
dataRouteRegex,
|
||||
@@ -910,13 +910,13 @@ export async function getPrerenderManifest(
|
||||
|
||||
const ret: NextPrerenderedRoutes = {
|
||||
staticRoutes: {},
|
||||
legacyBlockingRoutes: {},
|
||||
blockingFallbackRoutes: {},
|
||||
fallbackRoutes: {},
|
||||
bypassToken: manifest.preview.previewModeId,
|
||||
omittedRoutes: [],
|
||||
};
|
||||
|
||||
routes.forEach((route) => {
|
||||
routes.forEach(route => {
|
||||
const {
|
||||
initialRevalidateSeconds,
|
||||
dataRoute,
|
||||
@@ -932,7 +932,7 @@ export async function getPrerenderManifest(
|
||||
};
|
||||
});
|
||||
|
||||
lazyRoutes.forEach((lazyRoute) => {
|
||||
lazyRoutes.forEach(lazyRoute => {
|
||||
const {
|
||||
routeRegex,
|
||||
fallback,
|
||||
@@ -940,19 +940,24 @@ export async function getPrerenderManifest(
|
||||
dataRouteRegex,
|
||||
} = manifest.dynamicRoutes[lazyRoute];
|
||||
|
||||
if (!fallback) {
|
||||
if (typeof fallback === 'string') {
|
||||
ret.fallbackRoutes[lazyRoute] = {
|
||||
routeRegex,
|
||||
fallback,
|
||||
dataRoute,
|
||||
dataRouteRegex,
|
||||
};
|
||||
} else if (fallback === null) {
|
||||
ret.blockingFallbackRoutes[lazyRoute] = {
|
||||
routeRegex,
|
||||
dataRoute,
|
||||
dataRouteRegex,
|
||||
};
|
||||
} else {
|
||||
// Fallback behavior is disabled, all routes would've been provided
|
||||
// in the top-level `routes` key (`staticRoutes`).
|
||||
ret.omittedRoutes.push(lazyRoute);
|
||||
return;
|
||||
}
|
||||
|
||||
ret.fallbackRoutes[lazyRoute] = {
|
||||
routeRegex,
|
||||
fallback,
|
||||
dataRoute,
|
||||
dataRouteRegex,
|
||||
};
|
||||
});
|
||||
|
||||
return ret;
|
||||
@@ -960,7 +965,7 @@ export async function getPrerenderManifest(
|
||||
default: {
|
||||
return {
|
||||
staticRoutes: {},
|
||||
legacyBlockingRoutes: {},
|
||||
blockingFallbackRoutes: {},
|
||||
fallbackRoutes: {},
|
||||
bypassToken: null,
|
||||
omittedRoutes: [],
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
module.exports = { trailingSlash: true };
|
||||
module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
trailingSlash: true,
|
||||
};
|
||||
|
||||
@@ -33,6 +33,48 @@
|
||||
"path": "/test.txt",
|
||||
"status": 200,
|
||||
"mustContain": "this is a file"
|
||||
},
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/blog/post-1",
|
||||
"status": 308,
|
||||
"responseHeaders": {
|
||||
"refresh": "/url=/blog/post-1/$/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/blog/post-1/",
|
||||
"status": 200,
|
||||
"mustContain": "post: <!-- -->post-1"
|
||||
},
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/_next/data/testing-build-id/blog/post-1.json/",
|
||||
"status": 308,
|
||||
"responseHeaders": {
|
||||
"refresh": "/url=/_next/data/testing-build-id/blog/post-1.json$/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/_next/data/testing-build-id/blog/post-1.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"post-1\""
|
||||
},
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/api/hello",
|
||||
"status": 308,
|
||||
"responseHeaders": {
|
||||
"refresh": "/url=/api/hello/$/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/api/hello/",
|
||||
"status": 200,
|
||||
"mustContain": "hello from API"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
export default function Page() {
|
||||
return <p>nested page</p>;
|
||||
}
|
||||
|
||||
export const getServerSideProps = () => {
|
||||
return {
|
||||
props: {
|
||||
hello: 'world',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
3
packages/now-next/test/fixtures/00-trailing-slash-add/pages/api/hello.js
vendored
Normal file
3
packages/now-next/test/fixtures/00-trailing-slash-add/pages/api/hello.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default (req, res) => {
|
||||
res.end('hello from API');
|
||||
};
|
||||
11
packages/now-next/test/fixtures/00-trailing-slash-add/pages/blog/[post].js
vendored
Normal file
11
packages/now-next/test/fixtures/00-trailing-slash-add/pages/blog/[post].js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
export default function Page({ post }) {
|
||||
return <p>post: {post}</p>;
|
||||
}
|
||||
|
||||
export const getServerSideProps = ({ params }) => {
|
||||
return {
|
||||
props: {
|
||||
post: params.post,
|
||||
},
|
||||
};
|
||||
};
|
||||
108
packages/now-next/test/fixtures/30-monorepo-no-script/.gitignore
vendored
Normal file
108
packages/now-next/test/fixtures/30-monorepo-no-script/.gitignore
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# 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
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# IDEA
|
||||
.idea/
|
||||
*.iml
|
||||
7
packages/now-next/test/fixtures/30-monorepo-no-script/babel.config.js
vendored
Normal file
7
packages/now-next/test/fixtures/30-monorepo-no-script/babel.config.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = api => {
|
||||
api.cache(true);
|
||||
|
||||
const presets = [require.resolve('next/babel')];
|
||||
|
||||
return { presets, plugins: [] };
|
||||
};
|
||||
31
packages/now-next/test/fixtures/30-monorepo-no-script/now.json
vendored
Normal file
31
packages/now-next/test/fixtures/30-monorepo-no-script/now.json
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"version": 2,
|
||||
"uploadNowJson": true,
|
||||
"builds": [{ "src": "packages/www/package.json", "use": "@vercel/next" }],
|
||||
"routes": [{ "src": "/(.*)", "dest": "/packages/www/$1" }],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/",
|
||||
"status": 200,
|
||||
"mustContain": "Hello World"
|
||||
},
|
||||
{
|
||||
"logMustContain": "Your application is being built using `next build`"
|
||||
},
|
||||
{
|
||||
"logMustContain": "WARNING: your application is being opted out of @vercel/next's optimized lambdas mode due to legacy routes"
|
||||
},
|
||||
{
|
||||
"logMustNotContain": "WARNING: Your application is being opted out of \"@vercel/next\" optimized lambdas mode due to `functions` config"
|
||||
},
|
||||
{
|
||||
"logMustNotContain": "Traced Next.js serverless functions for external files in"
|
||||
},
|
||||
{
|
||||
"logMustNotContain": "All serverless functions created in"
|
||||
},
|
||||
{
|
||||
"logMustNotContain": "Compressed shared serverless function files"
|
||||
}
|
||||
]
|
||||
}
|
||||
11
packages/now-next/test/fixtures/30-monorepo-no-script/package.json
vendored
Normal file
11
packages/now-next/test/fixtures/30-monorepo-no-script/package.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"dependencies": {
|
||||
"next": "9.5.1",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1"
|
||||
}
|
||||
}
|
||||
8
packages/now-next/test/fixtures/30-monorepo-no-script/packages/www/next.config.js
vendored
Normal file
8
packages/now-next/test/fixtures/30-monorepo-no-script/packages/www/next.config.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
poweredByHeader: false,
|
||||
|
||||
webpack: (config, { defaultLoaders }) => {
|
||||
defaultLoaders.babel.options.rootMode = 'upward';
|
||||
return config;
|
||||
},
|
||||
};
|
||||
6
packages/now-next/test/fixtures/30-monorepo-no-script/packages/www/package.json
vendored
Normal file
6
packages/now-next/test/fixtures/30-monorepo-no-script/packages/www/package.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "@vercel-crash-demo/www",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"sideEffects": false
|
||||
}
|
||||
7
packages/now-next/test/fixtures/30-monorepo-no-script/packages/www/pages/index.jsx
vendored
Normal file
7
packages/now-next/test/fixtures/30-monorepo-no-script/packages/www/pages/index.jsx
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
const HelloWorld = () => (
|
||||
<h1>Hello World</h1>
|
||||
);
|
||||
|
||||
export default HelloWorld;
|
||||
5516
packages/now-next/test/fixtures/30-monorepo-no-script/yarn.lock
vendored
Normal file
5516
packages/now-next/test/fixtures/30-monorepo-no-script/yarn.lock
vendored
Normal file
File diff suppressed because it is too large
Load Diff
54
packages/now-next/test/fixtures/31-blocking-fallback/additional.js
vendored
Normal file
54
packages/now-next/test/fixtures/31-blocking-fallback/additional.js
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/* eslint-env jest */
|
||||
const fetch = require('node-fetch');
|
||||
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));
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}/regenerated/blue`);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
let $ = cheerio.load(await res.text());
|
||||
const initialTime = $('#time').text();
|
||||
expect($('#slug').text()).toBe('blue');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/regenerated/blue`);
|
||||
expect(res2.status).toBe(200);
|
||||
|
||||
$ = cheerio.load(await res2.text());
|
||||
expect($('#slug').text()).toBe('blue');
|
||||
expect(initialTime).not.toBe($('#time').text());
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /_next/data dynamic pathname', async () => {
|
||||
// wait for revalidation to expire
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/regenerated/blue.json`
|
||||
);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
const { pageProps: data } = await res.json();
|
||||
const initialTime = data.time;
|
||||
expect(data.slug).toBe('blue');
|
||||
expect(isNaN(initialTime)).toBe(false);
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const res2 = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/regenerated/blue.json`
|
||||
);
|
||||
expect(res2.status).toBe(200);
|
||||
|
||||
const { pageProps: data2 } = await res2.json();
|
||||
expect(data2.slug).toBe('blue');
|
||||
expect(initialTime).not.toBe(data2.time);
|
||||
});
|
||||
};
|
||||
5
packages/now-next/test/fixtures/31-blocking-fallback/next.config.js
vendored
Normal file
5
packages/now-next/test/fixtures/31-blocking-fallback/next.config.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
};
|
||||
62
packages/now-next/test/fixtures/31-blocking-fallback/now.json
vendored
Normal file
62
packages/now-next/test/fixtures/31-blocking-fallback/now.json
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next"
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/fixed/yellow",
|
||||
"status": 200,
|
||||
"mustContain": "yellow"
|
||||
},
|
||||
{ "delay": 2000 },
|
||||
{
|
||||
"path": "/fixed/yellow",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "HIT"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/fixed/yellow.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "HIT"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/fixed/yellow",
|
||||
"status": 200,
|
||||
"mustContain": "yellow"
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/fixed/yellow.json",
|
||||
"status": 200,
|
||||
"mustContain": "yellow"
|
||||
},
|
||||
|
||||
{
|
||||
"path": "/regenerated/blue",
|
||||
"status": 200,
|
||||
"mustContain": "blue"
|
||||
},
|
||||
{ "delay": 2000 },
|
||||
{
|
||||
"path": "/regenerated/blue",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "/HIT|STALE/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/regenerated/blue.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "/HIT|STALE/"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
7
packages/now-next/test/fixtures/31-blocking-fallback/package.json
vendored
Normal file
7
packages/now-next/test/fixtures/31-blocking-fallback/package.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
23
packages/now-next/test/fixtures/31-blocking-fallback/pages/fixed/[slug].js
vendored
Normal file
23
packages/now-next/test/fixtures/31-blocking-fallback/pages/fixed/[slug].js
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
export default function TestPage({ slug, time }) {
|
||||
return (
|
||||
<>
|
||||
Slug: <div id="slug">{slug}</div>
|
||||
<br />
|
||||
Time: <div id="time">{time}</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function getStaticProps({ params }) {
|
||||
return {
|
||||
props: {
|
||||
slug: params.slug,
|
||||
time: new Date().getTime(),
|
||||
},
|
||||
revalidate: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function getStaticPaths() {
|
||||
return { paths: [], fallback: 'unstable_blocking' };
|
||||
}
|
||||
23
packages/now-next/test/fixtures/31-blocking-fallback/pages/regenerated/[slug].js
vendored
Normal file
23
packages/now-next/test/fixtures/31-blocking-fallback/pages/regenerated/[slug].js
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
export default function TestPage({ slug, time }) {
|
||||
return (
|
||||
<>
|
||||
Slug: <div id="slug">{slug}</div>
|
||||
<br />
|
||||
Time: <div id="time">{time}</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function getStaticProps({ params }) {
|
||||
return {
|
||||
props: {
|
||||
slug: params.slug,
|
||||
time: new Date().getTime(),
|
||||
},
|
||||
revalidate: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function getStaticPaths() {
|
||||
return { paths: [], fallback: 'unstable_blocking' };
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/node",
|
||||
"version": "1.7.4",
|
||||
"version": "1.7.5-canary.1",
|
||||
"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",
|
||||
"@zeit/ncc": "0.20.4",
|
||||
"@zeit/node-file-trace": "0.8.1",
|
||||
"@zeit/node-file-trace": "0.8.2",
|
||||
"content-type": "1.0.4",
|
||||
"cookie": "0.4.0",
|
||||
"etag": "1.8.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/routing-utils",
|
||||
"version": "1.8.3",
|
||||
"version": "1.8.4-canary.0",
|
||||
"description": "Vercel routing utilities",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -218,18 +218,42 @@ function replaceSegments(
|
||||
return destination;
|
||||
}
|
||||
|
||||
function safelyCompile(str: string, indexes: { [k: string]: string }): string {
|
||||
if (!str) {
|
||||
return str;
|
||||
function safelyCompile(
|
||||
value: string,
|
||||
indexes: { [k: string]: string }
|
||||
): string {
|
||||
if (!value) {
|
||||
return value;
|
||||
}
|
||||
// path-to-regexp cannot compile question marks
|
||||
return str
|
||||
.split('?')
|
||||
.map(part => {
|
||||
const compiler = compile(part);
|
||||
return compiler(indexes);
|
||||
})
|
||||
.join('?');
|
||||
|
||||
for (const key of Object.keys(indexes)) {
|
||||
if (value.includes(`:${key}`)) {
|
||||
value = value
|
||||
.replace(
|
||||
new RegExp(`:${key}\\*`, 'g'),
|
||||
`:${key}--ESCAPED_PARAM_ASTERISK`
|
||||
)
|
||||
.replace(
|
||||
new RegExp(`:${key}\\?`, 'g'),
|
||||
`:${key}--ESCAPED_PARAM_QUESTION`
|
||||
)
|
||||
.replace(new RegExp(`:${key}\\+`, 'g'), `:${key}--ESCAPED_PARAM_PLUS`)
|
||||
.replace(
|
||||
new RegExp(`:${key}(?!\\w)`, 'g'),
|
||||
`--ESCAPED_PARAM_COLON${key}`
|
||||
);
|
||||
}
|
||||
}
|
||||
value = value
|
||||
.replace(/(:|\*|\?|\+|\(|\)|\{|\})/g, '\\$1')
|
||||
.replace(/--ESCAPED_PARAM_PLUS/g, '+')
|
||||
.replace(/--ESCAPED_PARAM_COLON/g, ':')
|
||||
.replace(/--ESCAPED_PARAM_QUESTION/g, '?')
|
||||
.replace(/--ESCAPED_PARAM_ASTERISK/g, '*');
|
||||
|
||||
// the value needs to start with a forward-slash to be compiled
|
||||
// correctly
|
||||
return compile(`/${value}`, { validate: false })(indexes).substr(1);
|
||||
}
|
||||
|
||||
function toSegmentDest(index: number): string {
|
||||
|
||||
@@ -508,6 +508,60 @@ test('convertHeaders', () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/like/params/:path',
|
||||
headers: [
|
||||
{
|
||||
key: 'x-path',
|
||||
value: ':path',
|
||||
},
|
||||
{
|
||||
key: 'some:path',
|
||||
value: 'hi',
|
||||
},
|
||||
{
|
||||
key: 'x-test',
|
||||
value: 'some:value*',
|
||||
},
|
||||
{
|
||||
key: 'x-test-2',
|
||||
value: 'value*',
|
||||
},
|
||||
{
|
||||
key: 'x-test-3',
|
||||
value: ':value?',
|
||||
},
|
||||
{
|
||||
key: 'x-test-4',
|
||||
value: ':value+',
|
||||
},
|
||||
{
|
||||
key: 'x-test-5',
|
||||
value: 'something https:',
|
||||
},
|
||||
{
|
||||
key: 'x-test-6',
|
||||
value: ':hello(world)',
|
||||
},
|
||||
{
|
||||
key: 'x-test-7',
|
||||
value: 'hello(world)',
|
||||
},
|
||||
{
|
||||
key: 'x-test-8',
|
||||
value: 'hello{1,}',
|
||||
},
|
||||
{
|
||||
key: 'x-test-9',
|
||||
value: ':hello{1,2}',
|
||||
},
|
||||
{
|
||||
key: 'content-security-policy',
|
||||
value:
|
||||
"default-src 'self'; img-src *; media-src media1.com media2.com; script-src userscripts.example.com/:path",
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const expected = [
|
||||
@@ -526,6 +580,25 @@ test('convertHeaders', () => {
|
||||
headers: { 'on-blog': '$1', $1: 'blog' },
|
||||
continue: true,
|
||||
},
|
||||
{
|
||||
continue: true,
|
||||
headers: {
|
||||
'content-security-policy':
|
||||
"default-src 'self'; img-src *; media-src media1.com media2.com; script-src userscripts.example.com/$1",
|
||||
some$1: 'hi',
|
||||
'x-path': '$1',
|
||||
'x-test': 'some:value*',
|
||||
'x-test-2': 'value*',
|
||||
'x-test-3': ':value?',
|
||||
'x-test-4': ':value+',
|
||||
'x-test-5': 'something https:',
|
||||
'x-test-6': ':hello(world)',
|
||||
'x-test-7': 'hello(world)',
|
||||
'x-test-8': 'hello{1,}',
|
||||
'x-test-9': ':hello{1,2}',
|
||||
},
|
||||
src: '^\\/like\\/params(?:\\/([^\\/]+?))$',
|
||||
},
|
||||
];
|
||||
|
||||
deepEqual(actual, expected);
|
||||
@@ -534,12 +607,14 @@ test('convertHeaders', () => {
|
||||
['hello/world/file.eot', 'another/font.ttf', 'dir/arial.font.css'],
|
||||
['404.html'],
|
||||
['/blog/first-post', '/blog/another/one'],
|
||||
['/like/params/first', '/like/params/second'],
|
||||
];
|
||||
|
||||
const mustNotMatch = [
|
||||
['hello/file.jpg', 'hello/font-css', 'dir/arial.font-css'],
|
||||
['403.html', '500.html'],
|
||||
['/blogg', '/random'],
|
||||
['/non-match', '/like/params', '/like/params/'],
|
||||
];
|
||||
|
||||
assertRegexMatches(actual, mustMatch, mustNotMatch);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/redwood",
|
||||
"version": "0.0.2-canary.1",
|
||||
"version": "0.0.2-canary.3",
|
||||
"main": "./dist/index.js",
|
||||
"license": "MIT",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
@@ -18,8 +18,7 @@
|
||||
"prepublishOnly": "./build.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@netlify/zip-it-and-ship-it": "1.2.0",
|
||||
"is-port-reachable": "3.0.0"
|
||||
"@netlify/zip-it-and-ship-it": "1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/aws-lambda": "8.10.19",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { join, dirname, relative, parse as parsePath, sep } from 'path';
|
||||
import { ChildProcess, SpawnOptions } from 'child_process';
|
||||
import {
|
||||
BuildOptions,
|
||||
Lambda,
|
||||
@@ -13,54 +12,22 @@ import {
|
||||
getSpawnOptions,
|
||||
runNpmInstall,
|
||||
execCommand,
|
||||
spawnCommand,
|
||||
readConfigFile,
|
||||
FileBlob,
|
||||
FileFsRef,
|
||||
NowBuildError,
|
||||
} from '@vercel/build-utils';
|
||||
import { makeAwsLauncher } from './launcher';
|
||||
const {
|
||||
getDependencies,
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
} = require('@netlify/zip-it-and-ship-it/src/dependencies.js');
|
||||
//@ts-ignore
|
||||
import isPortReachable from 'is-port-reachable';
|
||||
|
||||
interface RedwoodConfig {
|
||||
web?: {
|
||||
port?: number;
|
||||
apiProxyPath?: string;
|
||||
};
|
||||
api?: {
|
||||
port?: number;
|
||||
};
|
||||
browser?: {
|
||||
open?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
const LAUNCHER_FILENAME = '___vc_launcher';
|
||||
const BRIDGE_FILENAME = '___vc_bridge';
|
||||
const HELPERS_FILENAME = '___vc_helpers';
|
||||
const SOURCEMAP_SUPPORT_FILENAME = '__vc_sourcemap_support';
|
||||
|
||||
const entrypointToPort = new Map<string, number>();
|
||||
const childProcesses = new Set<ChildProcess>();
|
||||
export const version = 2;
|
||||
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
async function waitForPort(port: number): Promise<boolean> {
|
||||
for (let i = 0; i < 500; i++) {
|
||||
if (await isPortReachable(port)) {
|
||||
return true;
|
||||
}
|
||||
await sleep(100);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function build({
|
||||
workPath,
|
||||
files,
|
||||
@@ -89,46 +56,10 @@ export async function build({
|
||||
|
||||
const {
|
||||
buildCommand = 'yarn rw db up --no-db-client --auto-approve && yarn rw build',
|
||||
devCommand = 'yarn rw dev',
|
||||
} = config;
|
||||
|
||||
if (meta.isDev) {
|
||||
const toml = await readConfigFile<RedwoodConfig>(
|
||||
join(mountpoint, 'redwood.toml')
|
||||
);
|
||||
const webPort = toml?.web?.port || 8910;
|
||||
const apiPort = toml?.web?.port || 8911;
|
||||
let devPort = entrypointToPort.get(entrypoint);
|
||||
|
||||
if (typeof devPort === 'number') {
|
||||
debug('`%s` server already running for %j', devCommand, entrypoint);
|
||||
} else {
|
||||
devPort = webPort;
|
||||
entrypointToPort.set(entrypoint, devPort);
|
||||
|
||||
const opts: SpawnOptions = {
|
||||
cwd: mountpoint,
|
||||
stdio: 'inherit',
|
||||
env: { ...spawnOpts.env, PORT: String(devPort) },
|
||||
};
|
||||
|
||||
const child = spawnCommand(devCommand, opts);
|
||||
child.on('exit', () => entrypointToPort.delete(entrypoint));
|
||||
childProcesses.add(child);
|
||||
|
||||
const found = await waitForPort(devPort);
|
||||
if (!found) {
|
||||
throw new NowBuildError({
|
||||
code: 'REDWOOD_PORT_UNAVAILABLE',
|
||||
message: `Failed to detect a server running on port ${devPort}`,
|
||||
action: 'More Details',
|
||||
link:
|
||||
'https://err.sh/vercel/vercel/now-static-build-failed-to-detect-a-server',
|
||||
});
|
||||
}
|
||||
|
||||
debug('Detected dev server for %j', entrypoint);
|
||||
}
|
||||
debug('Detected @vercel/redwood dev, returning routes...');
|
||||
|
||||
let srcBase = mountpoint.replace(/^\.\/?/, '');
|
||||
|
||||
@@ -138,16 +69,11 @@ export async function build({
|
||||
|
||||
return {
|
||||
routes: [
|
||||
{
|
||||
src: `${srcBase}/api/(.*)`,
|
||||
dest: `http://localhost:${apiPort}/$1`,
|
||||
},
|
||||
{
|
||||
src: `${srcBase}/(.*)`,
|
||||
dest: `http://localhost:${webPort}/$1`,
|
||||
dest: `http://localhost:$PORT/$1`,
|
||||
},
|
||||
],
|
||||
watch: [join(srcBase, '**/*')],
|
||||
output: {},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@redwoodjs/api": "0.14.0"
|
||||
"@redwoodjs/api": "0.15.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@redwoodjs/core": "0.14.0"
|
||||
"@redwoodjs/core": "0.15.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"method": "POST",
|
||||
"headers": { "Accept": "application/json" },
|
||||
"body": { "query": "{ redwood { version } }" },
|
||||
"mustContain": "0.14.0"
|
||||
"mustContain": "0.15.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
"defaults"
|
||||
],
|
||||
"dependencies": {
|
||||
"@redwoodjs/router": "0.14.0",
|
||||
"@redwoodjs/web": "0.14.0",
|
||||
"@redwoodjs/router": "0.15.0",
|
||||
"@redwoodjs/web": "0.15.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2292,10 +2292,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@zeit/ncc/-/ncc-0.20.4.tgz#00f0a25a88cac3712af4ba66561d9e281c6f05c9"
|
||||
integrity sha512-fmq+F/QxPec+k/zvT7HiVpk7oiGFseS6brfT/AYqmCUp6QFRK7vZf2Ref46MImsg/g2W3g5X6SRvGRmOAvEfdA==
|
||||
|
||||
"@zeit/node-file-trace@0.8.1":
|
||||
version "0.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@zeit/node-file-trace/-/node-file-trace-0.8.1.tgz#ace86b366c86f0522e953a2441dfc915fd485df4"
|
||||
integrity sha512-r92pdExhK1cf9Ao5LaT/Ii7JLwv9O4o987cB1SiFIbAnxvW4+xqR1oCYRRvtjU1ildKKGpiBBjGRChyeYRcvxw==
|
||||
"@zeit/node-file-trace@0.8.2":
|
||||
version "0.8.2"
|
||||
resolved "https://registry.yarnpkg.com/@zeit/node-file-trace/-/node-file-trace-0.8.2.tgz#a00d21a98015c4ea18c8b1104ad60ea91b42dcca"
|
||||
integrity sha512-M6KR95Xz9af8kB8X7e4inhoIjVoKNT6WxjLQwPByAAdCP6JdCg3Fb0dbTh2WelKlAibUpfS9nANU/HUDcfedSA==
|
||||
dependencies:
|
||||
acorn "^7.1.1"
|
||||
acorn-class-fields "^0.3.2"
|
||||
|
||||
Reference in New Issue
Block a user