mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-26 11:49:15 +00:00
Compare commits
23 Commits
@now/node@
...
@now/node@
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4ce0d6802 | ||
|
|
f477ee6e3b | ||
|
|
59b4029a0c | ||
|
|
8411b53fa3 | ||
|
|
7651bcf4a4 | ||
|
|
6fd1b50924 | ||
|
|
a03b8689e9 | ||
|
|
22c8ed15c7 | ||
|
|
e1f7b859b6 | ||
|
|
c75cc99f1e | ||
|
|
30ddd29889 | ||
|
|
21337de7cd | ||
|
|
a6686f9ff5 | ||
|
|
65c621bd55 | ||
|
|
0827d3514d | ||
|
|
7bc5d9fb5b | ||
|
|
c53106ecee | ||
|
|
91e4d18ab8 | ||
|
|
33cd78b93a | ||
|
|
a765d27e5a | ||
|
|
e65ff4bfd5 | ||
|
|
30a4787390 | ||
|
|
36f6f1db77 |
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -1,5 +1,8 @@
|
||||
# Ignore test fixtures in GitHub Languages
|
||||
# See https://github.com/github/linguist#vendored-code
|
||||
examples/* linguist-vendored
|
||||
utils/* linguist-vendored
|
||||
test/* linguist-vendored
|
||||
packages/*/test/* linguist-vendored
|
||||
|
||||
# Go build fails with Windows line endings.
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -23,5 +23,5 @@ packages/now-cli/test/fixtures/integration
|
||||
test/lib/deployment/failed-page.txt
|
||||
.DS_Store
|
||||
.next
|
||||
.env
|
||||
public
|
||||
/.env
|
||||
/public
|
||||
|
||||
@@ -8,7 +8,7 @@ module.exports = {
|
||||
resolve: `gatsby-plugin-manifest`,
|
||||
options: {
|
||||
name: 'Gatsby + Node.js (TypeScript) API',
|
||||
short_name: 'Gatbsy + Node.js (TypeScript)',
|
||||
short_name: 'Gatsby + Node.js (TypeScript)',
|
||||
start_url: '/',
|
||||
icon: 'src/images/gatsby-icon.png',
|
||||
},
|
||||
|
||||
BIN
examples/ionic-react/public/assets/icon/favicon.png
Normal file
BIN
examples/ionic-react/public/assets/icon/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 930 B |
BIN
examples/ionic-react/public/assets/icon/icon.png
Normal file
BIN
examples/ionic-react/public/assets/icon/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
1
examples/ionic-react/public/assets/shapes.svg
Normal file
1
examples/ionic-react/public/assets/shapes.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="350" height="140" xmlns="http://www.w3.org/2000/svg" style="background:#f6f7f9"><g fill="none" fill-rule="evenodd"><path fill="#F04141" style="mix-blend-mode:multiply" d="M61.905-34.23l96.194 54.51-66.982 54.512L22 34.887z"/><circle fill="#10DC60" style="mix-blend-mode:multiply" cx="155.5" cy="135.5" r="57.5"/><path fill="#3880FF" style="mix-blend-mode:multiply" d="M208.538 9.513l84.417 15.392L223.93 93.93z"/><path fill="#FFCE00" style="mix-blend-mode:multiply" d="M268.625 106.557l46.332-26.75 46.332 26.75v53.5l-46.332 26.75-46.332-26.75z"/><circle fill="#7044FF" style="mix-blend-mode:multiply" cx="299.5" cy="9.5" r="38.5"/><rect fill="#11D3EA" style="mix-blend-mode:multiply" transform="rotate(-60 148.47 37.886)" x="143.372" y="-7.056" width="10.196" height="89.884" rx="5.098"/><path d="M-25.389 74.253l84.86 8.107c5.498.525 9.53 5.407 9.004 10.905a10 10 0 0 1-.057.477l-12.36 85.671a10.002 10.002 0 0 1-11.634 8.42l-86.351-15.226c-5.44-.959-9.07-6.145-8.112-11.584l13.851-78.551a10 10 0 0 1 10.799-8.219z" fill="#7044FF" style="mix-blend-mode:multiply"/><circle fill="#0CD1E8" style="mix-blend-mode:multiply" cx="273.5" cy="106.5" r="20.5"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
30
examples/ionic-react/public/index.html
Normal file
30
examples/ionic-react/public/index.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Ionic App</title>
|
||||
|
||||
<base href="/" />
|
||||
|
||||
<meta
|
||||
name="viewport"
|
||||
content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||
/>
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
<meta name="msapplication-tap-highlight" content="no" />
|
||||
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
|
||||
<link rel="shortcut icon" type="image/png" href="%PUBLIC_URL%/assets/icon/favicon.png" />
|
||||
|
||||
<!-- add to homescreen for ios -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-title" content="Ionic App" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
21
examples/ionic-react/public/manifest.json
Normal file
21
examples/ionic-react/public/manifest.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"short_name": "Ionic App",
|
||||
"name": "My Ionic App",
|
||||
"icons": [
|
||||
{
|
||||
"src": "assets/icon/favicon.png",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "assets/icon/icon.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/build-utils",
|
||||
"version": "2.2.1",
|
||||
"version": "2.2.2-canary.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -50,7 +50,7 @@ export interface Config {
|
||||
export interface Meta {
|
||||
isDev?: boolean;
|
||||
skipDownload?: boolean;
|
||||
requestPath?: string;
|
||||
requestPath?: string | null;
|
||||
filesChanged?: string[];
|
||||
filesRemoved?: string[];
|
||||
env?: Env;
|
||||
@@ -187,6 +187,42 @@ export interface ShouldServeOptions {
|
||||
config: Config;
|
||||
}
|
||||
|
||||
export interface StartDevServerOptions {
|
||||
/**
|
||||
* Name of entrypoint file for this particular build job. Value
|
||||
* `files[entrypoint]` is guaranteed to exist and be a valid File reference.
|
||||
* `entrypoint` is always a discrete file and never a glob, since globs are
|
||||
* expanded into separate builds at deployment time.
|
||||
*/
|
||||
entrypoint: string;
|
||||
|
||||
/**
|
||||
* A writable temporary directory where you are encouraged to perform your
|
||||
* build process. This directory will be populated with the restored cache.
|
||||
*/
|
||||
workPath: string;
|
||||
}
|
||||
|
||||
export interface StartDevServerSuccess {
|
||||
/**
|
||||
* Port number where the dev server can be connected to, assumed to be running
|
||||
* on `localhost`.
|
||||
*/
|
||||
port: number;
|
||||
|
||||
/**
|
||||
* Process ID number of the dev server. Useful for the `now dev` server to
|
||||
* shut down the dev server once an HTTP request has been fulfilled.
|
||||
*/
|
||||
pid: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* `startDevServer()` may return `null` to opt-out of spawning a dev server for
|
||||
* a given `entrypoint`.
|
||||
*/
|
||||
export type StartDevServerResult = StartDevServerSuccess | null;
|
||||
|
||||
/**
|
||||
* Credit to Iain Reid, MIT license.
|
||||
* Source: https://gist.github.com/iainreid820/5c1cc527fe6b5b7dba41fec7fe54bf6e
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "now",
|
||||
"version": "18.0.0",
|
||||
"version": "18.0.1-canary.3",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Now",
|
||||
@@ -64,6 +64,7 @@
|
||||
"devDependencies": {
|
||||
"@sentry/node": "5.5.0",
|
||||
"@sindresorhus/slugify": "0.11.0",
|
||||
"@tootallnate/once": "1.0.0",
|
||||
"@types/ansi-escapes": "3.0.0",
|
||||
"@types/ansi-regex": "4.0.0",
|
||||
"@types/async-retry": "1.2.1",
|
||||
|
||||
@@ -49,6 +49,7 @@ const help = () => {
|
||||
--ca ${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} CA certificate chain file
|
||||
-N, --next Show next page of results
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
@@ -61,6 +62,12 @@ const help = () => {
|
||||
${chalk.gray('–')} Remove a certificate
|
||||
|
||||
${chalk.cyan('$ now certs rm id')}
|
||||
|
||||
${chalk.gray('–')} Paginate results, where ${chalk.dim(
|
||||
'`1584722256178`'
|
||||
)} is the time in milliseconds since the UNIX epoch.
|
||||
|
||||
${chalk.cyan(`$ now certs ls --next 1584722256178`)}
|
||||
`);
|
||||
};
|
||||
|
||||
@@ -69,7 +76,7 @@ const COMMAND_CONFIG = {
|
||||
issue: ['issue'],
|
||||
ls: ['ls', 'list'],
|
||||
renew: ['renew'],
|
||||
rm: ['rm', 'remove']
|
||||
rm: ['rm', 'remove'],
|
||||
};
|
||||
|
||||
export default async function main(ctx: NowContext) {
|
||||
@@ -80,10 +87,11 @@ export default async function main(ctx: NowContext) {
|
||||
'--challenge-only': Boolean,
|
||||
'--overwrite': Boolean,
|
||||
'--output': String,
|
||||
'--after': String,
|
||||
'--crt': String,
|
||||
'--key': String,
|
||||
'--ca': String
|
||||
'--ca': String,
|
||||
'--next': Number,
|
||||
'-N': '--next',
|
||||
});
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import plural from 'pluralize';
|
||||
import psl from 'psl';
|
||||
import table from 'text-table';
|
||||
// @ts-ignore
|
||||
import Now from '../../util';
|
||||
@@ -10,14 +8,14 @@ import Client from '../../util/client';
|
||||
import getScope from '../../util/get-scope';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import getCerts from '../../util/certs/get-certs';
|
||||
import { CertNotFound } from '../../util/errors-ts';
|
||||
import strlen from '../../util/strlen';
|
||||
import { Output } from '../../util/output';
|
||||
import { NowContext, Cert } from '../../types';
|
||||
import getCommandFlags from '../../util/get-command-flags';
|
||||
|
||||
interface Options {
|
||||
'--debug'?: boolean;
|
||||
'--after'?: string;
|
||||
'--next'?: number;
|
||||
}
|
||||
|
||||
async function ls(
|
||||
@@ -32,8 +30,7 @@ async function ls(
|
||||
} = ctx;
|
||||
const { currentTeam } = config;
|
||||
const { apiUrl } = ctx;
|
||||
const debug = opts['--debug'];
|
||||
const after = opts['--after'];
|
||||
const { '--debug': debug, '--next': nextTimestamp } = opts;
|
||||
const client = new Client({ apiUrl, token, currentTeam, debug });
|
||||
let contextName = null;
|
||||
|
||||
@@ -47,7 +44,10 @@ async function ls(
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (typeof nextTimestamp !== 'undefined' && Number.isNaN(nextTimestamp)) {
|
||||
output.error('Please provide a number for flag --next');
|
||||
return 1;
|
||||
}
|
||||
const now = new Now({ apiUrl, token, debug, currentTeam });
|
||||
const lsStamp = stamp();
|
||||
|
||||
@@ -59,38 +59,29 @@ async function ls(
|
||||
}
|
||||
|
||||
// Get the list of certificates
|
||||
const certificates = await getCerts(output, now, { after }).catch(err => err);
|
||||
|
||||
if (certificates instanceof CertNotFound) {
|
||||
output.error(certificates.message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (certificates instanceof Error) {
|
||||
throw certificates;
|
||||
}
|
||||
|
||||
const certs = sortByCn(certificates);
|
||||
|
||||
output.log(
|
||||
`${plural('certificate', certs.length, true)} found under ${chalk.bold(
|
||||
contextName
|
||||
)} ${lsStamp()}`
|
||||
const { certs, pagination } = await getCerts(now, nextTimestamp).catch(
|
||||
err => err
|
||||
);
|
||||
|
||||
if (certs.length >= 100) {
|
||||
const { uid: lastCert } = certificates[certificates.length - 1];
|
||||
output.note(
|
||||
`There may be more certificates that can be retrieved with ${cmd(
|
||||
`now ${process.argv.slice(2).join(' ')} --after=${lastCert}`
|
||||
)}.\n`
|
||||
);
|
||||
}
|
||||
output.log(
|
||||
`${
|
||||
certs.length > 0 ? 'Certificates' : 'No certificates'
|
||||
} found under ${chalk.bold(contextName)} ${lsStamp()}`
|
||||
);
|
||||
|
||||
if (certs.length > 0) {
|
||||
console.log(formatCertsTable(certs));
|
||||
}
|
||||
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(opts, ['_', '--next']);
|
||||
output.log(
|
||||
`To display the next page run ${cmd(
|
||||
`now certs ls${flags} --next ${pagination.next}`
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -161,18 +152,4 @@ function formatExpirationDate(date: Date) {
|
||||
: chalk.gray(`in ${ms(diff)}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function sorts the list of certs by root domain changing *
|
||||
* to 'wildcard' since that will allow psl get the root domain
|
||||
* properly to make the comparison.
|
||||
*/
|
||||
function sortByCn(certsList: Cert[]) {
|
||||
return certsList.concat().sort((a: Cert, b: Cert) => {
|
||||
const domainA = psl.get(a.cns[0].replace('*', 'wildcard'));
|
||||
const domainB = psl.get(b.cns[0].replace('*', 'wildcard'));
|
||||
if (!domainA || !domainB) return 0;
|
||||
return domainA.localeCompare(domainB);
|
||||
});
|
||||
}
|
||||
|
||||
export default ls;
|
||||
|
||||
@@ -98,7 +98,7 @@ async function getCertsToDelete(
|
||||
contextName: string,
|
||||
id: string
|
||||
) {
|
||||
const cert = await getCertById(output, client, id);
|
||||
const cert = await getCertById(client, id);
|
||||
if (cert instanceof ERRORS.CertNotFound) {
|
||||
const certs = await getCertsForDomain(output, client, contextName, id);
|
||||
if (certs instanceof ERRORS.CertsPermissionDenied) {
|
||||
|
||||
@@ -37,7 +37,7 @@ export default async function dev(
|
||||
// retrieve dev command
|
||||
const [link, frameworks] = await Promise.all([
|
||||
getLinkedProject(output, client, cwd),
|
||||
getFrameworks(),
|
||||
getFrameworks(client),
|
||||
]);
|
||||
|
||||
if (link.status === 'error') {
|
||||
|
||||
@@ -155,11 +155,7 @@ export default async function main(ctx) {
|
||||
type === STATIC
|
||||
? null
|
||||
: caught(
|
||||
now.fetch(
|
||||
`/v1/now/deployments/${encodeURIComponent(
|
||||
finalId
|
||||
)}/events?types=event`
|
||||
)
|
||||
now.fetch(`/v1/now/deployments/${encodeURIComponent(finalId)}/events`)
|
||||
),
|
||||
isBuilds ? now.fetch(buildsUrl) : { builds: [] },
|
||||
]);
|
||||
|
||||
@@ -4,7 +4,7 @@ import Now from '../util';
|
||||
import createOutput from '../util/output';
|
||||
import logo from '../util/output/logo';
|
||||
import elapsed from '../util/output/elapsed.ts';
|
||||
import { maybeURL, normalizeURL, parseInstanceURL } from '../util/url';
|
||||
import { maybeURL, normalizeURL } from '../util/url';
|
||||
import printEvents from '../util/events';
|
||||
import Client from '../util/client.ts';
|
||||
import getScope from '../util/get-scope.ts';
|
||||
@@ -16,7 +16,6 @@ const help = () => {
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-a, --all Include access logs
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`now.json`'} file
|
||||
@@ -28,9 +27,6 @@ const help = () => {
|
||||
-n ${chalk.bold.underline(
|
||||
'NUMBER'
|
||||
)} Number of logs [100]
|
||||
-q ${chalk.bold.underline('QUERY')}, --query=${chalk.bold.underline(
|
||||
'QUERY'
|
||||
)} Search query
|
||||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
@@ -65,23 +61,18 @@ export default async function main(ctx) {
|
||||
let apiUrl;
|
||||
let head;
|
||||
let limit;
|
||||
let query;
|
||||
let follow;
|
||||
let types;
|
||||
let outputMode;
|
||||
|
||||
let since;
|
||||
let until;
|
||||
let instanceId;
|
||||
|
||||
argv = mri(ctx.argv.slice(2), {
|
||||
string: ['query', 'since', 'until', 'output'],
|
||||
boolean: ['help', 'all', 'debug', 'head', 'follow'],
|
||||
string: ['since', 'until', 'output'],
|
||||
boolean: ['help', 'debug', 'head', 'follow'],
|
||||
alias: {
|
||||
help: 'h',
|
||||
all: 'a',
|
||||
debug: 'd',
|
||||
query: 'q',
|
||||
follow: 'f',
|
||||
output: 'o',
|
||||
},
|
||||
@@ -121,7 +112,7 @@ export default async function main(ctx) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
[deploymentIdOrURL, instanceId] = parseInstanceURL(normalizedURL);
|
||||
deploymentIdOrURL = normalizedURL;
|
||||
}
|
||||
|
||||
debug = argv.debug;
|
||||
@@ -129,10 +120,8 @@ export default async function main(ctx) {
|
||||
|
||||
head = argv.head;
|
||||
limit = typeof argv.n === 'number' ? argv.n : 100;
|
||||
query = argv.query || '';
|
||||
follow = argv.f;
|
||||
if (follow) until = 0;
|
||||
types = argv.all ? [] : ['command', 'stdout', 'stderr', 'exit'];
|
||||
outputMode = argv.output in logPrinters ? argv.output : 'short';
|
||||
|
||||
const {
|
||||
@@ -204,9 +193,6 @@ export default async function main(ctx) {
|
||||
const findOpts1 = {
|
||||
direction,
|
||||
limit,
|
||||
query,
|
||||
types,
|
||||
instanceId,
|
||||
since,
|
||||
until,
|
||||
}; // no follow
|
||||
@@ -236,9 +222,6 @@ export default async function main(ctx) {
|
||||
const since2 = lastEvent ? lastEvent.date : Date.now();
|
||||
const findOpts2 = {
|
||||
direction: 'forward',
|
||||
query,
|
||||
types,
|
||||
instanceId,
|
||||
since: since2,
|
||||
follow: true,
|
||||
};
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import { Cert } from '../../types';
|
||||
import { Output } from '../output/create-output';
|
||||
import Client from '../client';
|
||||
import * as ERRORS from '../errors-ts';
|
||||
|
||||
export default async function getCertById(
|
||||
output: Output,
|
||||
client: Client,
|
||||
id: string
|
||||
) {
|
||||
export default async function getCertById(client: Client, id: string) {
|
||||
try {
|
||||
return await client.fetch<Cert>(`/v3/now/certs/${id}`);
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,47 +1,17 @@
|
||||
import { URLSearchParams } from 'url';
|
||||
import Client from '../client';
|
||||
import { Output } from '../output';
|
||||
import { Cert } from '../../types';
|
||||
import getCertById from './get-cert-by-id';
|
||||
import { CertNotFound } from '../errors-ts';
|
||||
import { Cert, PaginationOptions } from '../../types';
|
||||
|
||||
type Response = {
|
||||
certs: Cert[];
|
||||
pagination: PaginationOptions;
|
||||
};
|
||||
|
||||
function sortByCreated(a: Cert, b: Cert) {
|
||||
const dateA = new Date(a.created);
|
||||
const dateB = new Date(b.created);
|
||||
export default async function getCerts(client: Client, next?: number) {
|
||||
let certsUrl = `/v4/now/certs?limit=20`;
|
||||
|
||||
if (dateA > dateB) {
|
||||
return -1;
|
||||
if (next) {
|
||||
certsUrl += `&until=${next}`;
|
||||
}
|
||||
|
||||
if (dateA < dateB) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
export default async function getCerts(
|
||||
output: Output,
|
||||
client: Client,
|
||||
options?: { after?: string }
|
||||
) {
|
||||
const query = new URLSearchParams({ limit: '100' });
|
||||
|
||||
if (options && options.after) {
|
||||
const lastCert = await getCertById(output, client, options.after);
|
||||
|
||||
if (lastCert instanceof CertNotFound) {
|
||||
throw lastCert;
|
||||
}
|
||||
|
||||
query.set('until', new Date(lastCert.created).getTime().toString());
|
||||
}
|
||||
|
||||
const { certs } = await client.fetch<Response>(`/v3/now/certs?${query}`);
|
||||
|
||||
return certs.sort(sortByCreated);
|
||||
return await client.fetch<Response>(certsUrl);
|
||||
}
|
||||
|
||||
@@ -11,11 +11,8 @@ async function getEventsStream(now, idOrHost, options) {
|
||||
direction: options.direction,
|
||||
follow: options.follow ? '1' : '',
|
||||
format: options.format || 'lines',
|
||||
instanceId: options.instanceId,
|
||||
limit: options.limit,
|
||||
q: options.query,
|
||||
since: options.since,
|
||||
types: (options.types || []).join(','),
|
||||
until: options.until,
|
||||
})}`
|
||||
);
|
||||
|
||||
@@ -24,19 +24,19 @@ function onMessage(message) {
|
||||
}
|
||||
|
||||
async function processMessage(message) {
|
||||
const { builderName, buildParams } = message;
|
||||
const { builderName, buildOptions } = message;
|
||||
const builder = require(builderName);
|
||||
|
||||
// Convert the `files` to back into `FileFsRef` instances
|
||||
for (const name of Object.keys(buildParams.files)) {
|
||||
for (const name of Object.keys(buildOptions.files)) {
|
||||
const ref = Object.assign(
|
||||
Object.create(FileFsRef.prototype),
|
||||
buildParams.files[name]
|
||||
buildOptions.files[name]
|
||||
);
|
||||
buildParams.files[name] = ref;
|
||||
buildOptions.files[name] = ref;
|
||||
}
|
||||
|
||||
const result = await builder.build(buildParams);
|
||||
const result = await builder.build(buildOptions);
|
||||
|
||||
// `@now/next` sets this, but it causes "Converting circular
|
||||
// structure to JSON" errors, so delete the property...
|
||||
|
||||
@@ -2,24 +2,30 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import ms from 'ms';
|
||||
import bytes from 'bytes';
|
||||
import { promisify } from 'util';
|
||||
import { delimiter, dirname, join } from 'path';
|
||||
import { fork, ChildProcess } from 'child_process';
|
||||
import { createFunction } from '@zeit/fun';
|
||||
import { Builder, File, Lambda, FileBlob, FileFsRef } from '@now/build-utils';
|
||||
import {
|
||||
Builder,
|
||||
BuildOptions,
|
||||
Env,
|
||||
File,
|
||||
Lambda,
|
||||
FileBlob,
|
||||
FileFsRef,
|
||||
} from '@now/build-utils';
|
||||
import plural from 'pluralize';
|
||||
import minimatch from 'minimatch';
|
||||
import _treeKill from 'tree-kill';
|
||||
|
||||
import { Output } from '../output';
|
||||
import highlight from '../output/highlight';
|
||||
import { treeKill } from '../tree-kill';
|
||||
import { relative } from '../path-helpers';
|
||||
import { LambdaSizeExceededError } from '../errors-ts';
|
||||
|
||||
import DevServer from './server';
|
||||
import { builderModulePathPromise, getBuilder } from './builder-cache';
|
||||
import {
|
||||
EnvConfig,
|
||||
NowConfig,
|
||||
BuildMatch,
|
||||
BuildResult,
|
||||
@@ -27,7 +33,6 @@ import {
|
||||
BuilderOutput,
|
||||
BuildResultV3,
|
||||
BuilderOutputs,
|
||||
BuilderParams,
|
||||
EnvConfigs,
|
||||
} from './types';
|
||||
import { normalizeRoutes } from '@now/routing-utils';
|
||||
@@ -43,8 +48,6 @@ interface BuildMessageResult extends BuildMessage {
|
||||
error?: object;
|
||||
}
|
||||
|
||||
const treeKill = promisify(_treeKill);
|
||||
|
||||
async function createBuildProcess(
|
||||
match: BuildMatch,
|
||||
envConfigs: EnvConfigs,
|
||||
@@ -63,7 +66,7 @@ async function createBuildProcess(
|
||||
PATH = `${yarnPath}${delimiter}${PATH}`;
|
||||
}
|
||||
|
||||
const env: EnvConfig = {
|
||||
const env: Env = {
|
||||
...process.env,
|
||||
PATH,
|
||||
...envConfigs.allEnv,
|
||||
@@ -140,7 +143,7 @@ export async function executeBuild(
|
||||
);
|
||||
}
|
||||
|
||||
const buildParams: BuilderParams = {
|
||||
const buildOptions: BuildOptions = {
|
||||
files,
|
||||
entrypoint,
|
||||
workPath,
|
||||
@@ -162,7 +165,7 @@ export async function executeBuild(
|
||||
buildProcess.send({
|
||||
type: 'build',
|
||||
builderName: pkg.name,
|
||||
buildParams,
|
||||
buildOptions,
|
||||
});
|
||||
|
||||
buildResultOrOutputs = await new Promise((resolve, reject) => {
|
||||
@@ -193,7 +196,7 @@ export async function executeBuild(
|
||||
buildProcess!.on('message', onMessage);
|
||||
});
|
||||
} else {
|
||||
buildResultOrOutputs = await builder.build(buildParams);
|
||||
buildResultOrOutputs = await builder.build(buildOptions);
|
||||
}
|
||||
|
||||
// Sort out build result to builder v2 shape
|
||||
|
||||
@@ -4,7 +4,7 @@ import PCRE from 'pcre-to-regexp';
|
||||
import isURL from './is-url';
|
||||
import DevServer from './server';
|
||||
|
||||
import { HttpHeadersConfig, RouteConfig, RouteResult } from './types';
|
||||
import { HttpHeadersConfig, RouteResult } from './types';
|
||||
import { isHandler, Route, HandleValue } from '@now/routing-utils';
|
||||
|
||||
export function resolveRouteParameters(
|
||||
@@ -48,10 +48,10 @@ export function getRoutesTypes(routes: Route[] = []) {
|
||||
export async function devRouter(
|
||||
reqUrl: string = '/',
|
||||
reqMethod?: string,
|
||||
routes?: RouteConfig[],
|
||||
routes?: Route[],
|
||||
devServer?: DevServer,
|
||||
previousHeaders?: HttpHeadersConfig,
|
||||
missRoutes?: RouteConfig[],
|
||||
missRoutes?: Route[],
|
||||
phase?: HandleValue | null
|
||||
): Promise<RouteResult> {
|
||||
let result: RouteResult | undefined;
|
||||
|
||||
@@ -16,7 +16,9 @@ import {
|
||||
getTransformedRoutes,
|
||||
appendRoutesToPhase,
|
||||
HandleValue,
|
||||
Route,
|
||||
} from '@now/routing-utils';
|
||||
import once from '@tootallnate/once';
|
||||
import directoryTemplate from 'serve-handler/src/directory';
|
||||
import getPort from 'get-port';
|
||||
import { ChildProcess } from 'child_process';
|
||||
@@ -25,6 +27,8 @@ import which from 'which';
|
||||
|
||||
import {
|
||||
Builder,
|
||||
Env,
|
||||
StartDevServerResult,
|
||||
FileFsRef,
|
||||
PackageJson,
|
||||
detectBuilders,
|
||||
@@ -33,9 +37,9 @@ import {
|
||||
spawnCommand,
|
||||
} from '@now/build-utils';
|
||||
|
||||
import { once } from '../once';
|
||||
import link from '../output/link';
|
||||
import { Output } from '../output';
|
||||
import { treeKill } from '../tree-kill';
|
||||
import { relative } from '../path-helpers';
|
||||
import { getDistTag } from '../get-dist-tag';
|
||||
import getNowConfigPath from '../config/local-path';
|
||||
@@ -72,7 +76,6 @@ import errorTemplate502 from './templates/error_502';
|
||||
import redirectTemplate from './templates/redirect';
|
||||
|
||||
import {
|
||||
EnvConfig,
|
||||
NowConfig,
|
||||
DevServerOptions,
|
||||
BuildMatch,
|
||||
@@ -83,7 +86,6 @@ import {
|
||||
InvokePayload,
|
||||
InvokeResult,
|
||||
ListenSpec,
|
||||
RouteConfig,
|
||||
RouteResult,
|
||||
HttpHeadersConfig,
|
||||
EnvConfigs,
|
||||
@@ -133,6 +135,7 @@ export default class DevServer {
|
||||
private devCommand?: string;
|
||||
private devProcess?: ChildProcess;
|
||||
private devProcessPort?: number;
|
||||
private devServerPids: Set<number>;
|
||||
|
||||
private getNowConfigPromise: Promise<NowConfig> | null;
|
||||
private blockingBuildsPromise: Promise<void> | null;
|
||||
@@ -153,7 +156,7 @@ export default class DevServer {
|
||||
|
||||
this.cachedNowConfig = null;
|
||||
this.apiDir = null;
|
||||
this.apiExtensions = new Set<string>();
|
||||
this.apiExtensions = new Set();
|
||||
this.server = http.createServer(this.devServerHandler);
|
||||
this.server.timeout = 0; // Disable timeout
|
||||
this.serverUrlPrinted = false;
|
||||
@@ -173,6 +176,8 @@ export default class DevServer {
|
||||
this.podId = Math.random()
|
||||
.toString(32)
|
||||
.slice(-5);
|
||||
|
||||
this.devServerPids = new Set();
|
||||
}
|
||||
|
||||
async exit(code = 1) {
|
||||
@@ -451,11 +456,11 @@ export default class DevServer {
|
||||
await this.updateBuildMatches(nowConfig);
|
||||
}
|
||||
|
||||
async getLocalEnv(fileName: string, base?: EnvConfig): Promise<EnvConfig> {
|
||||
async getLocalEnv(fileName: string, base?: Env): Promise<Env> {
|
||||
// TODO: use the file watcher to only invalidate the env `dotfile`
|
||||
// once a change to the `fileName` occurs
|
||||
const filePath = join(this.cwd, fileName);
|
||||
let env: EnvConfig = {};
|
||||
let env: Env = {};
|
||||
try {
|
||||
const dotenv = await fs.readFile(filePath, 'utf8');
|
||||
this.output.debug(`Using local env: ${filePath}`);
|
||||
@@ -580,7 +585,7 @@ export default class DevServer {
|
||||
config.builds = config.builds || [];
|
||||
config.builds.push(...builders);
|
||||
|
||||
const routes: RouteConfig[] = [];
|
||||
const routes: Route[] = [];
|
||||
const { routes: nowConfigRoutes } = config;
|
||||
routes.push(...(redirectRoutes || []));
|
||||
routes.push(
|
||||
@@ -664,11 +669,7 @@ export default class DevServer {
|
||||
await this.tryValidateOrExit(config, validateNowConfigFunctions);
|
||||
}
|
||||
|
||||
validateEnvConfig(
|
||||
type: string,
|
||||
env: EnvConfig = {},
|
||||
localEnv: EnvConfig = {}
|
||||
): EnvConfig {
|
||||
validateEnvConfig(type: string, env: Env = {}, localEnv: Env = {}): Env {
|
||||
// Validate if there are any missing env vars defined in `now.json`,
|
||||
// but not in the `.env` / `.build.env` file
|
||||
const missing: string[] = Object.entries(env)
|
||||
@@ -684,7 +685,7 @@ export default class DevServer {
|
||||
throw new MissingDotenvVarsError(type, missing);
|
||||
}
|
||||
|
||||
const merged: EnvConfig = { ...env, ...localEnv };
|
||||
const merged: Env = { ...env, ...localEnv };
|
||||
|
||||
// Validate that the env var name matches what AWS Lambda allows:
|
||||
// - https://docs.aws.amazon.com/lambda/latest/dg/env_variables.html
|
||||
@@ -872,6 +873,7 @@ export default class DevServer {
|
||||
*/
|
||||
async stop(exitCode?: number): Promise<void> {
|
||||
const { devProcess } = this;
|
||||
const { debug, log } = this.output;
|
||||
if (this.stopping) return;
|
||||
|
||||
this.stopping = true;
|
||||
@@ -879,7 +881,7 @@ export default class DevServer {
|
||||
if (this.serverUrlPrinted) {
|
||||
// This makes it look cleaner
|
||||
process.stdout.write('\n');
|
||||
this.output.log(`Stopping ${chalk.bold('`now dev`')} server`);
|
||||
log(`Stopping ${chalk.bold('`now dev`')} server`);
|
||||
}
|
||||
|
||||
const ops: Promise<void>[] = [];
|
||||
@@ -908,15 +910,29 @@ export default class DevServer {
|
||||
ops.push(close(this.server));
|
||||
|
||||
if (this.watcher) {
|
||||
this.output.debug(`Closing file watcher`);
|
||||
debug(`Closing file watcher`);
|
||||
this.watcher.close();
|
||||
}
|
||||
|
||||
if (this.updateBuildersPromise) {
|
||||
this.output.debug(`Waiting for builders update to complete`);
|
||||
debug(`Waiting for builders update to complete`);
|
||||
ops.push(this.updateBuildersPromise);
|
||||
}
|
||||
|
||||
for (const pid of this.devServerPids) {
|
||||
debug(`Killing builder dev server with PID ${pid}`);
|
||||
ops.push(
|
||||
treeKill(pid).then(
|
||||
() => {
|
||||
debug(`Killed builder dev server with PID ${pid}`);
|
||||
},
|
||||
(err: Error) => {
|
||||
debug(`Failed to kill builder dev server with PID ${pid}: ${err}`);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await Promise.all(ops);
|
||||
} catch (err) {
|
||||
@@ -1222,9 +1238,11 @@ export default class DevServer {
|
||||
res: http.ServerResponse,
|
||||
nowRequestId: string,
|
||||
nowConfig: NowConfig,
|
||||
routes: RouteConfig[] | undefined = nowConfig.routes,
|
||||
routes: Route[] | undefined = nowConfig.routes,
|
||||
callLevel: number = 0
|
||||
) => {
|
||||
const { debug } = this.output;
|
||||
|
||||
// If there is a double-slash present in the URL,
|
||||
// then perform a redirect to make it "clean".
|
||||
const parsed = url.parse(req.url || '/');
|
||||
@@ -1241,16 +1259,14 @@ export default class DevServer {
|
||||
return;
|
||||
}
|
||||
|
||||
this.output.debug(`Rewriting URL from "${req.url}" to "${location}"`);
|
||||
debug(`Rewriting URL from "${req.url}" to "${location}"`);
|
||||
req.url = location;
|
||||
}
|
||||
|
||||
await this.updateBuildMatches(nowConfig);
|
||||
|
||||
if (this.blockingBuildsPromise) {
|
||||
this.output.debug(
|
||||
'Waiting for builds to complete before handling request'
|
||||
);
|
||||
debug('Waiting for builds to complete before handling request');
|
||||
await this.blockingBuildsPromise;
|
||||
}
|
||||
|
||||
@@ -1291,7 +1307,7 @@ export default class DevServer {
|
||||
Object.assign(destParsed.query, routeResult.uri_args);
|
||||
const destUrl = url.format(destParsed);
|
||||
|
||||
this.output.debug(`ProxyPass: ${destUrl}`);
|
||||
debug(`ProxyPass: ${destUrl}`);
|
||||
this.setResponseHeaders(res, nowRequestId);
|
||||
return proxyPass(req, res, destUrl, this.output);
|
||||
}
|
||||
@@ -1390,7 +1406,7 @@ export default class DevServer {
|
||||
if (!match) {
|
||||
// if the dev command is started, proxy to it
|
||||
if (this.devProcessPort) {
|
||||
this.output.debug('Proxy to dev command server');
|
||||
debug('Proxying to frontend dev server');
|
||||
return proxyPass(
|
||||
req,
|
||||
res,
|
||||
@@ -1422,7 +1438,7 @@ export default class DevServer {
|
||||
origUrl.pathname = dest;
|
||||
Object.assign(origUrl.query, uri_args);
|
||||
const newUrl = url.format(origUrl);
|
||||
this.output.debug(
|
||||
debug(
|
||||
`Checking build result's ${buildResult.routes.length} \`routes\` to match ${newUrl}`
|
||||
);
|
||||
const matchedRoute = await devRouter(
|
||||
@@ -1432,9 +1448,7 @@ export default class DevServer {
|
||||
this
|
||||
);
|
||||
if (matchedRoute.found && callLevel === 0) {
|
||||
this.output.debug(
|
||||
`Found matching route ${matchedRoute.dest} for ${newUrl}`
|
||||
);
|
||||
debug(`Found matching route ${matchedRoute.dest} for ${newUrl}`);
|
||||
req.url = newUrl;
|
||||
await this.serveProjectAsNowV2(
|
||||
req,
|
||||
@@ -1448,7 +1462,82 @@ export default class DevServer {
|
||||
}
|
||||
}
|
||||
|
||||
// Before doing any asset matching, check if this builder supports the
|
||||
// `startDevServer()` "optimization". In this case, the now dev server invokes
|
||||
// `startDevServer()` on the builder for every HTTP request so that it boots
|
||||
// up a single-serve dev HTTP server that now dev will proxy this HTTP request
|
||||
// to. Once the proxied request is finished, now dev shuts down the dev
|
||||
// server child process.
|
||||
const { builder, package: builderPkg } = match.builderWithPkg;
|
||||
if (typeof builder.startDevServer === 'function') {
|
||||
let devServerResult: StartDevServerResult = null;
|
||||
try {
|
||||
devServerResult = await builder.startDevServer({
|
||||
entrypoint: match.entrypoint,
|
||||
workPath: this.cwd,
|
||||
});
|
||||
} catch (err) {
|
||||
// `starDevServer()` threw an error. Most likely this means the dev
|
||||
// server process exited before sending the port information message
|
||||
// (missing dependency at runtime, for example).
|
||||
console.error(`Error starting "${builderPkg.name}" dev server:`, err);
|
||||
await this.sendError(
|
||||
req,
|
||||
res,
|
||||
nowRequestId,
|
||||
'NO_STATUS_CODE_FROM_DEV_SERVER',
|
||||
502
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (devServerResult) {
|
||||
const { port, pid } = devServerResult;
|
||||
this.devServerPids.add(pid);
|
||||
|
||||
res.once('close', () => {
|
||||
debug(`Killing builder dev server with PID ${pid}`);
|
||||
treeKill(pid).then(
|
||||
() => {
|
||||
this.devServerPids.delete(pid);
|
||||
debug(`Killed builder dev server with PID ${pid}`);
|
||||
},
|
||||
(err: Error) => {
|
||||
this.devServerPids.delete(pid);
|
||||
debug(
|
||||
`Failed to kill builder dev server with PID ${pid}: ${err}`
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
debug(
|
||||
`Proxying to "${builderPkg.name}" dev server (port=${port}, pid=${pid})`
|
||||
);
|
||||
|
||||
// Mix in the routing based query parameters
|
||||
const parsed = url.parse(req.url || '/', true);
|
||||
Object.assign(parsed.query, uri_args);
|
||||
req.url = url.format({
|
||||
pathname: parsed.pathname,
|
||||
query: parsed.query,
|
||||
});
|
||||
|
||||
this.setResponseHeaders(res, nowRequestId);
|
||||
return proxyPass(
|
||||
req,
|
||||
res,
|
||||
`http://localhost:${port}`,
|
||||
this.output,
|
||||
false
|
||||
);
|
||||
} else {
|
||||
debug(`Skipping \`startDevServer()\` for ${match.entrypoint}`);
|
||||
}
|
||||
}
|
||||
|
||||
let foundAsset = findAsset(match, requestPath, nowConfig);
|
||||
|
||||
if (!foundAsset && callLevel === 0) {
|
||||
await this.triggerBuild(match, buildRequestPath, req);
|
||||
|
||||
@@ -1456,25 +1545,30 @@ export default class DevServer {
|
||||
foundAsset = findAsset(match, requestPath, nowConfig);
|
||||
}
|
||||
|
||||
if (!foundAsset) {
|
||||
// if the dev command is started, proxy to it
|
||||
if (this.devProcessPort) {
|
||||
this.output.debug('Proxy to dev command server');
|
||||
return proxyPass(
|
||||
req,
|
||||
res,
|
||||
`http://localhost:${this.devProcessPort}`,
|
||||
this.output,
|
||||
false
|
||||
);
|
||||
}
|
||||
// Proxy to the dev server:
|
||||
// - when there is no asset
|
||||
// - when the asset is not a Lambda (the dev server must take care of all static files)
|
||||
if (
|
||||
this.devProcessPort &&
|
||||
(!foundAsset || (foundAsset && foundAsset.asset.type !== 'Lambda'))
|
||||
) {
|
||||
debug('Proxying to frontend dev server');
|
||||
return proxyPass(
|
||||
req,
|
||||
res,
|
||||
`http://localhost:${this.devProcessPort}`,
|
||||
this.output,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
if (!foundAsset) {
|
||||
await this.send404(req, res, nowRequestId);
|
||||
return;
|
||||
}
|
||||
|
||||
const { asset, assetKey } = foundAsset;
|
||||
this.output.debug(
|
||||
debug(
|
||||
`Serving asset: [${asset.type}] ${assetKey} ${(asset as any)
|
||||
.contentType || ''}`
|
||||
);
|
||||
@@ -1539,7 +1633,7 @@ export default class DevServer {
|
||||
body: body.toString('base64'),
|
||||
};
|
||||
|
||||
this.output.debug(`Invoking lambda: "${assetKey}" with ${path}`);
|
||||
debug(`Invoking lambda: "${assetKey}" with ${path}`);
|
||||
|
||||
let result: InvokeResult;
|
||||
try {
|
||||
@@ -1682,7 +1776,7 @@ export default class DevServer {
|
||||
|
||||
const port = await getPort();
|
||||
|
||||
const env: EnvConfig = {
|
||||
const env: Env = {
|
||||
// Because of child process 'pipe' below, isTTY will be false.
|
||||
// Most frameworks use `chalk`/`supports-color` so we enable it anyway.
|
||||
FORCE_COLOR: process.stdout.isTTY ? '1' : '0',
|
||||
@@ -1891,7 +1985,7 @@ async function shouldServe(
|
||||
const shouldServe = await builder.shouldServe({
|
||||
entrypoint: src,
|
||||
files,
|
||||
config,
|
||||
config: config || {},
|
||||
requestPath,
|
||||
workPath: devServer.cwd,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { basename, extname, join } from 'path';
|
||||
import { BuilderParams, BuildResult, ShouldServeParams } from './types';
|
||||
import { FileFsRef, BuildOptions, ShouldServeOptions } from '@now/build-utils';
|
||||
import { BuildResult } from './types';
|
||||
|
||||
export const version = 2;
|
||||
|
||||
@@ -7,7 +8,7 @@ export function build({
|
||||
files,
|
||||
entrypoint,
|
||||
config,
|
||||
}: BuilderParams): BuildResult {
|
||||
}: BuildOptions): BuildResult {
|
||||
let path = entrypoint;
|
||||
const outputDir = config.zeroConfig ? config.outputDirectory : '';
|
||||
const outputMatch = outputDir + '/';
|
||||
@@ -16,7 +17,7 @@ export function build({
|
||||
path = path.slice(outputMatch.length);
|
||||
}
|
||||
const output = {
|
||||
[path]: files[entrypoint],
|
||||
[path]: files[entrypoint] as FileFsRef,
|
||||
};
|
||||
const watch = [path];
|
||||
|
||||
@@ -28,7 +29,7 @@ export function shouldServe({
|
||||
files,
|
||||
requestPath,
|
||||
config = {},
|
||||
}: ShouldServeParams) {
|
||||
}: ShouldServeOptions) {
|
||||
let outputPrefix = '';
|
||||
const outputDir = config.zeroConfig ? config.outputDirectory : '';
|
||||
const outputMatch = outputDir + '/';
|
||||
|
||||
@@ -3,11 +3,16 @@ import { ChildProcess } from 'child_process';
|
||||
import { Lambda as FunLambda } from '@zeit/fun';
|
||||
import {
|
||||
Builder as BuildConfig,
|
||||
BuildOptions,
|
||||
PrepareCacheOptions,
|
||||
ShouldServeOptions,
|
||||
StartDevServerOptions,
|
||||
StartDevServerResult,
|
||||
Env,
|
||||
FileBlob,
|
||||
FileFsRef,
|
||||
Lambda,
|
||||
PackageJson,
|
||||
Config,
|
||||
} from '@now/build-utils';
|
||||
import { NowConfig } from 'now-client';
|
||||
import { HandleValue, Route } from '@now/routing-utils';
|
||||
@@ -22,25 +27,21 @@ export interface DevServerOptions {
|
||||
frameworkSlug: string | null;
|
||||
}
|
||||
|
||||
export interface EnvConfig {
|
||||
[name: string]: string | undefined;
|
||||
}
|
||||
|
||||
export interface EnvConfigs {
|
||||
/**
|
||||
* environment variables from `.env.build` file (deprecated)
|
||||
*/
|
||||
buildEnv: EnvConfig;
|
||||
buildEnv: Env;
|
||||
|
||||
/**
|
||||
* environment variables from `.env` file
|
||||
*/
|
||||
runEnv: EnvConfig;
|
||||
runEnv: Env;
|
||||
|
||||
/**
|
||||
* environment variables from `.env` and `.env.build`
|
||||
*/
|
||||
allEnv: EnvConfig;
|
||||
allEnv: Env;
|
||||
}
|
||||
|
||||
export interface BuildMatch extends BuildConfig {
|
||||
@@ -52,8 +53,6 @@ export interface BuildMatch extends BuildConfig {
|
||||
buildProcess?: ChildProcess;
|
||||
}
|
||||
|
||||
export type RouteConfig = Route;
|
||||
|
||||
export interface HttpHandler {
|
||||
(req: http.IncomingMessage, res: http.ServerResponse): void;
|
||||
}
|
||||
@@ -62,9 +61,9 @@ export interface BuilderInputs {
|
||||
[path: string]: FileFsRef;
|
||||
}
|
||||
|
||||
export type BuiltLambda = Lambda & {
|
||||
export interface BuiltLambda extends Lambda {
|
||||
fn?: FunLambda;
|
||||
};
|
||||
}
|
||||
|
||||
export type BuilderOutput = BuiltLambda | FileFsRef | FileBlob;
|
||||
|
||||
@@ -78,28 +77,6 @@ export interface CacheOutputs {
|
||||
[path: string]: CacheOutput;
|
||||
}
|
||||
|
||||
export interface BuilderParamsBase {
|
||||
files: BuilderInputs;
|
||||
entrypoint: string;
|
||||
config: Config;
|
||||
meta?: {
|
||||
isDev?: boolean;
|
||||
requestPath?: string | null;
|
||||
filesChanged?: string[];
|
||||
filesRemoved?: string[];
|
||||
env?: EnvConfig;
|
||||
buildEnv?: EnvConfig;
|
||||
};
|
||||
}
|
||||
|
||||
export interface BuilderParams extends BuilderParamsBase {
|
||||
workPath: string;
|
||||
}
|
||||
|
||||
export interface PrepareCacheParams extends BuilderParams {
|
||||
cachePath: string;
|
||||
}
|
||||
|
||||
export interface BuilderConfigAttr {
|
||||
maxLambdaSize?: string | number;
|
||||
}
|
||||
@@ -108,47 +85,40 @@ export interface Builder {
|
||||
version?: 1 | 2 | 3 | 4;
|
||||
config?: BuilderConfigAttr;
|
||||
build(
|
||||
params: BuilderParams
|
||||
opts: BuildOptions
|
||||
):
|
||||
| BuilderOutputs
|
||||
| BuildResult
|
||||
| Promise<BuilderOutputs>
|
||||
| Promise<BuildResult>;
|
||||
shouldServe?(params: ShouldServeParams): boolean | Promise<boolean>;
|
||||
prepareCache?(
|
||||
params: PrepareCacheParams
|
||||
opts: PrepareCacheOptions
|
||||
): CacheOutputs | Promise<CacheOutputs>;
|
||||
shouldServe?(params: ShouldServeOptions): boolean | Promise<boolean>;
|
||||
startDevServer?(opts: StartDevServerOptions): Promise<StartDevServerResult>;
|
||||
}
|
||||
|
||||
export interface BuildResult {
|
||||
output: BuilderOutputs;
|
||||
routes: RouteConfig[];
|
||||
routes: Route[];
|
||||
watch: string[];
|
||||
distPath?: string;
|
||||
}
|
||||
|
||||
export interface BuildResultV3 {
|
||||
output: Lambda;
|
||||
routes: RouteConfig[];
|
||||
routes: Route[];
|
||||
watch: string[];
|
||||
distPath?: string;
|
||||
}
|
||||
|
||||
export interface BuildResultV4 {
|
||||
output: { [filePath: string]: Lambda };
|
||||
routes: RouteConfig[];
|
||||
routes: Route[];
|
||||
watch: string[];
|
||||
distPath?: string;
|
||||
}
|
||||
|
||||
export interface ShouldServeParams {
|
||||
files: BuilderInputs;
|
||||
entrypoint: string;
|
||||
config?: Config;
|
||||
requestPath: string;
|
||||
workPath: string;
|
||||
}
|
||||
|
||||
export interface BuilderWithPackage {
|
||||
runInProcess?: boolean;
|
||||
builder: Readonly<Builder>;
|
||||
@@ -173,7 +143,7 @@ export interface RouteResult {
|
||||
// "uri_args": <object (key=value) list of new uri args to be passed along to dest >
|
||||
uri_args?: { [key: string]: any };
|
||||
// "matched_route": <object of the route spec that matched>
|
||||
matched_route?: RouteConfig;
|
||||
matched_route?: Route;
|
||||
// "matched_route_idx": <integer of the index of the route matched>
|
||||
matched_route_idx?: number;
|
||||
// "userDest": <boolean in case the destination was user defined>
|
||||
|
||||
@@ -19,7 +19,7 @@ export default async function addDNSRecord(
|
||||
) {
|
||||
try {
|
||||
const record = await client.fetch<Response>(
|
||||
`/v3/domains/${domain}/records`,
|
||||
`/v3/domains/${encodeURIComponent(domain)}/records`,
|
||||
{
|
||||
body: recordData,
|
||||
method: 'POST',
|
||||
|
||||
@@ -22,12 +22,15 @@ export default async function importZonefile(
|
||||
const zonefile = readFileSync(resolve(zonefilePath), 'utf8');
|
||||
|
||||
try {
|
||||
const res = await client.fetch<Response>(`/v3/domains/${domain}/records`, {
|
||||
headers: { 'Content-Type': 'text/dns' },
|
||||
body: zonefile,
|
||||
method: 'PUT',
|
||||
json: false,
|
||||
});
|
||||
const res = await client.fetch<Response>(
|
||||
`/v3/domains/${encodeURIComponent(domain)}/records`,
|
||||
{
|
||||
headers: { 'Content-Type': 'text/dns' },
|
||||
body: zonefile,
|
||||
method: 'PUT',
|
||||
json: false,
|
||||
}
|
||||
);
|
||||
|
||||
const { recordIds } = (await res.json()) as JSONResponse;
|
||||
cancelWait();
|
||||
|
||||
@@ -22,5 +22,7 @@ type Response = {
|
||||
};
|
||||
|
||||
export default async function checkTransfer(client: Client, name: string) {
|
||||
return client.fetch<Response>(`/v4/domains/${name}/registry`);
|
||||
return client.fetch<Response>(
|
||||
`/v4/domains/${encodeURIComponent(name)}/registry`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ async function getDomainByName(
|
||||
);
|
||||
try {
|
||||
const { domain } = await client.fetch<Response>(
|
||||
`/v4/domains/${domainName}`
|
||||
`/v4/domains/${encodeURIComponent(domainName)}`
|
||||
);
|
||||
cancelWait();
|
||||
return domain;
|
||||
|
||||
@@ -13,10 +13,13 @@ export default async function moveOutDomain(
|
||||
destination: string
|
||||
) {
|
||||
try {
|
||||
return await client.fetch<Response>(`/v4/domains/${name}`, {
|
||||
body: { op: 'move-out', destination },
|
||||
method: 'PATCH',
|
||||
});
|
||||
return await client.fetch<Response>(
|
||||
`/v4/domains/${encodeURIComponent(name)}`,
|
||||
{
|
||||
body: { op: 'move-out', destination },
|
||||
method: 'PATCH',
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
if (error.code === 'forbidden') {
|
||||
return new ERRORS.DomainPermissionDenied(name, contextName);
|
||||
|
||||
@@ -7,7 +7,9 @@ export default async function removeDomainByName(
|
||||
domain: string
|
||||
) {
|
||||
try {
|
||||
return await now.fetch(`/v3/domains/${domain}`, { method: 'DELETE' });
|
||||
return await now.fetch(`/v3/domains/${encodeURIComponent(domain)}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.code === 'not_found') {
|
||||
return new ERRORS.DomainNotFound(domain);
|
||||
|
||||
@@ -28,11 +28,8 @@ async function printEvents(
|
||||
const q = qs.stringify({
|
||||
direction: findOpts.direction,
|
||||
limit: findOpts.limit,
|
||||
q: findOpts.query,
|
||||
types: (findOpts.types || []).join(','),
|
||||
since: findOpts.since,
|
||||
until: findOpts.until,
|
||||
instanceId: findOpts.instanceId,
|
||||
follow: findOpts.follow ? '1' : '',
|
||||
format: 'lines',
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ import strlen from './strlen';
|
||||
export default function formatTable(
|
||||
header: string[],
|
||||
align: Array<'l' | 'r' | 'c' | '.'>,
|
||||
blocks: { name: string, rows: string[][] }[],
|
||||
blocks: { name: string; rows: string[][] }[],
|
||||
hsep = ' '
|
||||
) {
|
||||
const nrCols = header.length;
|
||||
@@ -50,8 +50,8 @@ export default function formatTable(
|
||||
for (let j = 0; j < nrCols; j++) {
|
||||
const col = `${row[j]}`;
|
||||
const al = align[j] || 'l';
|
||||
const pad =
|
||||
padding[j] > 1 ? ' '.repeat(padding[j] * 8 - strlen(col)) : '';
|
||||
const spaces = Math.max(padding[j] * 8 - strlen(col), 0);
|
||||
const pad = ' '.repeat(spaces);
|
||||
rows[i][j] = al === 'l' ? col + pad : pad + col;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
import fetch from 'node-fetch';
|
||||
import { Framework } from '@now/frameworks';
|
||||
import Client from './client';
|
||||
|
||||
export async function getFrameworks(): Promise<Framework[]> {
|
||||
const res = await fetch('https://api.zeit.co/v1/frameworks');
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error('Could not retrieve frameworks');
|
||||
}
|
||||
|
||||
const json: Framework[] = await res.json();
|
||||
|
||||
return json;
|
||||
export async function getFrameworks(client: Client) {
|
||||
return await client.fetch<Framework[]>('/v1/frameworks');
|
||||
}
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
import Client from './client';
|
||||
import getTeams from './get-teams';
|
||||
import { Team } from '../types';
|
||||
|
||||
export default async function getTeamById(client: Client, teamId: string) {
|
||||
const teams = await getTeams(client);
|
||||
return teams.find(team => team.id === teamId) || null;
|
||||
const teamCache = new Map<string, Team>();
|
||||
|
||||
export default async function getTeamById(
|
||||
client: Client,
|
||||
teamId: string
|
||||
): Promise<Team> {
|
||||
let team = teamCache.get(teamId);
|
||||
|
||||
if (!team) {
|
||||
team = await client.fetch<Team>(`/teams/${teamId}`);
|
||||
teamCache.set(teamId, team);
|
||||
}
|
||||
|
||||
return team;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import NowTeams from './teams.js';
|
||||
|
||||
let teams: Team[] | undefined;
|
||||
|
||||
export default async function getTeams(client: Client) {
|
||||
export default async function getTeams(client: Client): Promise<Team[]> {
|
||||
if (teams) return teams;
|
||||
|
||||
try {
|
||||
@@ -17,8 +17,8 @@ export default async function getTeams(client: Client) {
|
||||
debug: client._debug,
|
||||
});
|
||||
|
||||
const teams = (await teamClient.ls()).teams;
|
||||
return teams as Team[];
|
||||
teams = (await teamClient.ls()).teams;
|
||||
return teams || [];
|
||||
} catch (error) {
|
||||
if (error instanceof APIError && error.status === 403) {
|
||||
throw new InvalidToken();
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
export function once<T>(emitter: EventEmitter, name: string): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
function cleanup() {
|
||||
emitter.removeListener(name, onEvent);
|
||||
emitter.removeListener('error', onError);
|
||||
}
|
||||
function onEvent(arg: T) {
|
||||
cleanup();
|
||||
resolve(arg);
|
||||
}
|
||||
function onError(err: Error) {
|
||||
cleanup();
|
||||
reject(err);
|
||||
}
|
||||
emitter.on(name, onEvent);
|
||||
emitter.on('error', onError);
|
||||
});
|
||||
}
|
||||
@@ -123,7 +123,11 @@ export async function getLinkedOrg(
|
||||
|
||||
return { status: 'linked', org };
|
||||
} catch (error) {
|
||||
if (error.code === 'NOT_AUTHORIZED' || error.code === 'TEAM_DELETED') {
|
||||
if (
|
||||
error.status === 403 ||
|
||||
error.code === 'NOT_AUTHORIZED' ||
|
||||
error.code === 'TEAM_DELETED'
|
||||
) {
|
||||
output.error(error.message);
|
||||
return { status: 'error', exitCode: 1 };
|
||||
}
|
||||
|
||||
@@ -2,11 +2,7 @@ import Now from './index';
|
||||
|
||||
export default class Teams extends Now {
|
||||
async create({ slug }) {
|
||||
return this.retry(async (bail, attempt) => {
|
||||
if (this._debug) {
|
||||
console.time(`> [debug] #${attempt} POST /teams}`);
|
||||
}
|
||||
|
||||
return this.retry(async bail => {
|
||||
const res = await this._fetch(`/teams`, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
@@ -14,10 +10,6 @@ export default class Teams extends Now {
|
||||
},
|
||||
});
|
||||
|
||||
if (this._debug) {
|
||||
console.timeEnd(`> [debug] #${attempt} POST /teams`);
|
||||
}
|
||||
|
||||
if (res.status === 403) {
|
||||
return bail(new Error('Unauthorized'));
|
||||
}
|
||||
@@ -40,11 +32,7 @@ export default class Teams extends Now {
|
||||
}
|
||||
|
||||
async edit({ id, slug, name }) {
|
||||
return this.retry(async (bail, attempt) => {
|
||||
if (this._debug) {
|
||||
console.time(`> [debug] #${attempt} PATCH /teams/${id}}`);
|
||||
}
|
||||
|
||||
return this.retry(async bail => {
|
||||
const payload = {};
|
||||
if (name) {
|
||||
payload.name = name;
|
||||
@@ -58,10 +46,6 @@ export default class Teams extends Now {
|
||||
body: payload,
|
||||
});
|
||||
|
||||
if (this._debug) {
|
||||
console.timeEnd(`> [debug] #${attempt} PATCH /teams/${id}`);
|
||||
}
|
||||
|
||||
if (res.status === 403) {
|
||||
return bail(new Error('Unauthorized'));
|
||||
}
|
||||
@@ -84,11 +68,7 @@ export default class Teams extends Now {
|
||||
}
|
||||
|
||||
async inviteUser({ teamId, email }) {
|
||||
return this.retry(async (bail, attempt) => {
|
||||
if (this._debug) {
|
||||
console.time(`> [debug] #${attempt} POST /teams/${teamId}/members}`);
|
||||
}
|
||||
|
||||
return this.retry(async bail => {
|
||||
const publicRes = await this._fetch(`/www/user/public?email=${email}`);
|
||||
const { name, username } = await publicRes.json();
|
||||
|
||||
@@ -99,10 +79,6 @@ export default class Teams extends Now {
|
||||
},
|
||||
});
|
||||
|
||||
if (this._debug) {
|
||||
console.timeEnd(`> [debug] #${attempt} POST /teams/${teamId}/members}`);
|
||||
}
|
||||
|
||||
if (res.status === 403) {
|
||||
return bail(new Error('Unauthorized'));
|
||||
}
|
||||
@@ -126,17 +102,9 @@ export default class Teams extends Now {
|
||||
}
|
||||
|
||||
async ls() {
|
||||
return this.retry(async (bail, attempt) => {
|
||||
if (this._debug) {
|
||||
console.time(`> [debug] #${attempt} GET /teams}`);
|
||||
}
|
||||
|
||||
return this.retry(async bail => {
|
||||
const res = await this._fetch(`/teams`);
|
||||
|
||||
if (this._debug) {
|
||||
console.timeEnd(`> [debug] #${attempt} GET /teams`);
|
||||
}
|
||||
|
||||
if (res.status === 403) {
|
||||
const error = new Error('Unauthorized');
|
||||
error.code = 'not_authorized';
|
||||
|
||||
4
packages/now-cli/src/util/tree-kill.ts
Normal file
4
packages/now-cli/src/util/tree-kill.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import _treeKill from 'tree-kill';
|
||||
import { promisify } from 'util';
|
||||
|
||||
export const treeKill = promisify(_treeKill);
|
||||
@@ -13,10 +13,3 @@ export const normalizeURL = u => {
|
||||
|
||||
return u;
|
||||
};
|
||||
|
||||
export const parseInstanceURL = u => {
|
||||
const m = /^(.+)-([a-z0-9]{24})(\.now\.sh)$/.exec(u);
|
||||
const url = m ? m[1] + m[3] : u;
|
||||
const instanceId = m ? m[2] : null;
|
||||
return [url, instanceId];
|
||||
};
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export default (_req, res) => {
|
||||
module.exports = (_req, res) => {
|
||||
res.end('current date: ' + new Date().toISOString());
|
||||
};
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export default (_req, res) => {
|
||||
module.exports = (_req, res) => {
|
||||
res.end('random number: ' + Math.random());
|
||||
};
|
||||
|
||||
12
packages/now-cli/test/integration-v1.js
vendored
12
packages/now-cli/test/integration-v1.js
vendored
@@ -2299,19 +2299,15 @@ test('now certs ls', async t => {
|
||||
t.regex(output.stderr, /certificates? found under/gm, formatOutput(output));
|
||||
});
|
||||
|
||||
test('now certs ls --after=cert_test', async t => {
|
||||
const output = await execute(['certs', 'ls', '--after=cert_test']);
|
||||
test('now certs ls --next=123456', async t => {
|
||||
const output = await execute(['certs', 'ls', '--next=123456']);
|
||||
|
||||
console.log(output.stderr);
|
||||
console.log(output.stdout);
|
||||
console.log(output.exitCode);
|
||||
|
||||
t.is(output.exitCode, 1, formatOutput(output));
|
||||
t.regex(
|
||||
output.stderr,
|
||||
/The cert cert_test can't be found\./gm,
|
||||
formatOutput(output)
|
||||
);
|
||||
t.is(output.exitCode, 0, formatOutput(output));
|
||||
t.regex(output.stderr, /No certificates found under/gm, formatOutput(output));
|
||||
});
|
||||
|
||||
test('now hasOwnProperty not a valid subcommand', async t => {
|
||||
|
||||
12
packages/now-cli/test/integration.js
vendored
12
packages/now-cli/test/integration.js
vendored
@@ -2015,19 +2015,15 @@ test('now certs ls', async t => {
|
||||
t.regex(output.stderr, /certificates? found under/gm, formatOutput(output));
|
||||
});
|
||||
|
||||
test('now certs ls --after=cert_test', async t => {
|
||||
const output = await execute(['certs', 'ls', '--after=cert_test']);
|
||||
test('now certs ls --next=123456', async t => {
|
||||
const output = await execute(['certs', 'ls', '--next=123456']);
|
||||
|
||||
console.log(output.stderr);
|
||||
console.log(output.stdout);
|
||||
console.log(output.exitCode);
|
||||
|
||||
t.is(output.exitCode, 1, formatOutput(output));
|
||||
t.regex(
|
||||
output.stderr,
|
||||
/The cert cert_test can't be found\./gm,
|
||||
formatOutput(output)
|
||||
);
|
||||
t.is(output.exitCode, 0, formatOutput(output));
|
||||
t.regex(output.stderr, /No certificates found under/gm, formatOutput(output));
|
||||
});
|
||||
|
||||
test('now hasOwnProperty not a valid subcommand', async t => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/next",
|
||||
"version": "2.5.3",
|
||||
"version": "2.5.5-canary.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://zeit.co/docs/runtimes#official-runtimes/next-js",
|
||||
|
||||
@@ -222,9 +222,6 @@ export const build = async ({
|
||||
|
||||
// If this is the initial build, we want to start the server
|
||||
if (!urls[entrypoint]) {
|
||||
debug(`${name} Installing dependencies...`);
|
||||
await runNpmInstall(entryPath, ['--prefer-offline'], spawnOpts, meta);
|
||||
|
||||
if (!process.env.NODE_ENV) {
|
||||
process.env.NODE_ENV = 'development';
|
||||
}
|
||||
@@ -569,7 +566,7 @@ export const build = async ({
|
||||
},
|
||||
|
||||
// error handling
|
||||
...(output['404']
|
||||
...(output[path.join('./', entryDirectory, '404')]
|
||||
? [
|
||||
{ handle: 'error' } as Handler,
|
||||
|
||||
@@ -745,10 +742,10 @@ export const build = async ({
|
||||
// this can be either 404.html in latest versions
|
||||
// or _errors/404.html versions while this was experimental
|
||||
static404Page =
|
||||
staticPages['404'] && hasPages404
|
||||
? '404'
|
||||
: staticPages['_errors/404']
|
||||
? '_errors/404'
|
||||
staticPages[path.join(entryDirectory, '404')] && hasPages404
|
||||
? path.join(entryDirectory, '404')
|
||||
: staticPages[path.join(entryDirectory, '_errors/404')]
|
||||
? path.join(entryDirectory, '_errors/404')
|
||||
: undefined;
|
||||
|
||||
// > 1 because _error is a lambda but isn't used if a static 404 is available
|
||||
@@ -1259,17 +1256,18 @@ export const build = async ({
|
||||
|
||||
{
|
||||
src: path.join('/', entryDirectory, '.*'),
|
||||
dest: path.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
static404Page
|
||||
? static404Page
|
||||
: // if static 404 is not present but we have pages/404.js
|
||||
// it is a lambda due to _app getInitialProps
|
||||
hasPages404 && lambdas['404']
|
||||
? '404'
|
||||
: '_error'
|
||||
),
|
||||
// if static 404 is not present but we have pages/404.js
|
||||
// it is a lambda due to _app getInitialProps
|
||||
dest: static404Page
|
||||
? path.join('/', static404Page)
|
||||
: path.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
hasPages404 &&
|
||||
lambdas[path.join('./', entryDirectory, '404')]
|
||||
? '404'
|
||||
: '_error'
|
||||
),
|
||||
status: 404,
|
||||
},
|
||||
]),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "9.2.3-canary.4",
|
||||
"next": "latest",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
|
||||
1
packages/now-next/test/fixtures/25-mono-repo-404/.gitignore
vendored
Normal file
1
packages/now-next/test/fixtures/25-mono-repo-404/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.now
|
||||
36
packages/now-next/test/fixtures/25-mono-repo-404/now.json
vendored
Normal file
36
packages/now-next/test/fixtures/25-mono-repo-404/now.json
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"version": 2,
|
||||
"routes": [
|
||||
{ "src": "/(.*)", "dest": "/packages/webapp/$1", "continue": true }
|
||||
],
|
||||
"builds": [
|
||||
{
|
||||
"src": "packages/webapp/next.config.js",
|
||||
"use": "@now/next"
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/",
|
||||
"mustContain": "Hi"
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"responseHeaders": {
|
||||
"x-now-cache": "HIT"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/non-existent",
|
||||
"mustContain": "custom 404!!"
|
||||
},
|
||||
{
|
||||
"path": "/non-existent",
|
||||
"mustContain": "__next"
|
||||
},
|
||||
{
|
||||
"path": "/non-existent",
|
||||
"status": 404
|
||||
}
|
||||
]
|
||||
}
|
||||
7
packages/now-next/test/fixtures/25-mono-repo-404/package.json
vendored
Normal file
7
packages/now-next/test/fixtures/25-mono-repo-404/package.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"private": true,
|
||||
"name": "mono-repo"
|
||||
}
|
||||
5
packages/now-next/test/fixtures/25-mono-repo-404/packages/webapp/next.config.js
vendored
Normal file
5
packages/now-next/test/fixtures/25-mono-repo-404/packages/webapp/next.config.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
};
|
||||
9
packages/now-next/test/fixtures/25-mono-repo-404/packages/webapp/package.json
vendored
Normal file
9
packages/now-next/test/fixtures/25-mono-repo-404/packages/webapp/package.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "webapp",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"next": "9.3.4",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
1
packages/now-next/test/fixtures/25-mono-repo-404/packages/webapp/pages/404.js
vendored
Normal file
1
packages/now-next/test/fixtures/25-mono-repo-404/packages/webapp/pages/404.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export default () => 'custom 404!!';
|
||||
1
packages/now-next/test/fixtures/25-mono-repo-404/packages/webapp/pages/index.js
vendored
Normal file
1
packages/now-next/test/fixtures/25-mono-repo-404/packages/webapp/pages/index.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export default () => 'Hi';
|
||||
4607
packages/now-next/test/fixtures/25-mono-repo-404/yarn.lock
vendored
Normal file
4607
packages/now-next/test/fixtures/25-mono-repo-404/yarn.lock
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
packages/now-next/test/fixtures/26-mono-repo-404-lambda/.gitignore
vendored
Normal file
1
packages/now-next/test/fixtures/26-mono-repo-404-lambda/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.now
|
||||
30
packages/now-next/test/fixtures/26-mono-repo-404-lambda/now.json
vendored
Normal file
30
packages/now-next/test/fixtures/26-mono-repo-404-lambda/now.json
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"version": 2,
|
||||
"routes": [
|
||||
{ "src": "/(.*)", "dest": "/packages/webapp/$1", "continue": true }
|
||||
],
|
||||
"builds": [
|
||||
{
|
||||
"src": "packages/webapp/next.config.js",
|
||||
"use": "@now/next"
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/",
|
||||
"mustContain": "Hi"
|
||||
},
|
||||
{
|
||||
"path": "/non-existent",
|
||||
"mustContain": "custom 404!!"
|
||||
},
|
||||
{
|
||||
"path": "/non-existent",
|
||||
"mustContain": "__next"
|
||||
},
|
||||
{
|
||||
"path": "/non-existent",
|
||||
"status": 404
|
||||
}
|
||||
]
|
||||
}
|
||||
7
packages/now-next/test/fixtures/26-mono-repo-404-lambda/package.json
vendored
Normal file
7
packages/now-next/test/fixtures/26-mono-repo-404-lambda/package.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"private": true,
|
||||
"name": "mono-repo"
|
||||
}
|
||||
5
packages/now-next/test/fixtures/26-mono-repo-404-lambda/packages/webapp/next.config.js
vendored
Normal file
5
packages/now-next/test/fixtures/26-mono-repo-404-lambda/packages/webapp/next.config.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
};
|
||||
9
packages/now-next/test/fixtures/26-mono-repo-404-lambda/packages/webapp/package.json
vendored
Normal file
9
packages/now-next/test/fixtures/26-mono-repo-404-lambda/packages/webapp/package.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "webapp",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"next": "9.3.4",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
1
packages/now-next/test/fixtures/26-mono-repo-404-lambda/packages/webapp/pages/404.js
vendored
Normal file
1
packages/now-next/test/fixtures/26-mono-repo-404-lambda/packages/webapp/pages/404.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export default () => 'custom 404!!';
|
||||
12
packages/now-next/test/fixtures/26-mono-repo-404-lambda/packages/webapp/pages/_app.js
vendored
Normal file
12
packages/now-next/test/fixtures/26-mono-repo-404-lambda/packages/webapp/pages/_app.js
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
const App = ({ Component, pageProps }) => <Component {...pageProps} />;
|
||||
|
||||
App.getInitialProps = async ({ ctx, Component }) => {
|
||||
let pageProps = {};
|
||||
|
||||
if (Component.getInitialProps) {
|
||||
pageProps = await Component.getInitialProps(ctx);
|
||||
}
|
||||
return { pageProps };
|
||||
};
|
||||
|
||||
export default App;
|
||||
1
packages/now-next/test/fixtures/26-mono-repo-404-lambda/packages/webapp/pages/index.js
vendored
Normal file
1
packages/now-next/test/fixtures/26-mono-repo-404-lambda/packages/webapp/pages/index.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export default () => 'Hi';
|
||||
4607
packages/now-next/test/fixtures/26-mono-repo-404-lambda/yarn.lock
vendored
Normal file
4607
packages/now-next/test/fixtures/26-mono-repo-404-lambda/yarn.lock
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/node",
|
||||
"version": "1.5.1",
|
||||
"version": "1.5.2-canary.1",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://zeit.co/docs/runtimes#official-runtimes/node-js",
|
||||
@@ -24,6 +24,7 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.5.0",
|
||||
"@babel/plugin-transform-modules-commonjs": "7.5.0",
|
||||
"@tootallnate/once": "1.0.0",
|
||||
"@types/aws-lambda": "8.10.19",
|
||||
"@types/content-type": "1.1.3",
|
||||
"@types/cookie": "0.3.3",
|
||||
|
||||
54
packages/now-node/src/dev-server.ts
Normal file
54
packages/now-node/src/dev-server.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import http from 'http';
|
||||
import path from 'path';
|
||||
import { createServerWithHelpers } from './helpers';
|
||||
|
||||
function listen(
|
||||
server: http.Server,
|
||||
port: number,
|
||||
host: string
|
||||
): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
server.listen(port, host, () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const entrypoint = process.env.NOW_DEV_ENTRYPOINT;
|
||||
if (!entrypoint) {
|
||||
throw new Error('`NOW_DEV_ENTRYPOINT` must be defined');
|
||||
}
|
||||
|
||||
//const shouldAddHelpers = true;
|
||||
|
||||
const entrypointPath = path.join(process.cwd(), entrypoint);
|
||||
const handler = await import(entrypointPath);
|
||||
|
||||
/*
|
||||
const server = http.createServer((req, res) => {
|
||||
Promise.resolve(true).then(() => handler.default(req, res)).catch(err => {
|
||||
console.error('Caught error from HTTP handler:', err);
|
||||
if (!res.headersSent) {
|
||||
res.statusCode = 500;
|
||||
res.end('Internal server error\n');
|
||||
}
|
||||
});
|
||||
});
|
||||
*/
|
||||
const server = createServerWithHelpers(handler.default);
|
||||
|
||||
await listen(server, 0, '127.0.0.1');
|
||||
|
||||
const address = server.address();
|
||||
if (typeof process.send === 'function') {
|
||||
process.send(address);
|
||||
} else {
|
||||
console.log('Dev server listening:', address);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
NowRequestBody,
|
||||
} from './types';
|
||||
import { Server } from 'http';
|
||||
import { Readable } from 'stream';
|
||||
import { Bridge } from './bridge';
|
||||
|
||||
function getBodyParser(req: NowRequest, body: Buffer) {
|
||||
@@ -81,6 +82,7 @@ function setCharset(type: string, charset: string) {
|
||||
return format(parsed);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function createETag(body: any, encoding: 'utf8' | undefined) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const etag = require('etag');
|
||||
@@ -88,6 +90,7 @@ function createETag(body: any, encoding: 'utf8' | undefined) {
|
||||
return etag(buf, { weak: true });
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function send(req: NowRequest, res: NowResponse, body: any): NowResponse {
|
||||
let chunk: unknown = body;
|
||||
let encoding: 'utf8' | undefined;
|
||||
@@ -185,6 +188,7 @@ function send(req: NowRequest, res: NowResponse, body: any): NowResponse {
|
||||
return res;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function json(req: NowRequest, res: NowResponse, jsonBody: any): NowResponse {
|
||||
const body = JSON.stringify(jsonBody);
|
||||
|
||||
@@ -233,9 +237,24 @@ function setLazyProp<T>(req: NowRequest, prop: string, getter: () => T) {
|
||||
});
|
||||
}
|
||||
|
||||
export function rawBody(readable: Readable): Promise<Buffer> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let bytes = 0;
|
||||
const chunks: Buffer[] = [];
|
||||
readable.on('error', reject);
|
||||
readable.on('data', chunk => {
|
||||
chunks.push(chunk);
|
||||
bytes += chunk.length;
|
||||
});
|
||||
readable.on('end', () => {
|
||||
resolve(Buffer.concat(chunks, bytes));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function createServerWithHelpers(
|
||||
listener: (req: NowRequest, res: NowResponse) => void | Promise<void>,
|
||||
bridge: Bridge
|
||||
handler: (req: NowRequest, res: NowResponse) => void | Promise<void>,
|
||||
bridge?: Bridge
|
||||
) {
|
||||
const server = new Server(async (_req, _res) => {
|
||||
const req = _req as NowRequest;
|
||||
@@ -247,21 +266,23 @@ export function createServerWithHelpers(
|
||||
// don't expose this header to the client
|
||||
delete req.headers['x-now-bridge-request-id'];
|
||||
|
||||
if (typeof reqId !== 'string') {
|
||||
throw new ApiError(500, 'Internal Server Error');
|
||||
let body: Buffer;
|
||||
if (typeof reqId === 'string' && bridge) {
|
||||
const event = bridge.consumeEvent(reqId);
|
||||
body = event.body;
|
||||
} else {
|
||||
body = await rawBody(req);
|
||||
}
|
||||
|
||||
const event = bridge.consumeEvent(reqId);
|
||||
|
||||
setLazyProp<NowRequestCookies>(req, 'cookies', getCookieParser(req));
|
||||
setLazyProp<NowRequestQuery>(req, 'query', getQueryParser(req));
|
||||
setLazyProp<NowRequestBody>(req, 'body', getBodyParser(req, event.body));
|
||||
setLazyProp<NowRequestBody>(req, 'body', getBodyParser(req, body));
|
||||
|
||||
res.status = statusCode => status(res, statusCode);
|
||||
res.send = body => send(req, res, body);
|
||||
res.json = jsonBody => json(req, res, jsonBody);
|
||||
|
||||
await listener(req, res);
|
||||
await handler(req, res);
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
sendError(res, err.statusCode, err.message);
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { fork } from 'child_process';
|
||||
import { readFileSync, lstatSync, readlinkSync, statSync } from 'fs';
|
||||
import {
|
||||
basename,
|
||||
dirname,
|
||||
extname,
|
||||
join,
|
||||
relative,
|
||||
resolve,
|
||||
sep,
|
||||
parse as parsePath,
|
||||
} from 'path';
|
||||
import nodeFileTrace from '@zeit/node-file-trace';
|
||||
import {
|
||||
glob,
|
||||
download,
|
||||
@@ -23,16 +25,20 @@ import {
|
||||
getSpawnOptions,
|
||||
PrepareCacheOptions,
|
||||
BuildOptions,
|
||||
StartDevServerOptions,
|
||||
StartDevServerResult,
|
||||
shouldServe,
|
||||
Config,
|
||||
debug,
|
||||
isSymbolicLink,
|
||||
} from '@now/build-utils';
|
||||
import once from '@tootallnate/once';
|
||||
import nodeFileTrace from '@zeit/node-file-trace';
|
||||
import { makeNowLauncher, makeAwsLauncher } from './launcher';
|
||||
import { Register, register } from './typescript';
|
||||
|
||||
export { shouldServe };
|
||||
export { NowRequest, NowResponse } from './types';
|
||||
import { makeNowLauncher, makeAwsLauncher } from './launcher';
|
||||
import { readFileSync, lstatSync, readlinkSync, statSync } from 'fs';
|
||||
import { Register, register } from './typescript';
|
||||
|
||||
interface CompilerConfig {
|
||||
debug?: boolean;
|
||||
@@ -65,8 +71,6 @@ async function downloadInstallAndBundle({
|
||||
}: DownloadOptions) {
|
||||
const downloadedFiles = await download(files, workPath, meta);
|
||||
|
||||
console.log('Installing dependencies...');
|
||||
const installTime = Date.now();
|
||||
const entrypointFsDirname = join(workPath, dirname(entrypoint));
|
||||
const nodeVersion = await getNodeVersion(
|
||||
entrypointFsDirname,
|
||||
@@ -75,13 +79,20 @@ async function downloadInstallAndBundle({
|
||||
meta
|
||||
);
|
||||
const spawnOpts = getSpawnOptions(meta, nodeVersion);
|
||||
await runNpmInstall(
|
||||
entrypointFsDirname,
|
||||
['--prefer-offline'],
|
||||
spawnOpts,
|
||||
meta
|
||||
);
|
||||
debug(`Install complete [${Date.now() - installTime}ms]`);
|
||||
|
||||
if (meta.isDev) {
|
||||
debug('Skipping dependency installation because dev mode is enabled');
|
||||
} else {
|
||||
const installTime = Date.now();
|
||||
console.log('Installing dependencies...');
|
||||
await runNpmInstall(
|
||||
entrypointFsDirname,
|
||||
['--prefer-offline'],
|
||||
spawnOpts,
|
||||
meta
|
||||
);
|
||||
debug(`Install complete [${Date.now() - installTime}ms]`);
|
||||
}
|
||||
|
||||
const entrypointPath = downloadedFiles[entrypoint].fsPath;
|
||||
return { entrypointPath, entrypointFsDirname, nodeVersion, spawnOpts };
|
||||
@@ -309,6 +320,7 @@ export async function build({
|
||||
const shouldAddHelpers = !(
|
||||
config.helpers === false || process.env.NODEJS_HELPERS === '0'
|
||||
);
|
||||
|
||||
const awsLambdaHandler = getAWSLambdaHandler(entrypoint, config);
|
||||
|
||||
const {
|
||||
@@ -388,3 +400,46 @@ export async function prepareCache({
|
||||
const cache = await glob('node_modules/**', workPath);
|
||||
return cache;
|
||||
}
|
||||
|
||||
interface PortInfo {
|
||||
port: number;
|
||||
}
|
||||
|
||||
function isPortInfo(v: any): v is PortInfo {
|
||||
return v && typeof v.port === 'number';
|
||||
}
|
||||
|
||||
export async function startDevServer({
|
||||
entrypoint,
|
||||
workPath,
|
||||
}: StartDevServerOptions): Promise<StartDevServerResult> {
|
||||
if (extname(entrypoint) === '.ts') {
|
||||
// TypeScript isn't supported at the moment, so return `null`
|
||||
// for `now dev` to go through the regular `build()` pipeline.
|
||||
return null;
|
||||
}
|
||||
|
||||
const devServerPath = join(__dirname, 'dev-server.js');
|
||||
const child = fork(devServerPath, [], {
|
||||
cwd: workPath,
|
||||
env: {
|
||||
...process.env,
|
||||
NOW_DEV_ENTRYPOINT: entrypoint,
|
||||
},
|
||||
});
|
||||
const { pid } = child;
|
||||
const onMessage = once<{ port: number }>(child, 'message');
|
||||
const onExit = once<{ code: number; signal: string | null }>(child, 'exit');
|
||||
const result = await Promise.race([onMessage, onExit]);
|
||||
onExit.cancel();
|
||||
onMessage.cancel();
|
||||
if (isPortInfo(result)) {
|
||||
// "message" event
|
||||
return { port: result.port, pid };
|
||||
} else {
|
||||
// "exit" event
|
||||
throw new Error(
|
||||
`Failed to start dev server for "${entrypoint}" (code=${result.code}, signal=${result.signal})`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/routing-utils",
|
||||
"version": "1.8.1",
|
||||
"version": "1.8.2-canary.0",
|
||||
"description": "ZEIT Now routing utilities",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -90,12 +90,10 @@ export function convertHeaders(headers: NowHeader[]): Route[] {
|
||||
h.headers.forEach(({ key, value }) => {
|
||||
if (hasSegments) {
|
||||
if (key.includes(':')) {
|
||||
const keyCompiler = compile(key);
|
||||
key = keyCompiler(indexes);
|
||||
key = safelyCompile(key, indexes);
|
||||
}
|
||||
if (value.includes(':')) {
|
||||
const valueCompiler = compile(value);
|
||||
value = valueCompiler(indexes);
|
||||
value = safelyCompile(value, indexes);
|
||||
}
|
||||
}
|
||||
obj[key] = value;
|
||||
@@ -165,16 +163,13 @@ function replaceSegments(
|
||||
});
|
||||
|
||||
if (destination.includes(':') && segments.length > 0) {
|
||||
const pathnameCompiler = compile(pathname);
|
||||
const hashCompiler = compile(hash);
|
||||
pathname = pathnameCompiler(indexes);
|
||||
hash = hash ? `${hashCompiler(indexes)}` : null;
|
||||
pathname = safelyCompile(pathname, indexes);
|
||||
hash = hash ? safelyCompile(hash, indexes) : null;
|
||||
|
||||
for (const [key, strOrArray] of Object.entries(query)) {
|
||||
let value = Array.isArray(strOrArray) ? strOrArray[0] : strOrArray;
|
||||
if (value) {
|
||||
const queryCompiler = compile(value);
|
||||
value = queryCompiler(indexes);
|
||||
value = safelyCompile(value, indexes);
|
||||
}
|
||||
query[key] = value;
|
||||
}
|
||||
@@ -197,13 +192,27 @@ function replaceSegments(
|
||||
hash,
|
||||
});
|
||||
|
||||
// url.format() escapes the query string but we must preserve dollar signs
|
||||
destination = destination.replace(/=%24/g, '=$');
|
||||
// url.format() escapes the dollar sign but it must be preserved for now-proxy
|
||||
destination = destination.replace(/%24/g, '$');
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
function safelyCompile(str: string, indexes: { [k: string]: string }): string {
|
||||
if (!str) {
|
||||
return str;
|
||||
}
|
||||
// path-to-regexp cannot compile question marks
|
||||
return str
|
||||
.split('?')
|
||||
.map(part => {
|
||||
const compiler = compile(part);
|
||||
return compiler(indexes);
|
||||
})
|
||||
.join('?');
|
||||
}
|
||||
|
||||
function toSegmentDest(index: number): string {
|
||||
const i = index + 1; // js is base 0, regex is base 1
|
||||
return '$' + i.toString();
|
||||
|
||||
@@ -181,6 +181,15 @@ test('convertRedirects', () => {
|
||||
source: '/hello/:world*',
|
||||
destination: '/something#:world*',
|
||||
},
|
||||
{
|
||||
source: '/external/:id',
|
||||
destination:
|
||||
'https://example.com/?utm_source=google.com#/guides/:id/page?dynamic=code',
|
||||
},
|
||||
{
|
||||
source: '/optional/:id?',
|
||||
destination: '/api/optional/:id?',
|
||||
},
|
||||
]);
|
||||
|
||||
const expected = [
|
||||
@@ -244,6 +253,19 @@ test('convertRedirects', () => {
|
||||
headers: { Location: '/something#$1' },
|
||||
status: 308,
|
||||
},
|
||||
{
|
||||
src: '^\\/external(?:\\/([^\\/]+?))$',
|
||||
headers: {
|
||||
Location:
|
||||
'https://example.com/?utm_source=google.com#/guides/$1/page?dynamic=code',
|
||||
},
|
||||
status: 308,
|
||||
},
|
||||
{
|
||||
src: '^\\/optional(?:\\/([^\\/]+?))?$',
|
||||
headers: { Location: '/api/optional/$1' },
|
||||
status: 308,
|
||||
},
|
||||
];
|
||||
|
||||
deepEqual(actual, expected);
|
||||
@@ -261,6 +283,8 @@ test('convertRedirects', () => {
|
||||
['/feedback/another'],
|
||||
['/catchme/id-1', '/catchme/id/2'],
|
||||
['/hello/world', '/hello/another/world'],
|
||||
['/external/1', '/external/2'],
|
||||
['/optional', '/optional/1'],
|
||||
];
|
||||
|
||||
const mustNotMatch = [
|
||||
@@ -276,6 +300,8 @@ test('convertRedirects', () => {
|
||||
['/feedback/general'],
|
||||
['/catchm', '/random'],
|
||||
['/not-this-one', '/helloo'],
|
||||
['/externalnope', '/externally'],
|
||||
['/optionalnope', '/optionally'],
|
||||
];
|
||||
|
||||
assertRegexMatches(actual, mustMatch, mustNotMatch);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/static-build",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1-canary.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://zeit.co/docs/runtimes#official-runtimes/static-builds",
|
||||
|
||||
@@ -337,8 +337,14 @@ export async function build({
|
||||
);
|
||||
const spawnOpts = getSpawnOptions(meta, nodeVersion);
|
||||
|
||||
console.log('Installing dependencies...');
|
||||
await runNpmInstall(entrypointDir, ['--prefer-offline'], spawnOpts, meta);
|
||||
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 (pkg && (buildCommand || devCommand)) {
|
||||
// We want to add `node_modules/.bin` after `npm install`
|
||||
|
||||
@@ -1422,7 +1422,7 @@
|
||||
dependencies:
|
||||
defer-to-connect "^1.0.1"
|
||||
|
||||
"@tootallnate/once@1":
|
||||
"@tootallnate/once@1", "@tootallnate/once@1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.0.0.tgz#9c13c2574c92d4503b005feca8f2e16cc1611506"
|
||||
integrity sha512-KYyTT/T6ALPkIRd2Ge080X/BsXvy9O0hcWTtMWkPvwAwF99+vn6Dv4GzrFT/Nn1LePr+FFDbRXXlqmsy9lw2zA==
|
||||
|
||||
Reference in New Issue
Block a user