Compare commits

..

17 Commits

Author SHA1 Message Date
Joe Haddad
4e58951808 Publish
- @now/next@1.0.4
2019-10-20 14:56:29 -04:00
Joe Haddad
fbd805aad7 [now-next] Update console.time labels for clarity (#3157) 2019-10-20 14:37:35 -04:00
JJ Kasper
2a2705c6e3 Add test for now dev and Next.js src dir (#3149)
Follow up on #3140 we needed to publish the change before we could test it in `now dev`
2019-10-20 14:37:27 -04:00
JJ Kasper
986c957183 [now-next] Add support for src dir in now-dev (#3140)
Fixes: #3133
Fixes: https://github.com/zeit/next.js/issues/9007
2019-10-20 14:37:13 -04:00
Nathan Rajlich
c5d063e876 [now-next] Exit dev server child processes upon SIGINT/SIGTERM (#3137)
Explicitly send the SIGINT / SIGTERM signal to `now dev` server child processes, so that they are not left running when running the now-dev unit tests.

Related to #3113 which has hanging unit tests that never "complete".
2019-10-20 14:37:07 -04:00
Steven
500c36f5d4 Publish
- now@16.3.1
 - now-client@5.2.0
 - @now/next@1.0.3
 - @now/python@0.3.1
 - @now/static-build@0.10.1
2019-10-03 13:54:14 -04:00
Steven
69dbbeac44 [now-cli][now-client] Fix v1 files when defining a directory (#3123)
This is a follow up to #3117 which added a fix for `files` but did not observe directories.

This PR fixes the scenario where a directory is defined such that all files inside the directory should be added uploaded (recursively).

Thanks to @williamli 

[PRODUCT-350] #close

[PRODUCT-350]: https://zeit.atlassian.net/browse/PRODUCT-350
2019-10-03 13:44:21 -04:00
Sophearak Tha
69486c3adb [now-client] [now-cli] Handle notice type (#3122)
This PR handle `notice` type from API respond.
2019-10-03 12:14:02 -04:00
Steven
e6692bb79b [now-cli][now-client] Fix --local-config flag and files key (#3117)
This PR is a followup to #3110 that fixes the first deployment when using the `--local-config` flag and also fixes v1 deployments using the [`files`](https://zeit.co/docs/v1/features/configuration/#files-(array)) key.

The tests have been adjusted so we don't regress in both cases.

Fixes #3099 
Fixes #3105
Fixes #3107
Fixes #3109

[PRODUCT-350] #close

[PRODUCT-350]: https://zeit.atlassian.net/browse/PRODUCT-350
2019-10-03 09:07:01 -04:00
Chris
94fba1d7af [now-python] Encode body as utf-8 before making a request (#3093)
Fixes #3091
2019-10-03 09:06:54 -04:00
Sophearak Tha
223d8f4774 [now-cli] Rename lambda to serverless function (#3100)
This PR fix: [PRODUCT-66] #close

[PRODUCT-66]: https://zeit.atlassian.net/browse/PRODUCT-66
2019-10-03 09:06:49 -04:00
Nathan Rajlich
42e7a7e4e3 [now-static-build] Use stdio: 'inherit' for "dev" script child process (#3112)
Since `@now/static-build` is no longer sniffing the stdio streams for the bound port number in `now dev`, there's no need to have separate stdio streams for the "dev" script. Instead, inherit stdio from the parent process, which will allow for ANSI colors to be used when stdout is a TTY in `now dev`.

Also simplifies the `checkForPort()` function and removes the `promise-timeout` dependency.
2019-10-03 09:06:44 -04:00
Steven
6716fdd49b [now-cli][now-client] Add parameter nowConfig for custom now.json (#3110)
When now-client was implemented, it did not work with `--local-config` flag from now-cli because the only parameters it looks at are the files in a directory.

This fixes the regression in now@16.3.0 so that now-client can accept an optional `nowConfig` object or fallback to the `now.json` file.

Fixes #3099 
Fixes #3105
Fixes #3107
Fixes #3109

[PRODUCT-350] #close


[PRODUCT-350]: https://zeit.atlassian.net/browse/PRODUCT-350
2019-10-03 09:06:38 -04:00
Steven
3b69092fd8 Revert "[tests] Add test all script" since it runs on commit (#3108)
Reverts #3106 from @MAPESO 

The tests seem to be running on each commit which is going to slow down development.
2019-10-03 09:06:32 -04:00
Luis Alvarez D
aa8eaedbc8 [now-next] Upload build-time generated static artifacts (#3096)
For context, when you have a script that generates a new static file at build time (`sitemap.xml` for example), it has to be inside `.next/static`, and then you'll need a Now route for it, with this change you could generate the file inside `public`/`static` and the builder will now take care of it.

The util `includeOnlyEntryDirectory` is no longer being used after this change, should I remove it?
2019-10-03 09:06:25 -04:00
Mark
f519ed373f [tests] Add test all script (#3106)
This PR focuses on adding the `test` script to the `package.json` : )

## Main problem 

Previously there was no `test` script that includes all the tests

<img width="924" alt="Screen Shot 2019-10-01 at 8 13 13 AM" src="https://user-images.githubusercontent.com/16585386/65968681-26f6a080-e429-11e9-9f29-c6fd343fdb12.png">
2019-10-03 09:06:20 -04:00
Ana Trajkovska
851dff4b03 [now-cli] Integrate Projects API v2 (#3063)
This PR integrates v2 of Projects API that fixes an issue for projects named `list` or `remove`, because of the naming of the endpoints in v1. For listing all projects, previously in v1 it was `GET /v1/projects/list` and now it is `GET /v2/projects/`, and for removing a project it was `DELETE /v1/projects/remove`.
2019-10-03 09:06:14 -04:00
35 changed files with 6102 additions and 234 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "now",
"version": "16.3.0",
"version": "16.3.1",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Now",

View File

@@ -10,7 +10,7 @@ import Client from '../util/client.ts';
import logo from '../util/output/logo';
import getScope from '../util/get-scope';
const e = encodeURIComponent
const e = encodeURIComponent;
const help = () => {
console.log(`
@@ -48,8 +48,8 @@ const main = async ctx => {
argv = mri(ctx.argv.slice(2), {
boolean: ['help'],
alias: {
help: 'h'
}
help: 'h',
},
});
argv._ = argv._.slice(1);
@@ -63,7 +63,10 @@ const main = async ctx => {
await exit(0);
}
const { authConfig: { token }, config: { currentTeam }} = ctx;
const {
authConfig: { token },
config: { currentTeam },
} = ctx;
const client = new Client({ apiUrl, token, currentTeam, debug });
const { contextName } = await getScope(client);
@@ -93,17 +96,21 @@ async function run({ client, contextName }) {
if (args.length !== 0) {
console.error(
error(
`Invalid number of arguments. Usage: ${chalk.cyan('`now projects ls`')}`
`Invalid number of arguments. Usage: ${chalk.cyan(
'`now projects ls`'
)}`
)
);
return exit(1);
}
const list = await client.fetch('/projects/list', {method: 'GET'});
const list = await client.fetch('/v2/projects/', { method: 'GET' });
const elapsed = ms(new Date() - start);
console.log(
`> ${plural('project', list.length, true)} found under ${chalk.bold(contextName)} ${chalk.gray(`[${elapsed}]`)}`
`> ${plural('project', list.length, true)} found under ${chalk.bold(
contextName
)} ${chalk.gray(`[${elapsed}]`)}`
);
if (list.length > 0) {
@@ -114,19 +121,19 @@ async function run({ client, contextName }) {
header.concat(
list.map(secret => [
'',
chalk.bold(secret.name),
chalk.gray(`${ms(cur - new Date(secret.updatedAt)) } ago`)
])
chalk.bold(secret.name),
chalk.gray(`${ms(cur - new Date(secret.updatedAt))} ago`),
])
),
{
align: ['l', 'l', 'l'],
hsep: ' '.repeat(2),
stringLength: strlen
stringLength: strlen,
}
);
if (out) {
console.log(`\n${ out }\n`);
console.log(`\n${out}\n`);
}
}
return;
@@ -148,11 +155,11 @@ async function run({ client, contextName }) {
// Check the existence of the project
try {
await client.fetch(`/projects/info/${e(name)}`)
} catch(err) {
await client.fetch(`/projects/info/${e(name)}`);
} catch (err) {
if (err.status === 404) {
console.error(error('No such project exists'))
return exit(1)
console.error(error('No such project exists'));
return exit(1);
}
}
@@ -162,7 +169,9 @@ async function run({ client, contextName }) {
return exit(0);
}
await client.fetch('/projects/remove', {method: 'DELETE', body: {name}});
await client.fetch(`/v2/projects/${name}`, {
method: 'DELETE',
});
const elapsed = ms(new Date() - start);
console.log(
`${chalk.cyan('> Success!')} Project ${chalk.bold(
@@ -193,7 +202,10 @@ async function run({ client, contextName }) {
}
const [name] = args;
await client.fetch('/projects/ensure-project', {method: 'POST', body: {name}});
await client.fetch('/projects/ensure-project', {
method: 'POST',
body: { name },
});
const elapsed = ms(new Date() - start);
console.log(
@@ -204,9 +216,7 @@ async function run({ client, contextName }) {
return;
}
console.error(
error('Please specify a valid subcommand: ls | add | rm')
);
console.error(error('Please specify a valid subcommand: ls | add | rm'));
help();
exit(1);
}
@@ -220,7 +230,7 @@ function readConfirmation(projectName) {
return new Promise(resolve => {
process.stdout.write(
`The project: ${chalk.bold(projectName)} will be removed permanently.\n` +
`It will also delete everything under the project including deployments.\n`
`It will also delete everything under the project including deployments.\n`
);
process.stdout.write(

View File

@@ -5,13 +5,17 @@ import pluralize from 'pluralize';
import {
createDeployment,
createLegacyDeployment,
DeploymentOptions,
} from '../../../../now-client';
import wait from '../output/wait';
import createOutput from '../output';
import { Output } from '../output';
// @ts-ignore
import Now from '../../util';
import { NowConfig } from '../dev/types';
export default async function processDeployment({
now,
debug,
output,
hashes,
paths,
requestBody,
@@ -20,18 +24,34 @@ export default async function processDeployment({
legacy,
env,
quiet,
}: any) {
const { warn, log } = createOutput({ debug });
nowConfig,
}: {
now: Now;
output: Output;
hashes: { [key: string]: any };
paths: string[];
requestBody: DeploymentOptions;
uploadStamp: () => number;
deployStamp: () => number;
legacy: boolean;
env: any;
quiet: boolean;
nowConfig?: NowConfig;
}) {
const { warn, log, debug, note } = output;
let bar: Progress | null = null;
const path0 = paths[0];
const opts: DeploymentOptions = {
...requestBody,
debug: now._debug,
};
if (!legacy) {
let buildSpinner = null;
let deploySpinner = null;
for await (const event of createDeployment(paths[0], {
...requestBody,
debug: now._debug,
})) {
for await (const event of createDeployment(path0, opts, nowConfig)) {
if (event.type === 'hashes-calculated') {
hashes = event.payload;
}
@@ -40,6 +60,10 @@ export default async function processDeployment({
warn(event.payload);
}
if (event.type === 'notice') {
note(event.payload);
}
if (event.type === 'file_count') {
debug(
`Total files ${event.payload.total.size}, ${event.payload.missing.length} changed`
@@ -128,10 +152,7 @@ export default async function processDeployment({
}
}
} else {
for await (const event of createLegacyDeployment(paths[0], {
...requestBody,
debug: now._debug,
})) {
for await (const event of createLegacyDeployment(path0, opts, nowConfig)) {
if (event.type === 'hashes-calculated') {
hashes = event.payload;
}

View File

@@ -15,7 +15,6 @@ export default async function getConfig(output: Output, configFile?: string) {
if (config) {
return config;
}
// First try with the config supplied by the user via --local-config
if (configFile) {
const localFilePath = path.resolve(localPath, configFile);
@@ -27,8 +26,7 @@ export default async function getConfig(output: Output, configFile?: string) {
return localConfig;
}
if (localConfig !== null) {
const castedConfig = localConfig;
config = castedConfig;
config = localConfig;
return config;
}
}

View File

@@ -142,13 +142,14 @@ export default class Now extends EventEmitter {
if (isBuilds) {
deployment = await processDeployment({
now: this,
debug,
output: this._output,
hashes,
paths,
requestBody,
uploadStamp,
deployStamp,
quiet,
nowConfig,
});
} else {
// Read `registry.npmjs.org` authToken from .npmrc
@@ -183,7 +184,7 @@ export default class Now extends EventEmitter {
deployment = await processDeployment({
legacy: true,
now: this,
debug,
output: this._output,
hashes,
paths,
requestBody,
@@ -191,6 +192,7 @@ export default class Now extends EventEmitter {
deployStamp,
quiet,
env,
nowConfig,
});
}
@@ -377,7 +379,7 @@ export default class Now extends EventEmitter {
if (!app && !Object.keys(meta).length) {
// Get the 35 latest projects and their latest deployment
const query = new URLSearchParams({ limit: 35 });
const projects = await fetchRetry(`/projects/list?${query}`);
const projects = await fetchRetry(`/v2/projects/?${query}`);
const deployments = await Promise.all(
projects.map(async ({ id: projectId }) => {

View File

@@ -0,0 +1,2 @@
node_modules
.next

View File

@@ -0,0 +1,2 @@
README.md
yarn.lock

View File

@@ -0,0 +1,13 @@
{
"name": "nextjs",
"license": "MIT",
"scripts": {
"dev": "next",
"build": "next build"
},
"dependencies": {
"next": "^9.1.1",
"react": "^16.7.0",
"react-dom": "^16.7.0"
}
}

View File

@@ -0,0 +1,102 @@
import { useEffect, useState } from 'react';
import Head from 'next/head';
function Index() {
const [date, setDate] = useState(null);
useEffect(() => {
async function getDate() {
const res = await fetch('/api/date');
const newDate = await res.text();
setDate(newDate);
}
getDate();
}, []);
return (
<main>
<Head>
<title>Next.js + Node API</title>
</Head>
<h1>Next.js + Node.js API</h1>
<h2>
Deployed with{' '}
<a
href="https://zeit.co/docs"
target="_blank"
rel="noreferrer noopener"
>
ZEIT Now
</a>
!
</h2>
<p>
<a
href="https://github.com/zeit/now-examples/blob/master/nextjs-node"
target="_blank"
rel="noreferrer noopener"
>
This project
</a>{' '}
is a <a href="https://nextjs.org/">Next.js</a> app with two directories,{' '}
<code>/pages</code> for static content and <code>/api</code> which
contains a serverless <a href="https://nodejs.org/en/">Node.js</a>{' '}
function. See{' '}
<a href="/api/date">
<code>api/date</code> for the Date API with Node.js
</a>
.
</p>
<br />
<h2>The date according to Node.js is:</h2>
<p>{date ? date : 'Loading date...'}</p>
<style jsx>{`
main {
align-content: center;
box-sizing: border-box;
display: grid;
font-family: 'SF Pro Text', 'SF Pro Icons', 'Helvetica Neue',
'Helvetica', 'Arial', sans-serif;
hyphens: auto;
line-height: 1.65;
margin: 0 auto;
max-width: 680px;
min-height: 100vh;
padding: 72px 0;
text-align: center;
}
h1 {
font-size: 45px;
}
h2 {
margin-top: 1.5em;
}
p {
font-size: 16px;
}
a {
border-bottom: 1px solid white;
color: #0076ff;
cursor: pointer;
text-decoration: none;
transition: all 0.2s ease;
}
a:hover {
border-bottom: 1px solid #0076ff;
}
code,
pre {
color: #d400ff;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono,
DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace,
serif;
font-size: 0.92em;
}
code:before,
code:after {
content: '\`';
}
`}</style>
</main>
);
}
export default Index;

File diff suppressed because it is too large Load Diff

View File

@@ -869,3 +869,23 @@ test('[now dev] do not rebuild for changes in the output directory', async t =>
dev.kill('SIGTERM');
}
});
test('[now dev] 25-nextjs-src-dir', async t => {
const directory = fixture('25-nextjs-src-dir');
const { dev, port } = await testFixture(directory);
try {
// start `now dev` detached in child_process
dev.unref();
const result = await fetchWithRetry(`http://localhost:${port}`, 80);
const response = await result;
validateResponseHeaders(t, response);
const body = await response.text();
t.regex(body, /Next.js \+ Node.js API/gm);
} finally {
dev.kill('SIGTERM');
}
});

View File

@@ -267,6 +267,69 @@ module.exports = (req, res) => {
Object.assign(JSON.parse(getConfigFile(true)), { alias: 'zeit.co' })
),
},
'local-config-cloud-v1': {
'.gitignore': '*.html',
'index.js': `
const { createServer } = require('http');
const { readFileSync } = require('fs');
const svr = createServer((req, res) => {
const { url = '/' } = req;
const file = '.' + url;
console.log('reading file ' + file);
try {
let contents = readFileSync(file, 'utf8');
res.end(contents || '');
} catch (e) {
res.statusCode = 404;
res.end('Not found');
}
});
svr.listen(3000);`,
'main.html': '<h1>hello main</h1>',
'test.html': '<h1>hello test</h1>',
'folder/file1.txt': 'file1',
'folder/sub/file2.txt': 'file2',
Dockerfile: `FROM mhart/alpine-node:latest
LABEL name "now-cli-dockerfile-${session}"
RUN mkdir /app
WORKDIR /app
COPY . /app
RUN yarn
EXPOSE 3000
CMD ["node", "index.js"]`,
'now.json': JSON.stringify({
version: 1,
type: 'docker',
features: {
cloud: 'v1',
},
files: ['.gitignore', 'folder', 'index.js', 'main.html'],
}),
'now-test.json': JSON.stringify({
version: 1,
type: 'docker',
features: {
cloud: 'v1',
},
files: ['.gitignore', 'folder', 'index.js', 'test.html'],
}),
},
'local-config-v2': {
[`main-${session}.html`]: '<h1>hello main</h1>',
[`test-${session}.html`]: '<h1>hello test</h1>',
'now.json': JSON.stringify({
version: 2,
builds: [{ src: `main-${session}.html`, use: '@now/static' }],
routes: [{ src: '/another-main', dest: `/main-${session}.html` }],
}),
'now-test.json': JSON.stringify({
version: 2,
builds: [{ src: `test-${session}.html`, use: '@now/static' }],
routes: [{ src: '/another-test', dest: `/test-${session}.html` }],
}),
},
'alias-rules': {
'rules.json': JSON.stringify({
rules: [

View File

@@ -201,6 +201,77 @@ test('login', async t => {
t.is(typeof token, 'string');
});
test('deploy using --local-config flag v2', async t => {
const target = fixture('local-config-v2');
const { stdout, stderr, code } = await execa(
binaryPath,
['deploy', '--local-config', 'now-test.json', ...defaultArgs],
{
cwd: target,
reject: false,
}
);
console.log(stderr);
console.log(stdout);
console.log(code);
t.is(code, 0);
const { host } = new URL(stdout);
const testRes = await fetch(`https://${host}/test-${contextName}.html`);
const testText = await testRes.text();
t.is(testText, '<h1>hello test</h1>');
const anotherTestRes = await fetch(`https://${host}/another-test`);
const anotherTestText = await anotherTestRes.text();
t.is(anotherTestText, testText);
const mainRes = await fetch(`https://${host}/main-${contextName}.html`);
t.is(mainRes.status, 404, 'Should not deploy/build main now.json');
const anotherMainRes = await fetch(`https://${host}/another-main`);
t.is(anotherMainRes.status, 404, 'Should not deploy/build main now.json');
});
test('deploy using --local-config flag type cloud v1', async t => {
const target = fixture('local-config-cloud-v1');
const { stdout, stderr, code } = await execa(
binaryPath,
['deploy', '--public', '--local-config', 'now-test.json', ...defaultArgs],
{
cwd: target,
reject: false,
}
);
console.log(stderr);
console.log(stdout);
console.log(code);
t.is(code, 0);
const { host } = new URL(stdout);
const testRes = await fetch(`https://${host}/test.html`);
const testText = await testRes.text();
t.is(testText, '<h1>hello test</h1>');
const file1Res = await fetch(`https://${host}/folder/file1.txt`);
const file1Text = await file1Res.text();
t.is(file1Text, 'file1');
const file2Res = await fetch(`https://${host}/folder/sub/file2.txt`);
const file2Text = await file2Res.text();
t.is(file2Text, 'file2');
const mainRes = await fetch(`https://${host}/main.html`);
t.is(mainRes.status, 404, 'Should not deploy/build main now.json');
});
test('print the deploy help message', async t => {
const { stderr, stdout, code } = await execa(
binaryPath,
@@ -695,7 +766,10 @@ test('ignore files specified in .nowignore', async t => {
const directory = fixture('nowignore');
const args = ['--debug', '--public', '--name', session, ...defaultArgs];
const targetCall = await execa(binaryPath, args, { cwd: directory, reject: false });
const targetCall = await execa(binaryPath, args, {
cwd: directory,
reject: false,
});
console.log(targetCall.stderr);
console.log(targetCall.stdout);
@@ -713,7 +787,10 @@ test('ignore files specified in .nowignore via allowlist', async t => {
const directory = fixture('nowignore-allowlist');
const args = ['--debug', '--public', '--name', session, ...defaultArgs];
const targetCall = await execa(binaryPath, args, { cwd: directory, reject: false });
const targetCall = await execa(binaryPath, args, {
cwd: directory,
reject: false,
});
console.log(targetCall.stderr);
console.log(targetCall.stdout);

View File

@@ -1,6 +1,6 @@
{
"name": "now-client",
"version": "5.1.4",
"version": "5.2.0",
"main": "dist/src/index.js",
"typings": "dist/src/index.d.ts",
"license": "MIT",

View File

@@ -4,9 +4,13 @@ import readdir from 'recursive-readdir';
import { relative, join } from 'path';
import hashes, { mapToObject } from './utils/hashes';
import uploadAndDeploy from './upload';
import { getNowIgnore, createDebug } from './utils';
import { getNowIgnore, createDebug, parseNowJSON } from './utils';
import { DeploymentError } from './errors';
import { CreateDeploymentFunction, DeploymentOptions } from './types';
import {
CreateDeploymentFunction,
DeploymentOptions,
NowJsonOptions,
} from './types';
export { EVENTS } from './utils';
@@ -15,9 +19,11 @@ export default function buildCreateDeployment(
): CreateDeploymentFunction {
return async function* createDeployment(
path: string | string[],
options: DeploymentOptions = {}
options: DeploymentOptions = {},
nowConfig?: NowJsonOptions
): AsyncIterableIterator<any> {
const debug = createDebug(options.debug);
const cwd = process.cwd();
debug('Creating deployment...');
@@ -89,6 +95,47 @@ export default function buildCreateDeployment(
debug(`Deploying the provided path as single file`);
}
if (!nowConfig) {
// If the user did not provide a nowConfig,
// then use the now.json file in the root.
const fileName = 'now.json';
const absolutePath = fileList.find(f => relative(cwd, f) === fileName);
debug(absolutePath ? `Found ${fileName}` : `Missing ${fileName}`);
nowConfig = await parseNowJSON(absolutePath);
}
if (
version === 1 &&
nowConfig &&
Array.isArray(nowConfig.files) &&
nowConfig.files.length > 0
) {
// See the docs: https://zeit.co/docs/v1/features/configuration/#files-(array)
debug('Filtering file list based on `files` key in now.json');
const allowedFiles = new Set<string>(['Dockerfile']);
const allowedDirs = new Set<string>();
nowConfig.files.forEach(relPath => {
if (lstatSync(relPath).isDirectory()) {
allowedDirs.add(relPath);
} else {
allowedFiles.add(relPath);
}
});
fileList = fileList.filter(absPath => {
const relPath = relative(cwd, absPath);
if (allowedFiles.has(relPath)) {
return true;
}
for (let dir of allowedDirs) {
if (relPath.startsWith(dir + '/')) {
return true;
}
}
return false;
});
debug(`Found ${fileList.length} files: ${JSON.stringify(fileList)}`);
}
// This is a useful warning because it prevents people
// from getting confused about a deployment that renders 404.
if (
@@ -133,6 +180,7 @@ export default function buildCreateDeployment(
const deploymentOpts = {
debug: debug_,
totalFiles: files.size,
nowConfig,
token,
isDirectory,
path,

View File

@@ -1,6 +1,5 @@
import { DeploymentFile } from './utils/hashes';
import {
parseNowJSON,
fetch,
API_DEPLOYMENTS,
prepareFiles,
@@ -22,6 +21,7 @@ export interface Options {
defaultName?: string;
preflight?: boolean;
debug?: boolean;
nowConfig?: NowJsonOptions;
}
async function* createDeployment(
@@ -73,6 +73,10 @@ async function* createDeployment(
debug('Deployment created with a warning:', value);
yield { type: 'warning', payload: value };
}
if (name.startsWith('x-now-notice-')) {
debug('Deployment created with a notice:', value);
yield { type: 'notice', payload: value };
}
}
yield { type: 'created', payload: json };
@@ -108,32 +112,12 @@ export default async function* deploy(
options: Options
): AsyncIterableIterator<{ type: string; payload: any }> {
const debug = createDebug(options.debug);
delete options.debug;
debug(`Trying to read 'now.json'`);
const nowJson: DeploymentFile | undefined = Array.from(files.values()).find(
(file: DeploymentFile): boolean => {
return Boolean(
file.names.find((name: string): boolean => name.includes('now.json'))
);
}
);
debug(`'now.json' ${nowJson ? 'found' : "doesn't exist"}`);
const nowJsonMetadata: NowJsonOptions = parseNowJSON(nowJson);
const nowJsonMetadata = options.nowConfig || {};
delete nowJsonMetadata.github;
delete nowJsonMetadata.scope;
const meta = options.metadata || {};
const metadata = { ...nowJsonMetadata, ...meta };
if (nowJson) {
debug(
`Merged 'now.json' metadata and locally provided metadata:`,
JSON.stringify(metadata)
);
}
// Check if we should default to a static deployment
if (!metadata.version && !metadata.name) {

View File

@@ -124,9 +124,11 @@ export interface NowJsonOptions {
scope?: string;
type?: 'NPM' | 'STATIC' | 'DOCKER';
version?: number;
files?: string[];
}
export type CreateDeploymentFunction = (
path: string | string[],
options?: DeploymentOptions
options?: DeploymentOptions,
nowConfig?: NowJsonOptions
) => AsyncIterableIterator<any>;

View File

@@ -26,7 +26,7 @@ const isClientNetworkError = (err: Error | DeploymentError) => {
export default async function* upload(
files: Map<string, DeploymentFile>,
options: Options
options: Options,
): AsyncIterableIterator<any> {
const { token, teamId, debug: isDebug } = options;
const debug = createDebug(isDebug);

View File

@@ -32,13 +32,13 @@ export const EVENTS = new Set([
'build-state-changed',
]);
export function parseNowJSON(file?: DeploymentFile): NowJsonOptions {
if (!file) {
export async function parseNowJSON(filePath?: string): Promise<NowJsonOptions> {
if (!filePath) {
return {};
}
try {
const jsonString = file.data.toString();
const jsonString = await readFile(filePath, 'utf8');
return JSON.parse(jsonString);
} catch (e) {

View File

@@ -1,6 +1,6 @@
{
"name": "@now/next",
"version": "1.0.2",
"version": "1.0.4",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/v2/deployments/official-builders/next-js-now-next",

View File

@@ -37,12 +37,10 @@ import {
EnvConfig,
excludeFiles,
ExperimentalTraceVersion,
filesFromDirectory,
getDynamicRoutes,
getNextConfig,
getPathsInside,
getRoutes,
includeOnlyEntryDirectory,
isDynamicRoute,
normalizePackageJson,
normalizePage,
@@ -69,6 +67,20 @@ interface BuildParamsType extends BuildOptions {
export const version = 2;
const nowDevChildProcesses = new Set<ChildProcess>();
['SIGINT', 'SIGTERM'].forEach(signal => {
process.once(signal as NodeJS.Signals, () => {
for (const child of nowDevChildProcesses) {
debug(
`Got ${signal}, killing dev server child process (pid=${child.pid})`
);
process.kill(child.pid, signal);
}
process.exit(0);
});
});
/**
* Read package.json from files
*/
@@ -214,6 +226,7 @@ export const build = async ({
const { forked, getUrl } = startDevServer(entryPath, runtimeEnv);
urls[entrypoint] = await getUrl();
childProcess = forked;
nowDevChildProcesses.add(forked);
debug(
`${name} Development server for ${entrypoint} running at ${urls[entrypoint]}`
);
@@ -337,7 +350,7 @@ export const build = async ({
if (isLegacy) {
const filesAfterBuild = await glob('**', entryPath);
debug('Preparing lambda files...');
debug('Preparing serverless function files...');
let buildId: string;
try {
buildId = await readFile(
@@ -405,7 +418,7 @@ export const build = async ({
],
};
debug(`Creating lambda for page: "${page}"...`);
debug(`Creating serverless function for page: "${page}"...`);
lambdas[path.join(entryDirectory, pathname)] = await createLambda({
files: {
...nextFiles,
@@ -415,11 +428,11 @@ export const build = async ({
handler: 'now__launcher.launcher',
runtime: nodeVersion.runtime,
});
debug(`Created lambda for page: "${page}"`);
debug(`Created serverless function for page: "${page}"`);
})
);
} else {
debug('Preparing lambda files...');
debug('Preparing serverless function files...');
const pagesDir = path.join(entryPath, '.next', 'serverless', 'pages');
const pages = await glob('**/*.js', pagesDir);
@@ -496,7 +509,8 @@ export const build = async ({
} = {};
if (requiresTracing) {
const tracingLabel = 'Tracing Next.js lambdas for external files ...';
const tracingLabel =
'Tracing Next.js serverless functions for external files ...';
console.time(tracingLabel);
const apiPages: string[] = [];
@@ -542,7 +556,7 @@ export const build = async ({
apiFileList.forEach(collectTracedFiles(apiReasons, apiTracedFiles));
console.timeEnd(tracingLabel);
const zippingLabel = 'Compressing shared lambda files';
const zippingLabel = 'Compressing shared serverless function files';
console.time(zippingLabel);
pseudoLayers.push(await createPseudoLayer(tracedFiles));
@@ -560,7 +574,9 @@ export const build = async ({
const assetKeys = Object.keys(assets);
if (assetKeys.length > 0) {
debug('detected (legacy) assets to be bundled with lambda:');
debug(
'detected (legacy) assets to be bundled with serverless function:'
);
assetKeys.forEach(assetFile => debug(`\t${assetFile}`));
debug(
'\nPlease upgrade to Next.js 9.1 to leverage modern asset handling.'
@@ -570,7 +586,7 @@ export const build = async ({
const launcherPath = path.join(__dirname, 'templated-launcher.js');
const launcherData = await readFile(launcherPath, 'utf8');
const allLambdasLabel = `All lambdas created`;
const allLambdasLabel = `All serverless functions created (in parallel)`;
console.time(allLambdasLabel);
await Promise.all(
@@ -586,7 +602,7 @@ export const build = async ({
dynamicPages.push(normalizePage(pathname));
}
const label = `Creating lambda for page: "${page}"...`;
const label = `Created serverless function for "${page}" in`;
console.time(label);
const pageFileName = path.normalize(
@@ -637,6 +653,9 @@ export const build = async ({
'**',
path.join(entryPath, '.next', 'static')
);
const staticFolderFiles = await glob('**', path.join(entryPath, 'static'));
const publicFolderFiles = await glob('**', path.join(entryPath, 'public'));
const staticFiles = Object.keys(nextStaticFiles).reduce(
(mappedFiles, file) => ({
...mappedFiles,
@@ -646,23 +665,24 @@ export const build = async ({
}),
{}
);
const entryDirectoryFiles = includeOnlyEntryDirectory(files, entryDirectory);
const staticDirectoryFiles = filesFromDirectory(
entryDirectoryFiles,
path.join(entryDirectory, 'static')
);
const publicDirectoryFiles = filesFromDirectory(
entryDirectoryFiles,
path.join(entryDirectory, 'public')
);
const publicFiles = Object.keys(publicDirectoryFiles).reduce(
const staticDirectoryFiles = Object.keys(staticFolderFiles).reduce(
(mappedFiles, file) => ({
...mappedFiles,
[file.replace(/public[/\\]+/, '')]: publicDirectoryFiles[file],
[path.join(entryDirectory, 'static', file)]: staticFolderFiles[file],
}),
{}
);
const publicDirectoryFiles = Object.keys(publicFolderFiles).reduce(
(mappedFiles, file) => ({
...mappedFiles,
[path.join(
entryDirectory,
file.replace(/public[/\\]+/, '')
)]: publicFolderFiles[file],
}),
{}
);
let dynamicPrefix = path.join('/', entryDirectory);
dynamicPrefix = dynamicPrefix === '/' ? '' : dynamicPrefix;
@@ -682,7 +702,7 @@ export const build = async ({
return {
output: {
...publicFiles,
...publicDirectoryFiles,
...lambdas,
...staticPages,
...staticFiles,

View File

@@ -60,24 +60,6 @@ function excludeFiles(
}, {});
}
/**
* Creates a new Files object holding only the entrypoint files
*/
function includeOnlyEntryDirectory(
files: Files,
entryDirectory: string
): Files {
if (entryDirectory === '.') {
return files;
}
function matcher(filePath: string) {
return !filePath.startsWith(entryDirectory);
}
return excludeFiles(files, matcher);
}
/**
* Exclude package manager lockfiles from files
*/
@@ -92,17 +74,6 @@ function excludeLockFiles(files: Files): Files {
return files;
}
/**
* Include only the files from a selected directory
*/
function filesFromDirectory(files: Files, dir: string): Files {
function matcher(filePath: string) {
return !filePath.startsWith(dir.replace(/\\/g, '/'));
}
return excludeFiles(files, matcher);
}
/**
* Enforce specific package.json configuration for smallest possible lambda
*/
@@ -205,17 +176,35 @@ function getRoutes(
files: Files,
url: string
): Route[] {
let pagesDir = '';
const filesInside: Files = {};
const prefix = entryDirectory === `.` ? `/` : `/${entryDirectory}/`;
const fileKeys = Object.keys(files);
for (const file of Object.keys(files)) {
for (const file of fileKeys) {
if (!pathsInside.includes(file)) {
continue;
}
if (!pagesDir) {
if (file.startsWith(path.join(entryDirectory, 'pages'))) {
pagesDir = 'pages';
}
}
filesInside[file] = files[file];
}
// If default pages dir isn't found check for `src/pages`
if (
!pagesDir &&
fileKeys.some(file =>
file.startsWith(path.join(entryDirectory, 'src/pages'))
)
) {
pagesDir = 'src/pages';
}
const routes: Route[] = [
{
src: `${prefix}_next/(.*)`,
@@ -231,13 +220,13 @@ function getRoutes(
for (const file of filePaths) {
const relativePath = path.relative(entryDirectory, file);
const isPage = pathIsInside('pages', relativePath);
const isPage = pathIsInside(pagesDir, relativePath);
if (!isPage) {
continue;
}
const relativeToPages = path.relative('pages', relativePath);
const relativeToPages = path.relative(pagesDir, relativePath);
const extension = path.extname(relativeToPages);
const pageName = relativeToPages.replace(extension, '').replace(/\\/g, '/');
@@ -484,10 +473,8 @@ export async function createLambdaFromPseudoLayers({
export {
excludeFiles,
validateEntrypoint,
includeOnlyEntryDirectory,
excludeLockFiles,
normalizePackageJson,
filesFromDirectory,
getNextConfig,
getPathsInside,
getRoutes,

View File

@@ -122,6 +122,7 @@ it(
buildResult: { output },
} = await runBuildLambda(path.join(__dirname, 'public-files'));
expect(output['robots.txt']).toBeDefined();
expect(output['generated.txt']).toBeDefined();
},
FOUR_MINUTES
);

View File

@@ -0,0 +1,4 @@
const fs = require('fs');
// Adds a new file to the public folder at build time
fs.writeFileSync('public/generated.txt', 'Generated');

View File

@@ -1,9 +1,9 @@
{
"scripts": {
"now-build": "next build"
"now-build": "node create-public-file.js && next build"
},
"dependencies": {
"next": "8",
"next": "9",
"react": "16",
"react-dom": "16"
}

View File

@@ -2,9 +2,8 @@ const path = require('path');
const {
excludeFiles,
validateEntrypoint,
includeOnlyEntryDirectory,
normalizePackageJson,
getNextConfig
getNextConfig,
} = require('@now/next/dist/utils');
const { FileRef } = require('@now/build-utils');
@@ -33,7 +32,7 @@ describe('excludeFiles', () => {
const files = {
'pages/index.js': new FileRef({ digest: 'index' }),
'package.json': new FileRef({ digest: 'package' }),
'package-lock.json': new FileRef({ digest: 'package-lock' })
'package-lock.json': new FileRef({ digest: 'package-lock' }),
};
const result = excludeFiles(
files,
@@ -63,21 +62,6 @@ describe('validateEntrypoint', () => {
});
});
describe('includeOnlyEntryDirectory', () => {
it('should include files outside entry directory', () => {
const entryDirectory = 'frontend';
const files = {
'frontend/pages/index.js': new FileRef({ digest: 'index' }),
'package.json': new FileRef({ digest: 'package' }),
'package-lock.json': new FileRef({ digest: 'package-lock' })
};
const result = includeOnlyEntryDirectory(files, entryDirectory);
expect(result['frontend/pages/index.js']).toBeDefined();
expect(result['package.json']).toBeUndefined();
expect(result['package-lock.json']).toBeUndefined();
});
});
describe('normalizePackageJson', () => {
it('should work without a package.json being supplied', () => {
const result = normalizePackageJson();
@@ -85,15 +69,15 @@ describe('normalizePackageJson', () => {
dependencies: {
'next-server': 'v7.0.2-canary.49',
react: 'latest',
'react-dom': 'latest'
'react-dom': 'latest',
},
devDependencies: {
next: 'v7.0.2-canary.49'
next: 'v7.0.2-canary.49',
},
scripts: {
'now-build':
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas'
}
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas',
},
});
});
@@ -102,29 +86,29 @@ describe('normalizePackageJson', () => {
dependencies: {
'next-server': 'v7.0.2-canary.49',
react: 'latest',
'react-dom': 'latest'
'react-dom': 'latest',
},
devDependencies: {
next: 'v7.0.2-canary.49'
next: 'v7.0.2-canary.49',
},
scripts: {
'now-build': 'next build'
}
'now-build': 'next build',
},
};
const result = normalizePackageJson(defaultPackage);
expect(result).toEqual({
dependencies: {
'next-server': 'v7.0.2-canary.49',
react: 'latest',
'react-dom': 'latest'
'react-dom': 'latest',
},
devDependencies: {
next: 'v7.0.2-canary.49'
next: 'v7.0.2-canary.49',
},
scripts: {
'now-build':
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas'
}
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas',
},
});
});
@@ -133,23 +117,23 @@ describe('normalizePackageJson', () => {
dependencies: {
react: 'latest',
'react-dom': 'latest',
next: 'latest'
}
next: 'latest',
},
};
const result = normalizePackageJson(defaultPackage);
expect(result).toEqual({
dependencies: {
'next-server': 'v7.0.2-canary.49',
react: 'latest',
'react-dom': 'latest'
'react-dom': 'latest',
},
devDependencies: {
next: 'v7.0.2-canary.49'
next: 'v7.0.2-canary.49',
},
scripts: {
'now-build':
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas'
}
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas',
},
});
});
@@ -158,23 +142,23 @@ describe('normalizePackageJson', () => {
dependencies: {
react: 'latest',
'react-dom': 'latest',
next: 'latest'
}
next: 'latest',
},
};
const result = normalizePackageJson(defaultPackage);
expect(result).toEqual({
dependencies: {
'next-server': 'v7.0.2-canary.49',
react: 'latest',
'react-dom': 'latest'
'react-dom': 'latest',
},
devDependencies: {
next: 'v7.0.2-canary.49'
next: 'v7.0.2-canary.49',
},
scripts: {
'now-build':
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas'
}
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas',
},
});
});
@@ -183,23 +167,23 @@ describe('normalizePackageJson', () => {
dependencies: {
react: 'latest',
'react-dom': 'latest',
next: 'latest'
}
next: 'latest',
},
};
const result = normalizePackageJson(defaultPackage);
expect(result).toEqual({
dependencies: {
'next-server': 'v7.0.2-canary.49',
react: 'latest',
'react-dom': 'latest'
'react-dom': 'latest',
},
devDependencies: {
next: 'v7.0.2-canary.49'
next: 'v7.0.2-canary.49',
},
scripts: {
'now-build':
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas'
}
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas',
},
});
});
@@ -211,7 +195,7 @@ describe('normalizePackageJson', () => {
dev: 'next',
build: 'next build',
start: 'next start',
test: "xo && stylelint './pages/**/*.js' && jest"
test: "xo && stylelint './pages/**/*.js' && jest",
},
main: 'pages/index.js',
license: 'MIT',
@@ -226,7 +210,7 @@ describe('normalizePackageJson', () => {
'stylelint-config-recommended': '^2.1.0',
'stylelint-config-styled-components': '^0.1.1',
'stylelint-processor-styled-components': '^1.5.1',
xo: '^0.23.0'
xo: '^0.23.0',
},
dependencies: {
consola: '^2.2.6',
@@ -234,7 +218,7 @@ describe('normalizePackageJson', () => {
next: '^7.0.2',
react: '^16.6.3',
'react-dom': '^16.6.3',
'styled-components': '^4.1.1'
'styled-components': '^4.1.1',
},
xo: {
extends: 'xo-react',
@@ -244,15 +228,15 @@ describe('normalizePackageJson', () => {
'test',
'pages/_document.js',
'pages/index.js',
'pages/home.js'
'pages/home.js',
],
rules: {
'react/no-unescaped-entities': null
}
'react/no-unescaped-entities': null,
},
},
jest: {
testEnvironment: 'node'
}
testEnvironment: 'node',
},
};
const result = normalizePackageJson(defaultPackage);
expect(result).toEqual({
@@ -263,7 +247,7 @@ describe('normalizePackageJson', () => {
'now-build':
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas',
start: 'next start',
test: "xo && stylelint './pages/**/*.js' && jest"
test: "xo && stylelint './pages/**/*.js' && jest",
},
main: 'pages/index.js',
license: 'MIT',
@@ -283,12 +267,12 @@ describe('normalizePackageJson', () => {
xo: '^0.23.0',
consola: '^2.2.6',
fontfaceobserver: '^2.0.13',
'styled-components': '^4.1.1'
'styled-components': '^4.1.1',
},
dependencies: {
'next-server': 'v7.0.2-canary.49',
react: '^16.6.3',
'react-dom': '^16.6.3'
'react-dom': '^16.6.3',
},
xo: {
extends: 'xo-react',
@@ -298,15 +282,15 @@ describe('normalizePackageJson', () => {
'test',
'pages/_document.js',
'pages/index.js',
'pages/home.js'
'pages/home.js',
],
rules: {
'react/no-unescaped-entities': null
}
'react/no-unescaped-entities': null,
},
},
jest: {
testEnvironment: 'node'
}
testEnvironment: 'node',
},
});
});
});

View File

@@ -52,8 +52,9 @@ if 'handler' in __now_variables or 'Handler' in __now_variables:
):
body = base64.b64decode(body)
request_body = body.encode('utf-8') if isinstance(body, str) else body
conn = http.client.HTTPConnection('0.0.0.0', port)
conn.request(method, path, headers=headers, body=body)
conn.request(method, path, headers=headers, body=request_body)
res = conn.getresponse()
data = res.read().decode('utf-8')

View File

@@ -1,6 +1,6 @@
{
"name": "@now/python",
"version": "0.3.0",
"version": "0.3.1",
"main": "./dist/index.js",
"license": "MIT",
"homepage": "https://zeit.co/docs/v2/deployments/official-builders/python-now-python",

View File

@@ -0,0 +1,20 @@
from http.server import BaseHTTPRequestHandler
import json
class handler(BaseHTTPRequestHandler):
def do_POST(self):
post_body = json.loads(self.rfile.read(int(self.headers['content-length'])).decode('utf-8'))
name = post_body.get('name', 'someone')
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
response_data = {'greeting': f'hello, {name}'}
self.wfile.write(json.dumps(response_data).encode('utf-8'))
return
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write('ok'.encode('utf-8'))
return

View File

@@ -0,0 +1,19 @@
{
"version": 2,
"builds": [
{
"src": "*.py",
"use": "@now/python"
}
],
"probes": [
{
"path": "/",
"method": "POST",
"body": {
"name": "Χριστοφορε"
},
"status": 200
}
]
}

View File

@@ -0,0 +1,19 @@
module.exports = async function({ deploymentUrl, fetch, randomness }) {
const nowjson = require('./now.json');
const probe = nowjson.probes[0];
const probeUrl = `https://${deploymentUrl}${probe.path}`;
const resp = await fetch(probeUrl, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(probe.body),
});
const text = await resp.text();
const respBody = JSON.parse(text);
if (respBody.greeting !== 'hello, Χριστοφορε') {
throw new Error(`unexpected response: ${respBody}`);
}
};

View File

@@ -1,6 +1,6 @@
{
"name": "@now/static-build",
"version": "0.10.0",
"version": "0.10.1",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/v2/deployments/official-builders/static-build-now-static-build",
@@ -19,11 +19,12 @@
},
"devDependencies": {
"@types/cross-spawn": "6.0.0",
"@types/ms": "0.7.31",
"@types/promise-timeout": "1.3.0",
"cross-spawn": "6.0.5",
"get-port": "5.0.0",
"is-port-reachable": "2.0.1",
"promise-timeout": "1.3.0",
"ms": "2.1.2",
"typescript": "3.5.2"
}
}

View File

@@ -1,7 +1,8 @@
import ms from 'ms';
import path from 'path';
import spawn from 'cross-spawn';
import getPort from 'get-port';
import { timeout } from 'promise-timeout';
import { SpawnOptions } from 'child_process';
import isPortReachable from 'is-port-reachable';
import { existsSync, readFileSync, statSync, readdirSync } from 'fs';
import { frameworks, Framework } from './frameworks';
@@ -25,11 +26,20 @@ import {
PrepareCacheOptions,
} from '@now/build-utils';
async function checkForPort(port: number | undefined): Promise<void> {
const sleep = (n: number) => new Promise(resolve => setTimeout(resolve, n));
const DEV_SERVER_PORT_BIND_TIMEOUT = ms('5m');
async function checkForPort(
port: number | undefined,
timeout: number
): Promise<void> {
const start = Date.now();
while (!(await isPortReachable(port))) {
await new Promise(resolve => {
setTimeout(resolve, 100);
});
if (Date.now() - start > timeout) {
throw new Error(`Detecting port ${port} timed out after ${ms(timeout)}`);
}
await sleep(100);
}
}
@@ -256,32 +266,20 @@ export async function build({
devPort = await getPort();
nowDevScriptPorts.set(entrypoint, devPort);
const opts = {
const opts: SpawnOptions = {
cwd: entrypointDir,
stdio: 'inherit',
env: { ...process.env, PORT: String(devPort) },
};
const child = spawn('yarn', ['run', devScript], opts);
child.on('exit', () => nowDevScriptPorts.delete(entrypoint));
if (child.stdout) {
child.stdout.setEncoding('utf8');
child.stdout.pipe(process.stdout);
}
if (child.stderr) {
child.stderr.setEncoding('utf8');
child.stderr.pipe(process.stderr);
}
// Now wait for the server to have listened on `$PORT`, after which we
// will ProxyPass any requests to that development server that come in
// for this builder.
try {
await timeout(
new Promise(resolve => {
checkForPort(devPort).then(resolve);
}),
5 * 60 * 1000
);
await checkForPort(devPort, DEV_SERVER_PORT_BIND_TIMEOUT);
} catch (err) {
throw new Error(
`Failed to detect a server running on port ${devPort}.\nDetails: https://err.sh/zeit/now/now-static-build-failed-to-detect-a-server`

View File

@@ -1,7 +1,10 @@
declare module 'is-port-reachable' {
export interface IsPortReachableOptions {
timeout?: number | undefined;
host?: string;
}
export default function(port: number | undefined, options?: IsPortReachableOptions): Promise<boolean>;
}
export interface IsPortReachableOptions {
timeout?: number | undefined;
host?: string;
}
export default function(
port: number | undefined,
options?: IsPortReachableOptions
): Promise<boolean>;
}

View File

@@ -1671,6 +1671,11 @@
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.30.tgz#f6c38b7ecbbf698b0bbd138315a0f0f18954f85f"
integrity sha512-OftRLCgAzJP7vmKn9by/GVjnf4hloz/pXNOwPo0vKGAfXI7GqWXJi9N2kRar4cP5s1dGwuwcagWqO6iHBTq1Mg==
"@types/ms@0.7.31":
version "0.7.31"
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197"
integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==
"@types/multistream@2.1.1":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@types/multistream/-/multistream-2.1.1.tgz#4badd2440ee3570594ea552420fe2e29ebe512bd"
@@ -8793,11 +8798,6 @@ promise-retry@^1.1.1:
err-code "^1.0.0"
retry "^0.10.0"
promise-timeout@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/promise-timeout/-/promise-timeout-1.3.0.tgz#d1c78dd50a607d5f0a5207410252a3a0914e1014"
integrity sha512-5yANTE0tmi5++POym6OgtFmwfDvOXABD9oj/jLQr5GPEyuNEb7jH4wbbANJceJid49jwhi1RddxnhnEAb/doqg==
promisepipe@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/promisepipe/-/promisepipe-3.0.0.tgz#c9b6e5aa861ef5fcce6134f6f75e14f8f30bd3b2"