Compare commits

..

48 Commits

Author SHA1 Message Date
Nathan Rajlich
0b89d30d6c Publish
- @now/build-utils@0.5.5
 - @now/cgi@0.1.4
 - @now/go@0.4.6
 - @now/mdx-deck@0.5.4
 - @now/next@0.3.2
 - @now/node-server@0.7.2
 - @now/node@0.7.2
 - @now/php@0.5.4
 - @now/python@0.2.2
 - @now/rust@0.2.4
2019-05-11 08:13:57 -07:00
Tim Neutkens
8a021c9417 [now-next] Add support for /api routes (#494) 2019-05-11 16:18:23 +02:00
Nathan Rajlich
f218771382 Publish
- @now/cgi@0.1.4-canary.0
 - @now/go@0.4.6-canary.0
 - @now/mdx-deck@0.5.4-canary.0
 - @now/node-server@0.7.2-canary.1
 - @now/node@0.7.2-canary.2
 - @now/php@0.5.4-canary.0
 - @now/python@0.2.2-canary.0
 - @now/rust@0.2.4-canary.1
2019-05-10 19:19:15 -07:00
Nathan Rajlich
17309291ed [now-node-server] Pass meta to download() function (#489) 2019-05-10 19:18:31 -07:00
Nathan Rajlich
86300577ae [now-rust] Pass meta to download() function 2019-05-10 17:14:22 -07:00
Nathan Rajlich
f9594e0d61 [now-python] Pass meta to download() function (#491) 2019-05-11 01:13:48 +02:00
Nathan Rajlich
20fd4b2e12 [now-php] Download files to workPath and pass meta (#490)
It's not clear to me why we were installing to `userfiles` directory,
so let me know if this breaks something.
2019-05-11 01:13:14 +02:00
Nathan Rajlich
718e4d0e0c [now-mdx-deck] Pass meta to download() function (#488) 2019-05-11 01:12:46 +02:00
Nathan Rajlich
dc3584cd08 [now-cgi] Download files to workPath and pass meta (#487) 2019-05-11 01:12:12 +02:00
Nathan Rajlich
b41788b241 Update yarn.lock 2019-05-10 13:17:05 -07:00
Nathan Rajlich
af9a2f9792 [now-node-server] Update @zeit/ncc to v0.18.5 2019-05-10 13:17:05 -07:00
Nathan Rajlich
f8b8e760de [now-node] Update @zeit/ncc to v0.18.5 2019-05-10 13:17:05 -07:00
Sophearak Tha
93d6ec8024 [now-go] Only use valid exported function with net/http interface (#477)
* Only use valid exported function with `net/http` interface

* Improve log, show link to docs if we coudn't parsed the entrypoint
2019-05-10 21:13:44 +07:00
Nathan Rajlich
7ed6b84056 Remove packages/now-rust/now-rust-0.2.3.tgz (#480)
It appears to have been accidentally committed in
bd2d05344e.
2019-05-08 14:50:57 -07:00
Nathan Rajlich
31da488365 Publish
- @now/build-utils@0.5.5-canary.1
 - @now/next@0.3.2-canary.1
 - @now/node@0.7.2-canary.1
2019-05-08 12:22:17 -07:00
Nathan Rajlich
8eaf05f782 Add initial CODEOWNERS file (#479)
* Add initial `CODEOWNERS` file

* Add `@now/go` to `CODEOWNERS` file
2019-05-08 12:07:32 -07:00
Nathan Rajlich
9311e90f27 [now-next] Fix failing unit test when isDev: true (#478) 2019-05-08 12:00:35 -07:00
Steven
c0de970de2 Add coverage to .gitignore (#473)
* Add coverage to `.gitignore`

* Add .tgz to `.gitignore`

* *.tgz
2019-05-07 17:57:09 -07:00
Nathan Rajlich
465ac2093d [now-node] Pass in the meta object to download() (#475)
Depends on https://github.com/zeit/now-builders/pull/474.
2019-05-07 17:56:39 -07:00
Nathan Rajlich
19ab0e8698 [now-build-utils] Make download() a no-op in now dev (#474)
This will be necessary for the update in `now dev` to have the builder
`workPath` be equal to the `cwd` source directory of the dev server.

Otherwise, unnecessary file modifications are made (copying the source
file to itself) and file corruption often occurs.
2019-05-07 17:56:16 -07:00
Steven
02fa98e5e3 Publish
- @now/build-utils@0.5.5-canary.0
 - @now/next@0.3.2-canary.0
 - @now/node-server@0.7.2-canary.0
 - @now/node@0.7.2-canary.0
 - @now/rust@0.2.4-canary.0
2019-05-07 17:08:55 -04:00
Steven
4aef9d48b0 [now-node] Bump ncc to 0.18.3 (#472) 2019-05-07 15:40:14 -04:00
Luis Fernando Alvarez D
bd2d05344e [now-next] Add public files to the output (#468) 2019-05-07 14:06:05 -05:00
Steven
edc7696623 Add newline to .gitignore 2019-05-07 09:28:59 -04:00
Steven
e2f91094bc Publish
- @now/bash@0.2.3
 - @now/build-utils@0.5.4
 - @now/cgi@0.1.3
 - @now/go@0.4.5
 - @now/html-minifier@1.1.3
 - @now/lambda@0.5.3
 - @now/md@0.5.3
 - @now/mdx-deck@0.5.3
 - @now/next@0.3.1
 - @now/node-bridge@1.1.2
 - @now/node-server@0.7.1
 - @now/node@0.7.1
 - @now/optipng@0.6.1
 - @now/php-bridge@0.5.2
 - @now/php@0.5.3
 - @now/python@0.2.1
 - @now/rust@0.2.3
 - @now/static-build@0.5.5
 - @now/wordpress@0.5.2
2019-05-07 07:09:50 -04:00
Steven
38dba57378 Bump stable version 2019-05-07 07:00:57 -04:00
Nathan Rajlich
be6a6ba1d7 Publish
- @now/build-utils@0.5.2-canary.2
 - @now/next@0.2.1-canary.1
2019-05-06 17:07:12 -07:00
Nathan Rajlich
31fb5d9ec8 [now-next] Sync runtime env vars in dev server after app.prepare() (#467)
* [now-next] Sync runtime env vars in dev server after `app.prepare()`

* Update packages/now-next/src/index.ts

Co-Authored-By: TooTallNate <n@n8.io>

* Update packages/now-next/src/index.ts

Co-Authored-By: TooTallNate <n@n8.io>

* Add `syncEnvVars()` helper function for common logic
2019-05-06 16:43:20 -07:00
Joe Haddad
6c8f946a48 [now-build-utils] Remove mutable option and add sha+ephemeral scheme (#466)
Co-Authored-By: Steven <steven@ceriously.com>

* Add an ephemeral option for files

* Use array destructuring

Co-Authored-By: Timer <joe.haddad@zeit.co>

* Remove mutable option

* Remove code all together

* Introduce `sha+ephemeral` handling

* http => https

* Elaborate more on the cloudfront url

* Update comment a bit more

* Add comment explaining other url
2019-05-06 19:12:39 -04:00
Steven
d59e1b9789 Publish
- @now/build-utils@0.5.2-canary.1
 - @now/go@0.4.3-canary.1
2019-05-06 08:47:35 -04:00
Steven
2852d3fbc3 [now-build-utils] Add yarn --ignore-engines during install (#463) 2019-05-05 22:57:37 -04:00
Sophearak Tha
d0292eb751 Improve go checking in user dev machine (#460) 2019-05-06 09:11:24 +07:00
Steven
17bbf69346 Publish
- @now/node-server@0.6.1-canary.2
 - @now/python@0.1.1-canary.2
2019-05-03 19:48:26 -04:00
Steven
4fb4229c90 Remove python/pip installer (#459)
* Remove python/pip installer

* Add back PYTHONUSERBASE env var
2019-05-03 19:46:47 -04:00
Steven
03b7586b50 [now-node-server] Fix unit tests 2019-05-03 14:09:45 -04:00
Mickaël Allonneau
a1427866ca [now-node-server] 'config.includeFiles' accepts a string (#442)
* 1st try (fails)

* 2nd try (fails)

* 3rd try (fails)

* 4th try (fail)
2019-05-03 13:54:33 -04:00
Nathan Rajlich
5f787b8146 Publish
- @now/bash@0.2.1-canary.0
 - @now/cgi@0.1.1-canary.0
 - @now/html-minifier@1.1.1-canary.0
 - @now/lambda@0.5.1-canary.0
 - @now/md@0.5.1-canary.0
 - @now/node-server@0.6.1-canary.1
 - @now/node@0.6.1-canary.2
 - @now/optipng@0.5.1-canary.0
 - @now/php@0.5.1-canary.0
 - @now/python@0.1.1-canary.1
 - @now/rust@0.2.1-canary.0
2019-05-02 15:21:06 -07:00
Nathan Rajlich
b03405a665 Add default shouldServe() to appropriate builders (#456)
This adds the default `shouldServe()` implementation to the following
builders:

 * `@now/bash`
 * `@now/cgi`
 * `@now/html-minifier`
 * `@now/lambda`
 * `@now/md`
 * `@now/node-server`
 * `@now/node`
 * `@now/optipng`
 * `@now/php`
 * `@now/python`
 * `@now/rust`

The default implementation may be used for these builders because
they map 1-1 with their entrypoint file -> output file/lambda.
2019-05-02 15:09:58 -07:00
Steven
4393dad15a Publish
- @now/node@0.6.1-canary.1
 - @now/static-build@0.5.3-canary.1
2019-05-02 16:07:38 -04:00
Marios Antonoudiou
b4d604b2e9 [now-static-build] Fix @now/static-build nested build (#452) 2019-05-02 15:15:40 -04:00
Mickaël Allonneau
5fb6e5c0ba [now-node] 'config.includeFiles' accepts a string (#441) 2019-05-02 14:26:35 -04:00
Steven
9d7dd3a713 Publish
- @now/python@0.1.1-canary.0
2019-05-02 11:32:59 -04:00
Steven
4f867b320d [now-python] Upgrade build env to python3.6 (#450)
* [now-python] Upgarde build env to python3.6

* Add support for isDev
2019-05-02 11:00:39 -04:00
Nathan Rajlich
c153690104 Publish
- @now/build-utils@0.5.2-canary.0
 - @now/go@0.4.3-canary.0
 - @now/next@0.2.1-canary.0
 - @now/static-build@0.5.3-canary.0
2019-05-01 23:34:18 -07:00
Nathan Rajlich
8c1b96edf7 [now-build-utils] Use cross-spawn for Windows support (#449) 2019-05-01 23:26:20 -07:00
Carlos Alexandro Becker
15c83a69f7 [now-go] Add support for config.includeFiles (#447) 2019-05-01 17:39:53 -04:00
Steven
0986de85ee [now-static-build] Warn when now-dev script is missing (#443)
* Warn when now-dev script is missing

* Add link to local dev docs

* Update packages/now-static-build/index.js

Co-Authored-By: styfle <steven@ceriously.com>

* Fix url
2019-05-01 17:32:50 -04:00
Leo Lamprecht
94c5d83ccc Copy the entire process environment to child (#448)
* Copy the entire process environment to child

* Removed lockfile

* Removed useless code

* Add comment
2019-05-01 22:26:35 +02:00
69 changed files with 700 additions and 304 deletions

7
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,7 @@
# Documentation
# https://help.github.com/en/articles/about-code-owners
* @styfle
/packages/now-node @styfle @tootallnate
/packages/now-next @styfle @dav-is
/packages/now-go @styfle @sophearak

4
.gitignore vendored
View File

@@ -1,4 +1,6 @@
node_modules
tmp
target/
.next
.next
coverage
*.tgz

View File

@@ -1,10 +1,13 @@
const execa = require('execa');
const { join } = require('path');
const snakeCase = require('snake-case');
const glob = require('@now/build-utils/fs/glob'); // eslint-disable-line import/no-extraneous-dependencies
const download = require('@now/build-utils/fs/download'); // eslint-disable-line import/no-extraneous-dependencies
const { createLambda } = require('@now/build-utils/lambda'); // eslint-disable-line import/no-extraneous-dependencies
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory'); // eslint-disable-line import/no-extraneous-dependencies
const {
glob,
download,
createLambda,
getWriteableDirectory,
shouldServe,
} = require('@now/build-utils'); // eslint-disable-line import/no-extraneous-dependencies
exports.config = {
maxLambdaSize: '10mb',
@@ -15,7 +18,7 @@ exports.analyze = ({ files, entrypoint }) => files[entrypoint].digest;
exports.build = async ({
workPath, files, entrypoint, config,
}) => {
const srcDir = await getWritableDirectory();
const srcDir = await getWriteableDirectory();
console.log('downloading files...');
await download(files, srcDir);
@@ -55,3 +58,5 @@ exports.build = async ({
[entrypoint]: lambda,
};
};
exports.shouldServe = shouldServe;

View File

@@ -1,6 +1,6 @@
{
"name": "@now/bash",
"version": "0.2.0",
"version": "0.2.3",
"description": "Now 2.0 builder for HTTP endpoints written in Bash",
"main": "index.js",
"author": "Nathan Rajlich <nate@zeit.co>",

View File

@@ -1,6 +1,6 @@
{
"name": "@now/build-utils",
"version": "0.5.1",
"version": "0.5.5",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
@@ -10,8 +10,10 @@
"directory": "packages/now-build-utils"
},
"dependencies": {
"@types/cross-spawn": "6.0.0",
"async-retry": "1.2.3",
"async-sema": "2.1.4",
"cross-spawn": "6.0.5",
"end-of-stream": "1.4.1",
"fs-extra": "7.0.0",
"glob": "7.1.3",

View File

@@ -8,7 +8,6 @@ import { File } from './types';
interface FileRefOptions {
mode?: number;
digest: string;
mutable?: boolean;
}
const semaToDownloadFromS3 = new Sema(5);
@@ -26,26 +25,29 @@ export default class FileRef implements File {
public type: 'FileRef';
public mode: number;
public digest: string;
public mutable: boolean;
constructor({ mode = 0o100644, digest, mutable = false }: FileRefOptions) {
constructor({ mode = 0o100644, digest }: FileRefOptions) {
assert(typeof mode === 'number');
assert(typeof digest === 'string');
assert(typeof mutable === 'boolean');
this.type = 'FileRef';
this.mode = mode;
this.digest = digest;
this.mutable = mutable;
}
async toStreamAsync(): Promise<NodeJS.ReadableStream> {
let url = '';
// sha:24be087eef9fac01d61b30a725c1a10d7b45a256
const digestParts = this.digest.split(':');
if (digestParts[0] === 'sha') {
url = this.mutable
? `https://s3.amazonaws.com/now-files/${digestParts[1]}`
: `https://dmmcy0pwk6bqi.cloudfront.net/${digestParts[1]}`;
const [digestType, digestHash] = this.digest.split(':');
if (digestType === 'sha') {
// This CloudFront URL edge caches the `now-files` S3 bucket to prevent
// overloading it
// `https://now-files.s3.amazonaws.com/${digestHash}`
url = `https://dmmcy0pwk6bqi.cloudfront.net/${digestHash}`;
} else if (digestType === 'sha+ephemeral') {
// This URL is currently only used for cache files that constantly
// change. We shouldn't cache it on CloudFront because it'd always be a
// MISS.
url = `https://now-ephemeral-files.s3.amazonaws.com/${digestHash}`;
} else {
throw new Error('Expected digest to be sha');
}
@@ -58,14 +60,14 @@ export default class FileRef implements File {
const resp = await fetch(url);
if (!resp.ok) {
const error = new BailableError(
`download: ${resp.status} ${resp.statusText} for ${url}`,
`download: ${resp.status} ${resp.statusText} for ${url}`
);
if (resp.status === 403) error.bail = true;
throw error;
}
return resp.body;
},
{ factor: 1, retries: 3 },
{ factor: 1, retries: 3 }
);
} finally {
// console.timeEnd(`downloading ${url}`);
@@ -77,15 +79,15 @@ export default class FileRef implements File {
let flag = false;
// eslint-disable-next-line consistent-return
return multiStream((cb) => {
return multiStream(cb => {
if (flag) return cb(null, null);
flag = true;
this.toStreamAsync()
.then((stream) => {
.then(stream => {
cb(null, stream);
})
.catch((error) => {
.catch(error => {
cb(error, null);
});
});

View File

@@ -4,11 +4,11 @@ import { File, Files, Meta } from '../types';
import { remove, mkdirp, readlink, symlink } from 'fs-extra';
export interface DownloadedFiles {
[filePath: string]: FileFsRef
[filePath: string]: FileFsRef;
}
const S_IFMT = 61440; /* 0170000 type of file */
const S_IFLNK = 40960; /* 0120000 symbolic link */
const S_IFMT = 61440; /* 0170000 type of file */
const S_IFLNK = 40960; /* 0120000 symbolic link */
export function isSymbolicLink(mode: number): boolean {
return (mode & S_IFMT) === S_IFLNK;
@@ -17,9 +17,9 @@ export function isSymbolicLink(mode: number): boolean {
async function downloadFile(file: File, fsPath: string): Promise<FileFsRef> {
const { mode } = file;
if (mode && isSymbolicLink(mode) && file.type === 'FileFsRef') {
const [ target ] = await Promise.all([
const [target] = await Promise.all([
readlink((file as FileFsRef).fsPath),
mkdirp(path.dirname(fsPath))
mkdirp(path.dirname(fsPath)),
]);
await symlink(target, fsPath);
return FileFsRef.fromFsPath({ mode, fsPath });
@@ -34,12 +34,25 @@ async function removeFile(basePath: string, fileMatched: string) {
await remove(file);
}
export default async function download(files: Files, basePath: string, meta?: Meta): Promise<DownloadedFiles> {
export default async function download(
files: Files,
basePath: string,
meta?: Meta
): Promise<DownloadedFiles> {
const { isDev = false, filesChanged = null, filesRemoved = null } =
meta || {};
if (isDev) {
// In `now dev`, the `download()` function is a no-op because
// the `basePath` matches the `cwd` of the dev server, so the
// source files are already available.
return files as DownloadedFiles;
}
const files2: DownloadedFiles = {};
const { filesChanged = null, filesRemoved = null } = meta || {};
await Promise.all(
Object.keys(files).map(async (name) => {
Object.keys(files).map(async name => {
// If the file does not exist anymore, remove it.
if (Array.isArray(filesRemoved) && filesRemoved.includes(name)) {
await removeFile(basePath, name);
@@ -55,7 +68,7 @@ export default async function download(files: Files, basePath: string, meta?: Me
const fsPath = path.join(basePath, name);
files2[name] = await downloadFile(file, fsPath);
}),
})
);
return files2;

View File

@@ -8,12 +8,16 @@ import FileFsRef from '../file-fs-ref';
type GlobOptions = vanillaGlob_.IOptions;
interface FsFiles {
[filePath: string]: FileFsRef
[filePath: string]: FileFsRef;
}
const vanillaGlob = promisify(vanillaGlob_);
export default async function glob(pattern: string, opts: GlobOptions | string, mountpoint?: string): Promise<FsFiles> {
export default async function glob(
pattern: string,
opts: GlobOptions | string,
mountpoint?: string
): Promise<FsFiles> {
let options: GlobOptions;
if (typeof opts === 'string') {
options = { cwd: opts };
@@ -23,7 +27,7 @@ export default async function glob(pattern: string, opts: GlobOptions | string,
if (!options.cwd) {
throw new Error(
'Second argument (basePath) must be specified for names of resulting files',
'Second argument (basePath) must be specified for names of resulting files'
);
}
@@ -41,11 +45,11 @@ export default async function glob(pattern: string, opts: GlobOptions | string,
const files = await vanillaGlob(pattern, options);
for (const relativePath of files) {
const fsPath = path.join(options.cwd!, relativePath);
const fsPath = path.join(options.cwd!, relativePath).replace(/\\/g, '/');
let stat: Stats = options.statCache![fsPath] as Stats;
assert(
stat,
`statCache does not contain value for ${relativePath} (resolved to ${fsPath})`,
`statCache does not contain value for ${relativePath} (resolved to ${fsPath})`
);
if (stat.isFile()) {
const isSymlink = options.symlinks![fsPath];

View File

@@ -1,22 +1,28 @@
import assert from 'assert';
import fs from 'fs-extra';
import path from 'path';
import { spawn, SpawnOptions } from 'child_process';
import spawn from 'cross-spawn';
import { SpawnOptions } from 'child_process';
function spawnAsync(command: string, args: string[], cwd: string, opts: SpawnOptions = {}) {
function spawnAsync(
command: string,
args: string[],
cwd: string,
opts: SpawnOptions = {}
) {
return new Promise<void>((resolve, reject) => {
const stderrLogs: Buffer[] = []
const stderrLogs: Buffer[] = [];
opts = { stdio: 'inherit', cwd, ...opts };
const child = spawn(command, args, opts);
if (opts.stdio === 'pipe'){
if (opts.stdio === 'pipe') {
child.stderr.on('data', data => stderrLogs.push(data));
}
child.on('error', reject);
child.on('close', (code, signal) => {
if (code === 0) {
return resolve()
return resolve();
}
const errorLogs = stderrLogs.map(line => line.toString()).join('');
@@ -58,13 +64,15 @@ async function scanParentDirs(destPath: string, scriptName?: string) {
// eslint-disable-next-line no-await-in-loop
if (await fs.pathExists(packageJsonPath)) {
// eslint-disable-next-line no-await-in-loop
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
const packageJson = JSON.parse(
await fs.readFile(packageJsonPath, 'utf8')
);
hasScript = Boolean(
packageJson.scripts && scriptName && packageJson.scripts[scriptName],
packageJson.scripts && scriptName && packageJson.scripts[scriptName]
);
// eslint-disable-next-line no-await-in-loop
hasPackageLockJson = await fs.pathExists(
path.join(currentDestPath, 'package-lock.json'),
path.join(currentDestPath, 'package-lock.json')
);
break;
}
@@ -77,7 +85,10 @@ async function scanParentDirs(destPath: string, scriptName?: string) {
return { hasScript, hasPackageLockJson };
}
export async function installDependencies(destPath: string, args: string[] = []) {
export async function installDependencies(
destPath: string,
args: string[] = []
) {
assert(path.isAbsolute(destPath));
let commandArgs = args;
@@ -91,7 +102,7 @@ export async function installDependencies(destPath: string, args: string[] = [])
// Node.js version that `@now/node` and `@now/node-server` use
npm_config_target: '8.10.0',
},
stdio: 'pipe'
stdio: 'pipe',
};
if (hasPackageLockJson) {
@@ -105,9 +116,9 @@ export async function installDependencies(destPath: string, args: string[] = [])
} else {
await spawnAsync(
'yarn',
['--cwd', destPath].concat(commandArgs),
['--ignore-engines', '--cwd', destPath].concat(commandArgs),
destPath,
opts as SpawnOptions,
opts as SpawnOptions
);
}
}
@@ -120,7 +131,7 @@ export async function runPackageJsonScript(
assert(path.isAbsolute(destPath));
const { hasScript, hasPackageLockJson } = await scanParentDirs(
destPath,
scriptName,
scriptName
);
if (!hasScript) return false;
@@ -129,7 +140,12 @@ export async function runPackageJsonScript(
await spawnAsync('npm', ['run', scriptName], destPath, opts);
} else {
console.log(`running "yarn run ${scriptName}"`);
await spawnAsync('yarn', ['--cwd', destPath, 'run', scriptName], destPath, opts);
await spawnAsync(
'yarn',
['--cwd', destPath, 'run', scriptName],
destPath,
opts
);
}
return true;

View File

@@ -0,0 +1,9 @@
const scheduler = require('@google-cloud/scheduler');
module.exports = (_, res) => {
if (scheduler) {
res.end('found:RANDOMNESS_PLACEHOLDER');
} else {
res.end('nope:RANDOMNESS_PLACEHOLDER');
}
};

View File

@@ -0,0 +1,10 @@
{
"version": 2,
"builds": [
{ "src": "index.js", "use": "@now/node", "config": { "maxLambdaSize": "15mb" } }
],
"probes": [
{ "path": "/", "mustContain": "found:RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -0,0 +1,8 @@
{
"name": "15-yarn-ignore-engines",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"@google-cloud/scheduler": "0.3.0"
}
}

View File

@@ -5,15 +5,17 @@ const glob = require('@now/build-utils/fs/glob'); // eslint-disable-line import/
const download = require('@now/build-utils/fs/download'); // eslint-disable-line import/no-extraneous-dependencies
const { createLambda } = require('@now/build-utils/lambda'); // eslint-disable-line import/no-extraneous-dependencies
const getWritableDirectory = require('@now/build-utils/fs/get-writable-directory'); // eslint-disable-line import/no-extraneous-dependencies
const { shouldServe } = require('@now/build-utils'); // eslint-disable-line import/no-extraneous-dependencies
exports.analyze = ({ files, entrypoint }) => files[entrypoint].digest;
exports.build = async ({ files, entrypoint }) => {
exports.build = async ({
workPath, files, entrypoint, meta,
}) => {
console.log('downloading files...');
const srcDir = await getWritableDirectory();
const outDir = await getWritableDirectory();
await download(files, srcDir);
await download(files, workPath, meta);
const handlerPath = path.join(__dirname, 'handler');
await copyFile(handlerPath, path.join(outDir, 'handler'));
@@ -23,7 +25,7 @@ exports.build = async ({ files, entrypoint }) => {
// For now only the entrypoint file is copied into the lambda
await copyFile(
path.join(srcDir, entrypoint),
path.join(workPath, entrypoint),
path.join(outDir, entrypoint),
);
@@ -40,3 +42,5 @@ exports.build = async ({ files, entrypoint }) => {
[entrypoint]: lambda,
};
};
exports.shouldServe = shouldServe;

View File

@@ -1,6 +1,6 @@
{
"name": "@now/cgi",
"version": "0.1.0",
"version": "0.1.4",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -3,6 +3,7 @@ import execa from 'execa';
import fetch from 'node-fetch';
import { mkdirp, pathExists } from 'fs-extra';
import { dirname, join } from 'path';
import { homedir } from 'os';
import Debug from 'debug';
const debug = Debug('@now/go:go-helpers');
@@ -118,16 +119,25 @@ export async function downloadGo(
platform = process.platform,
arch = process.arch
) {
debug('Installing `go` v%s to %o for %s %s', version, dir, platform, arch);
// Check default `Go` in user machine
const isUserGo = await pathExists(join(homedir(), 'go'));
const url = getGoUrl(version, platform, arch);
// if we found GOPATH in ENV, use it
if (process.env.GOPATH !== undefined) {
// If we found GOPATH in ENV, or default `Go` path exists
// asssume that user have `Go` installed
if (isUserGo || process.env.GOPATH !== undefined) {
return createGo(dir, platform, arch);
} else {
// Check `Go` bin in builder CWD
const isGoExist = await pathExists(join(dir, 'bin'));
if (!isGoExist) {
debug(
'Installing `go` v%s to %o for %s %s',
version,
dir,
platform,
arch
);
const url = getGoUrl(version, platform, arch);
debug('Downloading `go` URL: %o', url);
console.log('Downloading Go ...');
const res = await fetch(url);

View File

@@ -14,6 +14,7 @@ import {
import { createGo, getAnalyzedEntrypoint } from './go-helpers';
interface Analyzed {
found?: boolean;
packageName: string;
functionName: string;
watch: string[];
@@ -47,6 +48,7 @@ export async function build({
getWriteableDirectory(),
getWriteableDirectory(),
]);
if (meta.isDev) {
const devGoPath = `dev${entrypointArr[entrypointArr.length - 1]}`;
const goPathArr = goPath.split(sep);
@@ -57,6 +59,17 @@ export async function build({
const srcPath = join(goPath, 'src', 'lambda');
const downloadedFiles = await download(files, srcPath);
const input = dirname(downloadedFiles[entrypoint].fsPath);
var includedFiles: Files = {};
if (config && config.includeFiles) {
for (const pattern of config.includeFiles) {
const files = await glob(pattern, input);
for (const assetName of Object.keys(files)) {
includedFiles[assetName] = files[assetName];
}
}
}
console.log(`Parsing AST for "${entrypoint}"`);
let analyzed: string;
@@ -69,7 +82,9 @@ export async function build({
if (!analyzed) {
const err = new Error(
`Could not find an exported function in "${entrypoint}"`
`Could not find an exported function in "${entrypoint}"
Learn more: https://zeit.co/docs/v2/deployments/official-builders/go-now-go/#entrypoint
`
);
console.log(err.message);
throw err;
@@ -264,7 +279,7 @@ export async function build({
}
const lambda = await createLambda({
files: await glob('**', outDir),
files: { ...(await glob('**', outDir)), ...includedFiles },
handler: 'handler',
runtime: 'go1.x',
environment: {},

View File

@@ -1,6 +1,6 @@
{
"name": "@now/go",
"version": "0.4.2",
"version": "0.4.6",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -0,0 +1,16 @@
package cowsay
import (
"fmt"
"io/ioutil"
"net/http"
)
// Handler function
func Handler(w http.ResponseWriter, r *http.Request) {
bts, err := ioutil.ReadFile("templates/foo.txt")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
fmt.Fprintf(w, string(bts))
}

View File

@@ -0,0 +1,20 @@
{
"version": 2,
"builds": [
{
"src": "index.go",
"use": "@now/go",
"config": {
"includeFiles": [
"templates/**"
]
}
}
],
"probes": [
{
"path": "/",
"mustContain": "foobar from file"
}
]
}

View File

@@ -0,0 +1 @@
foobar from file

View File

@@ -0,0 +1,22 @@
package function
import (
"fmt"
"net/http"
)
// Person struct
type Person struct {
name string
age int
}
// NewPerson struct method
func NewPerson(name string, age int) *Person {
return &Person{name: name, age: age}
}
// H func
func H(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "RANDOMNESS_PLACEHOLDER")
}

View File

@@ -0,0 +1,9 @@
{
"version": 2,
"builds": [
{ "src": "index.go", "use": "@now/go" }
],
"probes": [
{ "path": "/", "mustContain": "RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -138,24 +138,32 @@ func main() {
}
parsed := parse(fileName)
offset := parsed.Pos()
reqRep := "*http.Request http.ResponseWriter"
for _, decl := range parsed.Decls {
fn, ok := decl.(*ast.FuncDecl)
if !ok {
// this declaraction is not a function
// this declaration is not a function
// so we're not interested
continue
}
if fn.Name.IsExported() == true {
// we found the first exported function
// we're done!
analyzed := analyze{
PackageName: parsed.Name.Name,
FuncName: fn.Name.Name,
Watch: unique(relatedFiles),
// find a valid `net/http` handler function
for _, param := range fn.Type.Params.List {
if strings.Contains(reqRep, string(rf[param.Type.Pos()-offset:param.Type.End()-offset])) {
// we found the first exported function with `net/http`
// we're done!
analyzed := analyze{
PackageName: parsed.Name.Name,
FuncName: fn.Name.Name,
Watch: unique(relatedFiles),
}
analyzedJSON, _ := json.Marshal(analyzed)
fmt.Print(string(analyzedJSON))
os.Exit(0)
}
}
json, _ := json.Marshal(analyzed)
fmt.Print(string(json))
os.Exit(0)
}
}
}

View File

@@ -1,4 +1,4 @@
const FileBlob = require('@now/build-utils/file-blob.js'); // eslint-disable-line import/no-extraneous-dependencies
const { FileBlob, shouldServe } = require('@now/build-utils'); // eslint-disable-line import/no-extraneous-dependencies
const { minify } = require('html-minifier');
const defaultOptions = {
@@ -28,3 +28,5 @@ exports.build = async ({ files, entrypoint, config }) => {
return { [entrypoint]: result };
};
exports.shouldServe = shouldServe;

View File

@@ -1,6 +1,6 @@
{
"name": "@now/html-minifier",
"version": "1.1.0",
"version": "1.1.3",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -1,5 +1,6 @@
const { Lambda } = require('@now/build-utils/lambda.js'); // eslint-disable-line import/no-extraneous-dependencies
const streamToBuffer = require('@now/build-utils/fs/stream-to-buffer.js'); // eslint-disable-line import/no-extraneous-dependencies
const { shouldServe } = require('@now/build-utils'); // eslint-disable-line import/no-extraneous-dependencies
exports.build = async ({ files, entrypoint, config }) => {
if (!files[entrypoint]) throw new Error('Entrypoint not found in files');
@@ -10,3 +11,5 @@ exports.build = async ({ files, entrypoint, config }) => {
const lambda = new Lambda({ zipBuffer, handler, runtime });
return { [entrypoint]: lambda };
};
exports.shouldServe = shouldServe;

View File

@@ -1,6 +1,6 @@
{
"name": "@now/lambda",
"version": "0.5.0",
"version": "0.5.3",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -1,4 +1,4 @@
const FileBlob = require('@now/build-utils/file-blob.js'); // eslint-disable-line import/no-extraneous-dependencies
const { FileBlob, shouldServe } = require('@now/build-utils'); // eslint-disable-line import/no-extraneous-dependencies
const unified = require('unified');
const unifiedStream = require('unified-stream');
const markdown = require('remark-parse');
@@ -38,3 +38,5 @@ exports.build = async ({ files, entrypoint, config }) => {
return { [replacedEntrypoint]: result };
};
exports.shouldServe = shouldServe;

View File

@@ -1,6 +1,6 @@
{
"name": "@now/md",
"version": "0.5.0",
"version": "0.5.3",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -8,9 +8,11 @@ const { runNpmInstall } = require('@now/build-utils/fs/run-user-scripts.js'); //
const writeFile = promisify(fs.writeFile);
exports.build = async ({ files, entrypoint, workPath }) => {
exports.build = async ({
files, entrypoint, workPath, meta,
}) => {
console.log('downloading user files...');
const downloadedFiles = await download(files, workPath);
const downloadedFiles = await download(files, workPath, meta);
console.log('writing package.json...');
const packageJson = { dependencies: { 'mdx-deck': '1.7.15' } };
const packageJsonPath = path.join(workPath, 'package.json');

View File

@@ -1,6 +1,6 @@
{
"name": "@now/mdx-deck",
"version": "0.5.0",
"version": "0.5.4",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@now/next",
"version": "0.2.0",
"version": "0.3.2",
"license": "MIT",
"main": "./dist/index",
"scripts": {
@@ -14,7 +14,7 @@
"directory": "packages/now-next"
},
"dependencies": {
"@now/node-bridge": "^1.1.0",
"@now/node-bridge": "^1.1.2",
"fs-extra": "^7.0.0",
"get-port": "^5.0.0",
"resolve-from": "^5.0.0",

View File

@@ -2,18 +2,21 @@ import resolveFrom from 'resolve-from';
import { parse } from 'url';
import getPort from 'get-port';
import { createServer } from 'http';
import { syncEnvVars } from './utils';
export interface ProcessEnv {
[key: string]: string;
}
process.on('unhandledRejection', err => {
console.error('Exiting builder due to build error:');
console.error(err);
process.exit(1);
});
async function main(env: ProcessEnv, cwd: string) {
async function main(cwd: string) {
const next = require(resolveFrom(cwd, 'next'));
const app = next({ dev: true, dir: cwd });
const handler = app.getRequestHandler();
const openPort = await getPort({
port: [ 5000, 4000 ]
port: [5000, 4000],
});
const url = `http://localhost:${openPort}`;
@@ -21,21 +24,21 @@ async function main(env: ProcessEnv, cwd: string) {
// Prepare for incoming requests
await app.prepare();
// The runtime env vars are passed in to `argv[2]`
// as a base64-encoded JSON string
const runtimeEnv = JSON.parse(
Buffer.from(process.argv[2], 'base64').toString()
);
syncEnvVars(process.env, process.env, runtimeEnv);
createServer((req, res) => {
const parsedUrl = parse(req.url || '', true);
handler(req, res, parsedUrl);
}).listen(openPort, (error: NodeJS.ErrnoException) => {
if (error) {
console.error(error);
process.exit(1);
return;
}
}).listen(openPort, () => {
if (process.send) {
process.send(url);
}
});
}
main(process.env as ProcessEnv, process.cwd());
main(process.cwd());

View File

@@ -25,19 +25,23 @@ import {
import nextLegacyVersions from './legacy-versions';
import {
EnvConfig,
excludeFiles,
getNextConfig,
getPathsInside,
getRoutes,
includeOnlyEntryDirectory,
normalizePackageJson,
onlyStaticDirectory,
filesFromDirectory,
stringMap,
syncEnvVars,
validateEntrypoint,
} from './utils';
interface BuildParamsMeta {
isDev: boolean | undefined;
env?: EnvConfig;
buildEnv?: EnvConfig;
}
interface BuildParamsType extends BuildOptions {
@@ -119,13 +123,17 @@ function isLegacyNext(nextVersion: string) {
const name = '[@now/next]';
const urls: stringMap = {};
function startDevServer(entryPath: string) {
const forked = fork(path.join(__dirname, 'dev-server.js'), [], {
function startDevServer(entryPath: string, runtimeEnv: EnvConfig) {
// The runtime env vars are encoded and passed in as `argv[2]`, so that the
// dev-server process can replace them onto `process.env` after the Next.js
// "prepare" step
const encodedEnv = Buffer.from(JSON.stringify(runtimeEnv)).toString('base64');
// `env` is omitted since that
// makes it default to `process.env`
const forked = fork(path.join(__dirname, 'dev-server.js'), [encodedEnv], {
cwd: entryPath,
execArgv: [],
env: {
NOW_REGION: 'dev1',
},
});
const getUrl = () =>
@@ -182,7 +190,13 @@ export const build = async ({
if (!urls[entrypoint]) {
console.log(`${name} Installing dependencies...`);
await runNpmInstall(entryPath, ['--prefer-offline']);
const { forked, getUrl } = startDevServer(entryPath);
// The runtime env vars consist of the base `process.env` vars, but with the
// build env vars removed, and the runtime env vars mixed in afterwards
const runtimeEnv: EnvConfig = Object.assign({}, process.env);
syncEnvVars(runtimeEnv, meta.buildEnv || {}, meta.env || {});
const { forked, getUrl } = startDevServer(entryPath, runtimeEnv);
urls[entrypoint] = await getUrl();
childProcess = forked;
console.log(
@@ -434,13 +448,30 @@ export const build = async ({
{}
);
const staticDirectoryFiles = onlyStaticDirectory(
includeOnlyEntryDirectory(files, entryDirectory),
entryDirectory
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(
(mappedFiles, file) => ({
...mappedFiles,
[file.replace(/public[/\\]+/, '')]: publicDirectoryFiles[file],
}),
{}
);
return {
output: { ...lambdas, ...staticFiles, ...staticDirectoryFiles },
output: {
...publicFiles,
...lambdas,
...staticFiles,
...staticDirectoryFiles,
},
routes: [],
watch: [],
childProcesses: [],

View File

@@ -1,12 +1,16 @@
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = process.env.NOW_REGION === 'dev1' ? 'development' : 'production';
process.env.NODE_ENV =
process.env.NOW_REGION === 'dev1' ? 'development' : 'production';
}
const { Server } = require('http');
const { Bridge } = require('./now__bridge');
const page = require('./page');
const server = new Server(page.render);
// page.render is for React rendering
// page.default is for /api rendering
// page is for module.exports in /api
const server = new Server(page.render || page.default || page);
const bridge = new Bridge(server);
bridge.listen();

View File

@@ -2,18 +2,22 @@ import fs from 'fs-extra';
import path from 'path';
import { Files } from '@now/build-utils';
type stringMap = {[key: string]: string};
type stringMap = { [key: string]: string };
export interface EnvConfig {
[name: string]: string | undefined;
}
/**
* Validate if the entrypoint is allowed to be used
*/
function validateEntrypoint(entrypoint: string) {
if (
!/package\.json$/.exec(entrypoint)
&& !/next\.config\.js$/.exec(entrypoint)
!/package\.json$/.exec(entrypoint) &&
!/next\.config\.js$/.exec(entrypoint)
) {
throw new Error(
'Specified "src" for "@now/next" has to be "package.json" or "next.config.js"',
'Specified "src" for "@now/next" has to be "package.json" or "next.config.js"'
);
}
}
@@ -21,7 +25,10 @@ function validateEntrypoint(entrypoint: string) {
/**
* Exclude certain files from the files object
*/
function excludeFiles(files: Files, matcher: (filePath: string) => boolean): Files {
function excludeFiles(
files: Files,
matcher: (filePath: string) => boolean
): Files {
return Object.keys(files).reduce((newFiles, filePath) => {
if (matcher(filePath)) {
return newFiles;
@@ -36,7 +43,10 @@ function excludeFiles(files: Files, matcher: (filePath: string) => boolean): Fil
/**
* Creates a new Files object holding only the entrypoint files
*/
function includeOnlyEntryDirectory(files: Files, entryDirectory: string): Files {
function includeOnlyEntryDirectory(
files: Files,
entryDirectory: string
): Files {
if (entryDirectory === '.') {
return files;
}
@@ -63,11 +73,11 @@ function excludeLockFiles(files: Files): Files {
}
/**
* Include the static directory from files
* Include only the files from a selected directory
*/
function onlyStaticDirectory(files: Files, entryDir: string): Files {
function filesFromDirectory(files: Files, dir: string): Files {
function matcher(filePath: string) {
return !filePath.startsWith(path.join(entryDir, 'static'));
return !filePath.startsWith(dir.replace(/\\/g, '/'));
}
return excludeFiles(files, matcher);
@@ -76,7 +86,13 @@ function onlyStaticDirectory(files: Files, entryDir: string): Files {
/**
* Enforce specific package.json configuration for smallest possible lambda
*/
function normalizePackageJson(defaultPackageJson: {dependencies?: stringMap, devDependencies?: stringMap, scripts?: stringMap} = {}) {
function normalizePackageJson(
defaultPackageJson: {
dependencies?: stringMap;
devDependencies?: stringMap;
scripts?: stringMap;
} = {}
) {
const dependencies: stringMap = {};
const devDependencies: stringMap = {
...defaultPackageJson.dependencies,
@@ -112,7 +128,8 @@ function normalizePackageJson(defaultPackageJson: {dependencies?: stringMap, dev
},
scripts: {
...defaultPackageJson.scripts,
'now-build': 'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas',
'now-build':
'NODE_OPTIONS=--max_old_space_size=3000 next build --lambdas',
},
};
}
@@ -151,7 +168,12 @@ function getPathsInside(entryDirectory: string, files: Files) {
return watch;
}
function getRoutes(entryDirectory: string, pathsInside: string[], files: Files, url: string): any[] {
function getRoutes(
entryDirectory: string,
pathsInside: string[],
files: Files,
url: string
): any[] {
const filesInside: Files = {};
const prefix = entryDirectory === `.` ? `/` : `/${entryDirectory}/`;
@@ -166,15 +188,16 @@ function getRoutes(entryDirectory: string, pathsInside: string[], files: Files,
const routes: any[] = [
{
src: `${prefix}_next/(.*)`,
dest: `${url}/_next/$1`
dest: `${url}/_next/$1`,
},
{
src: `${prefix}static/(.*)`,
dest: `${url}/static/$1`
}
dest: `${url}/static/$1`,
},
];
const filePaths = Object.keys(filesInside);
for (const file of Object.keys(filesInside)) {
for (const file of filePaths) {
const relativePath = path.relative(entryDirectory, file);
const isPage = pathIsInside('pages', relativePath);
@@ -192,7 +215,7 @@ function getRoutes(entryDirectory: string, pathsInside: string[], files: Files,
routes.push({
src: `${prefix}${pageName}`,
dest: `${url}/${pageName}`
dest: `${url}/${pageName}`,
});
if (pageName.endsWith('index')) {
@@ -200,23 +223,57 @@ function getRoutes(entryDirectory: string, pathsInside: string[], files: Files,
routes.push({
src: `${prefix}${resolvedIndex}`,
dest: `${url}/${resolvedIndex}`
dest: `${url}/${resolvedIndex}`,
});
}
}
// Add public folder routes
for (const file of filePaths) {
const relativePath = path.relative(entryDirectory, file);
const isPublic = pathIsInside('public', relativePath);
if (!isPublic) continue;
const fileName = path.relative('public', relativePath);
const route = {
src: `${prefix}${fileName}`,
dest: `${url}/${fileName}`,
};
// Only add the route if a page is not already using it
if (!routes.some(r => r.src === route.src)) {
routes.push(route);
}
}
return routes;
}
function syncEnvVars(base: EnvConfig, removeEnv: EnvConfig, addEnv: EnvConfig) {
// Remove any env vars from `removeEnv`
// that are not present in the `addEnv`
const addKeys = new Set(Object.keys(addEnv));
for (const name of Object.keys(removeEnv)) {
if (!addKeys.has(name)) {
delete base[name];
}
}
// Add in the keys from `addEnv`
Object.assign(base, addEnv);
}
export {
excludeFiles,
validateEntrypoint,
includeOnlyEntryDirectory,
excludeLockFiles,
normalizePackageJson,
onlyStaticDirectory,
filesFromDirectory,
getNextConfig,
getPathsInside,
getRoutes,
stringMap,
syncEnvVars,
};

View File

@@ -28,6 +28,7 @@ it(
} = await runBuildLambda(path.join(__dirname, 'monorepo'));
expect(output['www/index']).toBeDefined();
expect(output['www/static/test.txt']).toBeDefined();
expect(output['www/data.txt']).toBeDefined();
const filePaths = Object.keys(output);
const hasUnderScoreAppStaticFile = filePaths.some(filePath => filePath.match(/static.*\/pages\/_app\.js$/));
const hasUnderScoreErrorStaticFile = filePaths.some(filePath => filePath.match(/static.*\/pages\/_error\.js$/));
@@ -96,3 +97,14 @@ it(
},
FOUR_MINUTES,
);
it(
'Should build the public-files test',
async () => {
const {
buildResult: { output },
} = await runBuildLambda(path.join(__dirname, 'public-files'));
expect(output['robots.txt']).toBeDefined();
},
FOUR_MINUTES,
);

View File

@@ -0,0 +1 @@
data

View File

@@ -0,0 +1,3 @@
module.exports = {
target: 'serverless',
};

View File

@@ -0,0 +1,6 @@
{
"version": 2,
"builds": [
{"src": "next.config.js", "use": "@now/next"}
]
}

View File

@@ -0,0 +1,10 @@
{
"scripts": {
"now-build": "next build"
},
"dependencies": {
"next": "8",
"react": "16",
"react-dom": "16"
}
}

View File

@@ -0,0 +1 @@
export default () => 'Index page';

View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow: /

View File

@@ -2,7 +2,7 @@
const path = require('path');
const os = require('os');
const { build } = require('@now/next');
const { FileBlob } = require('@now/build-utils');
const { download, FileBlob } = require('@now/build-utils');
jest.setTimeout(45000);
@@ -22,6 +22,15 @@ describe('build meta dev', () => {
export default () => 'Index page'
`,
}),
// This file should be omitted because `pages/index.js` will use the same route
'public/index': new FileBlob({
mode: 0o777,
data: 'text',
}),
'public/data.txt': new FileBlob({
mode: 0o777,
data: 'data',
}),
'package.json': new FileBlob({
mode: 0o777,
data: `
@@ -46,24 +55,12 @@ describe('build meta dev', () => {
.slice(3),
);
console.log('workPath directory: ', workPath);
/*
it('should have builder v2 response isDev=false', async () => {
const meta = { isDev: false, requestPath: null };
const { output, routes, watch } = await build({
files,
workPath,
entrypoint,
meta,
});
//console.log('output: ', Object.keys(output));
expect(Object.keys(output).length).toBe(7);
expect(output.index.type).toBe('Lambda');
expect(routes.length).toBe(0);
expect(watch.length).toBe(0);
});
*/
it('should have builder v2 response isDev=true', async () => {
// Since `download()` is a no-op when `isDev=true`, the assumption is that the
// source files are already present, so manually download them here first.
await download(files, workPath);
const meta = { isDev: true, requestPath: null };
const {
output, routes, watch, childProcesses,
@@ -83,8 +80,15 @@ describe('build meta dev', () => {
{ src: '/static/(.*)', dest: 'http://localhost:5000/static/$1' },
{ src: '/index', dest: 'http://localhost:5000/index' },
{ src: '/', dest: 'http://localhost:5000/' },
{ src: '/data.txt', dest: 'http://localhost:5000/data.txt' },
]);
expect(watch).toEqual([
'next.config.js',
'pages/index.js',
'public/index',
'public/data.txt',
'package.json',
]);
expect(watch).toEqual(['next.config.js', 'pages/index.js', 'package.json']);
childProcesses.forEach(cp => cp.kill());
});
});

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node-bridge",
"version": "1.1.0",
"version": "1.1.2",
"license": "MIT",
"main": "./index.js",
"repository": {

View File

@@ -9,6 +9,7 @@ const {
runNpmInstall,
runPackageJsonScript,
} = require('@now/build-utils/fs/run-user-scripts.js'); // eslint-disable-line import/no-extraneous-dependencies
const { shouldServe } = require('@now/build-utils'); // eslint-disable-line import/no-extraneous-dependencies
/** @typedef { import('@now/build-utils/file-ref') } FileRef */
/** @typedef {{[filePath: string]: FileRef}} Files */
@@ -27,11 +28,13 @@ const {
* @param {string[]} [options.npmArguments]
*/
async function downloadInstallAndBundle(
{ files, entrypoint, workPath },
{
files, entrypoint, workPath, meta,
},
{ npmArguments = [] } = {},
) {
console.log('downloading user files...');
const downloadedFiles = await download(files, workPath);
const downloadedFiles = await download(files, workPath, meta);
console.log("installing dependencies for user's code...");
const entrypointFsDirname = path.join(workPath, path.dirname(entrypoint));
@@ -49,8 +52,12 @@ async function compile(workPath, downloadedFiles, entrypoint, config) {
});
if (config && config.includeFiles) {
const includeFiles = typeof config.includeFiles === 'string'
? [config.includeFiles]
: config.includeFiles;
// eslint-disable-next-line no-restricted-syntax
for (const pattern of config.includeFiles) {
for (const pattern of includeFiles) {
// eslint-disable-next-line no-await-in-loop
const files = await glob(pattern, inputDir);
@@ -94,10 +101,12 @@ exports.config = {
* @returns {Promise<Files>}
*/
exports.build = async ({
files, entrypoint, config, workPath,
files, entrypoint, config, workPath, meta,
}) => {
const [downloadedFiles, entrypointFsDirname] = await downloadInstallAndBundle(
{ files, entrypoint, workPath },
{
files, entrypoint, workPath, meta,
},
{ npmArguments: ['--prefer-offline'] },
);
@@ -145,3 +154,5 @@ exports.prepareCache = async ({ workPath }) => ({
...(await glob('package-lock.json', workPath)),
...(await glob('yarn.lock', workPath)),
});
exports.shouldServe = shouldServe;

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node-server",
"version": "0.6.1-canary.0",
"version": "0.7.2",
"license": "MIT",
"repository": {
"type": "git",
@@ -8,8 +8,8 @@
"directory": "packages/now-node-server"
},
"dependencies": {
"@now/node-bridge": "^1.1.0",
"@zeit/ncc": "0.18.2",
"@now/node-bridge": "^1.1.2",
"@zeit/ncc": "0.18.5",
"fs-extra": "7.0.1"
},
"scripts": {

View File

@@ -0,0 +1,5 @@
const express = require('express');
const app = express();
app.listen();

View File

@@ -9,6 +9,13 @@
"templates/**"
]
}
},
{
"src": "accepts-string/index.js",
"use": "@now/node-server",
"config": {
"includeFiles": "templates/**"
}
}
],
"probes": [

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node",
"version": "0.6.1-canary.0",
"version": "0.7.2",
"license": "MIT",
"main": "./dist/index",
"repository": {
@@ -9,8 +9,8 @@
"directory": "packages/now-node"
},
"dependencies": {
"@now/node-bridge": "^1.1.0",
"@zeit/ncc": "0.18.2",
"@now/node-bridge": "^1.1.2",
"@zeit/ncc": "0.18.5",
"fs-extra": "7.0.1"
},
"scripts": {

View File

@@ -6,21 +6,24 @@ import {
FileBlob,
FileFsRef,
Files,
Meta,
createLambda,
runNpmInstall,
runPackageJsonScript,
PrepareCacheOptions,
BuildOptions,
shouldServe,
} from '@now/build-utils';
interface CompilerConfig {
includeFiles?: string[]
includeFiles?: string | string[];
}
interface DownloadOptions {
files: Files,
files: Files;
entrypoint: string;
workPath: string;
meta?: Meta;
npmArguments?: string[];
}
@@ -28,10 +31,11 @@ async function downloadInstallAndBundle({
files,
entrypoint,
workPath,
npmArguments = []
meta,
npmArguments = [],
}: DownloadOptions) {
console.log('downloading user files...');
const downloadedFiles = await download(files, workPath);
const downloadedFiles = await download(files, workPath, meta);
console.log("installing dependencies for user's code...");
const entrypointFsDirname = join(workPath, dirname(entrypoint));
@@ -41,15 +45,27 @@ async function downloadInstallAndBundle({
return { entrypointPath, entrypointFsDirname };
}
async function compile(entrypointPath: string, entrypoint: string, config: CompilerConfig): Promise<Files> {
async function compile(
entrypointPath: string,
entrypoint: string,
config: CompilerConfig
): Promise<Files> {
const input = entrypointPath;
const inputDir = dirname(input);
const rootIncludeFiles = inputDir.split(sep).pop() || '';
const ncc = require('@zeit/ncc');
const { code, map, assets } = await ncc(input, { sourceMap: true, sourceMapRegister: true });
const { code, map, assets } = await ncc(input, {
sourceMap: true,
sourceMapRegister: true,
});
if (config && config.includeFiles) {
for (const pattern of config.includeFiles) {
const includeFiles =
typeof config.includeFiles === 'string'
? [config.includeFiles]
: config.includeFiles;
for (const pattern of includeFiles) {
const files = await glob(pattern, inputDir);
for (const assetName of Object.keys(files)) {
@@ -61,12 +77,12 @@ async function compile(entrypointPath: string, entrypoint: string, config: Compi
// if asset contain directory
// no need to use `rootIncludeFiles`
if (assetName.includes(sep)) {
fullPath = assetName
fullPath = assetName;
}
assets[fullPath] = {
'source': data,
'permissions': mode
source: data,
permissions: mode,
};
}
}
@@ -75,7 +91,9 @@ async function compile(entrypointPath: string, entrypoint: string, config: Compi
const preparedFiles: Files = {};
// move all user code to 'user' subdirectory
preparedFiles[entrypoint] = new FileBlob({ data: code });
preparedFiles[`${entrypoint.replace('.ts', '.js')}.map`] = new FileBlob({ data: map });
preparedFiles[`${entrypoint.replace('.ts', '.js')}.map`] = new FileBlob({
data: map,
});
// eslint-disable-next-line no-restricted-syntax
for (const assetName of Object.keys(assets)) {
const { source: data, permissions: mode } = assets[assetName];
@@ -87,16 +105,26 @@ async function compile(entrypointPath: string, entrypoint: string, config: Compi
}
export const config = {
maxLambdaSize: '5mb'
maxLambdaSize: '5mb',
};
export async function build({ files, entrypoint, workPath, config }: BuildOptions) {
export async function build({
files,
entrypoint,
workPath,
config,
meta,
}: BuildOptions) {
const {
entrypointPath,
entrypointFsDirname
} = await downloadInstallAndBundle(
{ files, entrypoint, workPath, npmArguments: ['--prefer-offline'] }
);
entrypointFsDirname,
} = await downloadInstallAndBundle({
files,
entrypoint,
workPath,
meta,
npmArguments: ['--prefer-offline'],
});
console.log('running user script...');
await runPackageJsonScript(entrypointFsDirname, 'now-build');
@@ -110,19 +138,19 @@ export async function build({ files, entrypoint, workPath, config }: BuildOption
'// PLACEHOLDER',
[
`listener = require("./${entrypoint}");`,
'if (listener.default) listener = listener.default;'
'if (listener.default) listener = listener.default;',
].join(' ')
);
const launcherFiles = {
'launcher.js': new FileBlob({ data: launcherData }),
'bridge.js': new FileFsRef({ fsPath: require('@now/node-bridge') })
'bridge.js': new FileFsRef({ fsPath: require('@now/node-bridge') }),
};
const lambda = await createLambda({
files: { ...preparedFiles, ...launcherFiles },
handler: 'launcher.launcher',
runtime: 'nodejs8.10'
runtime: 'nodejs8.10',
});
return { [entrypoint]: lambda };
@@ -132,6 +160,8 @@ export async function prepareCache({ workPath }: PrepareCacheOptions) {
return {
...(await glob('node_modules/**', workPath)),
...(await glob('package-lock.json', workPath)),
...(await glob('yarn.lock', workPath))
...(await glob('yarn.lock', workPath)),
};
}
export { shouldServe };

View File

@@ -0,0 +1,7 @@
const edge = require('edge.js');
module.exports = (req, resp) => {
edge.registerViews('templates');
resp.end(edge.render('accepts-string', { name: 'String!' }));
};

View File

@@ -18,6 +18,13 @@
"root.edge"
]
}
},
{
"src": "accepts-string.js",
"use": "@now/node",
"config": {
"includeFiles": "templates/accepts-string.edge"
}
}
],
"probes": [
@@ -25,6 +32,10 @@
"path": "/",
"mustContain": "hello Now!"
},
{
"path": "/accepts-string.js",
"mustContain": "hello String!"
},
{
"path": "/root.js",
"mustContain": "hello Root!"

View File

@@ -0,0 +1 @@
hello {{ name }}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/optipng",
"version": "0.5.0",
"version": "0.6.1",
"license": "MIT",
"main": "./dist/index",
"files": [

View File

@@ -1,13 +1,14 @@
// eslint-disable-line import/no-extraneous-dependencies
import {
import {
FileBlob,
BuildOptions,
AnalyzeOptions
} from '@now/build-utils'
import OptiPng from 'optipng'
import pipe from 'multipipe'
AnalyzeOptions,
shouldServe,
} from '@now/build-utils';
import OptiPng from 'optipng';
import pipe from 'multipipe';
export function analyze({ files, entrypoint }: AnalyzeOptions) {
export function analyze({ files, entrypoint }: AnalyzeOptions) {
return files[entrypoint].digest;
}
@@ -20,3 +21,5 @@ export async function build({ files, entrypoint }: BuildOptions) {
const result = await FileBlob.fromStream({ stream });
return { [entrypoint]: result };
}
export { shouldServe };

View File

@@ -1,6 +1,6 @@
{
"name": "@now/php-bridge",
"version": "0.5.0",
"version": "0.5.2",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -1,5 +1,9 @@
const {
createLambda, rename, glob, download,
createLambda,
rename,
glob,
download,
shouldServe,
} = require('@now/build-utils'); // eslint-disable-line import/no-extraneous-dependencies
const path = require('path');
const { getFiles } = require('@now/php-bridge');
@@ -9,11 +13,10 @@ exports.config = {
};
exports.build = async ({
files, entrypoint, workPath, config,
files, entrypoint, workPath, config, meta,
}) => {
// Download all files to workPath
const fileDir = path.join(workPath, 'userfiles');
const downloadedFiles = await download(files, fileDir);
const downloadedFiles = await download(files, workPath, meta);
let includedFiles = {};
if (config && config.includeFiles) {
@@ -21,7 +24,7 @@ exports.build = async ({
// eslint-disable-next-line no-restricted-syntax
for (const pattern of config.includeFiles) {
// eslint-disable-next-line no-await-in-loop
const matchedFiles = await glob(pattern, fileDir);
const matchedFiles = await glob(pattern, workPath);
Object.assign(includedFiles, matchedFiles);
}
// explicit and always include the entrypoint
@@ -52,3 +55,5 @@ exports.build = async ({
return { [entrypoint]: lambda };
};
exports.shouldServe = shouldServe;

View File

@@ -1,6 +1,6 @@
{
"name": "@now/php",
"version": "0.5.0",
"version": "0.5.4",
"license": "MIT",
"repository": {
"type": "git",
@@ -8,7 +8,7 @@
"directory": "packages/now-php"
},
"dependencies": {
"@now/php-bridge": "^0.5.0"
"@now/php-bridge": "^0.5.2"
},
"scripts": {
"test": "jest"

View File

@@ -1,56 +0,0 @@
import { join } from 'path';
import fetch from 'node-fetch';
import execa from 'execa';
import { createWriteStream } from 'fs';
import { getWriteableDirectory } from '@now/build-utils';
const url = 'https://bootstrap.pypa.io/get-pip.py';
// downloads `get-pip.py` and returns its absolute path
async function downloadGetPipScript() {
console.log('downloading "get-pip.py"...');
const res = await fetch(url);
if (!res.ok || res.status !== 200) {
throw new Error(`Could not download "get-pip.py" from "${url}"`);
}
const dir = await getWriteableDirectory();
const filePath = join(dir, 'get-pip.py');
const writeStream = createWriteStream(filePath);
return new Promise<string>((resolve, reject) => {
res.body
.on('error', reject)
.pipe(writeStream)
.on('finish', () => resolve(filePath));
});
}
// downloads and installs `pip` (respecting
// process.env.PYTHONUSERBASE), and returns
// the absolute path to it
export async function downloadAndInstallPip() {
const { PYTHONUSERBASE } = process.env;
if (!PYTHONUSERBASE) {
// this is the directory in which `pip` will be
// installed to. `--user` will assume `~` if this
// is not set, and `~` is not writeable on AWS Lambda.
// let's refuse to proceed
throw new Error(
'Could not install "pip": "PYTHONUSERBASE" env var is not set',
);
}
const getPipFilePath = await downloadGetPipScript();
console.log('running "python get-pip.py"...');
try {
await execa('python3', [getPipFilePath, '--user'], { stdio: 'inherit' });
} catch (err) {
console.log('could not install pip');
throw err;
}
return join(PYTHONUSERBASE, 'bin', 'pip');
}

View File

@@ -9,20 +9,30 @@ import {
download,
glob,
createLambda,
shouldServe,
BuildOptions,
} from '@now/build-utils';
import { downloadAndInstallPip } from './download-and-install-pip';
async function pipInstall(pipPath: string, workDir: string, ...args: string[]) {
const target = '.';
console.log(`running "pip install --target ${target} --upgrade ${args.join(' ')}"...`);
console.log(
`running "pip install --target ${target} --upgrade ${args.join(' ')}"...`
);
try {
await execa(pipPath, ['install', '--target', target, '--upgrade', ...args], {
cwd: workDir,
stdio: 'inherit',
});
await execa(
pipPath,
['install', '--target', target, '--upgrade', ...args],
{
cwd: workDir,
stdio: 'inherit',
}
);
} catch (err) {
console.log(`failed to run "pip install --target ${target} --upgrade ${args.join(' ')}"...`);
console.log(
`failed to run "pip install --target ${target} --upgrade ${args.join(
' '
)}"...`
);
throw err;
}
}
@@ -42,11 +52,10 @@ async function pipInstallUser(pipPath: string, ...args: string[]) {
async function pipenvInstall(pyUserBase: string, srcDir: string) {
console.log('running "pipenv_to_requirements -f');
try {
await execa(
join(pyUserBase, 'bin', 'pipenv_to_requirements'),
['-f'],
{ cwd: srcDir, stdio: 'inherit' },
);
await execa(join(pyUserBase, 'bin', 'pipenv_to_requirements'), ['-f'], {
cwd: srcDir,
stdio: 'inherit',
});
} catch (err) {
console.log('failed to run "pipenv_to_requirements -f"');
throw err;
@@ -57,17 +66,18 @@ export const config = {
maxLambdaSize: '5mb',
};
export const build = async ({ workPath, files, entrypoint }: BuildOptions) => {
export const build = async ({
workPath,
files: originalFiles,
entrypoint,
meta = {},
}: BuildOptions) => {
console.log('downloading files...');
// eslint-disable-next-line no-param-reassign
files = await download(files, workPath);
// this is where `pip` will be installed to
// we need it to be under `/tmp`
const downloadedFiles = await download(originalFiles, workPath, meta);
const foundLockFile = 'Pipfile.lock' in downloadedFiles;
const pyUserBase = await getWriteableDirectory();
process.env.PYTHONUSERBASE = pyUserBase;
const pipPath = await downloadAndInstallPip();
const pipPath = 'pip3';
try {
// See: https://stackoverflow.com/a/44728772/376773
@@ -77,8 +87,10 @@ export const build = async ({ workPath, files, entrypoint }: BuildOptions) => {
//
// distutils.errors.DistutilsOptionError: must supply either home
// or prefix/exec-prefix -- not both
const setupCfg = join(workPath, 'setup.cfg');
await writeFile(setupCfg, '[install]\nprefix=\n');
if (meta.isDev) {
const setupCfg = join(workPath, 'setup.cfg');
await writeFile(setupCfg, '[install]\nprefix=\n');
}
} catch (err) {
console.log('failed to create "setup.cfg" file');
throw err;
@@ -87,10 +99,7 @@ export const build = async ({ workPath, files, entrypoint }: BuildOptions) => {
await pipInstall(pipPath, workPath, 'werkzeug');
await pipInstall(pipPath, workPath, 'requests');
const entryDirectory = dirname(entrypoint);
const requirementsTxt = join(entryDirectory, 'requirements.txt');
if (files['Pipfile.lock']) {
if (foundLockFile) {
console.log('found "Pipfile.lock"');
// Install pipenv.
@@ -100,6 +109,8 @@ export const build = async ({ workPath, files, entrypoint }: BuildOptions) => {
}
const fsFiles = await glob('**', workPath);
const entryDirectory = dirname(entrypoint);
const requirementsTxt = join(entryDirectory, 'requirements.txt');
if (fsFiles[requirementsTxt]) {
console.log('found local "requirements.txt"');
@@ -122,7 +133,7 @@ export const build = async ({ workPath, files, entrypoint }: BuildOptions) => {
.replace(/\.py$/, '');
const nowHandlerPyContents = originalNowHandlerPyContents.replace(
/__NOW_HANDLER_FILENAME/g,
userHandlerFilePath,
userHandlerFilePath
);
// in order to allow the user to have `server.py`, we need our `server.py` to be called
@@ -131,7 +142,7 @@ export const build = async ({ workPath, files, entrypoint }: BuildOptions) => {
await writeFile(
join(workPath, `${nowHandlerPyFilename}.py`),
nowHandlerPyContents,
nowHandlerPyContents
);
const lambda = await createLambda({
@@ -145,3 +156,5 @@ export const build = async ({ workPath, files, entrypoint }: BuildOptions) => {
[entrypoint]: lambda,
};
};
export { shouldServe };

View File

@@ -1,6 +1,6 @@
{
"name": "@now/python",
"version": "0.1.0",
"version": "0.2.2",
"main": "index.js",
"license": "MIT",
"repository": {

View File

@@ -8,6 +8,7 @@ const glob = require('@now/build-utils/fs/glob.js'); // eslint-disable-line impo
const { runShellScript } = require('@now/build-utils/fs/run-user-scripts.js'); // eslint-disable-line import/no-extraneous-dependencies
const FileFsRef = require('@now/build-utils/file-fs-ref.js'); // eslint-disable-line import/no-extraneous-dependencies
const FileRef = require('@now/build-utils/file-ref.js'); // eslint-disable-line import/no-extraneous-dependencies
const { shouldServe } = require('@now/build-utils'); // eslint-disable-line import/no-extraneous-dependencies
const installRust = require('./install-rust.js');
exports.config = {
@@ -253,10 +254,10 @@ async function buildSingleFile({
exports.build = async (m) => {
const {
files, entrypoint, workPath, config,
files, entrypoint, workPath, config, meta,
} = m;
console.log('downloading files');
const downloadedFiles = await download(files, workPath);
const downloadedFiles = await download(files, workPath, meta);
const entryPath = downloadedFiles[entrypoint].fsPath;
await installRust();
@@ -367,3 +368,5 @@ exports.getDefaultCache = ({ files, entrypoint }) => {
});
return { [targetFolderDir]: defaultCacheRef };
};
exports.shouldServe = shouldServe;

View File

@@ -1,6 +1,6 @@
{
"name": "@now/rust",
"version": "0.2.0",
"version": "0.2.4",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -15,7 +15,7 @@ function validateDistDir(distDir) {
const distDirName = path.basename(distDir);
if (!existsSync(distDir)) {
const message = `Build was unable to create the distDir: ${distDirName}.`
+ '\nMake sure you mentioned the correct dist directory: https://zeit.co/docs/v2/deployments/official-builders/static-build-now-static-build/#configuring-the-build-output-directory';
+ '\nMake sure you mentioned the correct dist directory: https://zeit.co/docs/v2/deployments/official-builders/static-build-now-static-build/#local-development';
throw new Error(message);
}
}
@@ -58,6 +58,7 @@ exports.build = async ({
devPort = await getPort();
nowDevScriptPorts.set(entrypoint, devPort);
const opts = {
cwd: entrypointFsDirname,
env: { ...process.env, PORT: String(devPort) },
};
const child = spawn('yarn', ['run', 'now-dev'], opts);
@@ -103,6 +104,12 @@ exports.build = async ({
dest: `http://localhost:${devPort}${srcBase}/$1`,
});
} else {
if (meta.isDev) {
console.log('WARN: "now-dev" script is missing from package.json');
console.log(
'See the local development docs: http://zeit.co/docs/v2/deployments/official-builders/static-now-static#local-development',
);
}
// Run the `now-build` script and wait for completion to collect the build
// outputs
console.log('running user "now-build" script from `package.json`...');

View File

@@ -1,6 +1,6 @@
{
"name": "@now/static-build",
"version": "0.5.2",
"version": "0.5.5",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@now/wordpress",
"version": "0.5.0",
"version": "0.5.2",
"license": "MIT",
"repository": {
"type": "git",
@@ -8,7 +8,7 @@
"directory": "packages/now-wordpress"
},
"dependencies": {
"@now/php-bridge": "^0.5.0",
"@now/php-bridge": "^0.5.2",
"node-fetch": "2.3.0",
"yauzl": "2.10.0"
},

View File

@@ -28,11 +28,27 @@ async function runBuildLambda(inputPath) {
entrypoint,
config: build.config,
});
console.log(analyzeResult);
const workPath = await getWritableDirectory();
const buildResult = await wrapper.build({
files: inputFiles, entrypoint, config: build.config, workPath,
files: inputFiles,
entrypoint,
config: build.config,
workPath,
});
const { output } = buildResult;
// Windows support
if (output) {
buildResult.output = Object.keys(output).reduce(
(result, path) => ({
...result,
[path.replace(/\\/g, '/')]: output[path],
}),
{},
);
}
return {
analyzeResult,
buildResult,

View File

@@ -940,6 +940,13 @@
dependencies:
"@babel/types" "^7.3.0"
"@types/cross-spawn@6.0.0":
version "6.0.0"
resolved "https://registry.yarnpkg.com/@types/cross-spawn/-/cross-spawn-6.0.0.tgz#320aaf1d1a12979f1b84fe7a5590a7e860bf3a80"
integrity sha512-evp2ZGsFw9YKprDbg8ySgC9NA15g3YgiI8ANkGmKKvvi0P2aDGYLPxQIC5qfeKNUOe3TjABVGuah6omPRpIYhg==
dependencies:
"@types/node" "*"
"@types/debug@^4.1.3":
version "4.1.4"
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.4.tgz#56eec47706f0fd0b7c694eae2f3172e6b0b769da"
@@ -1128,10 +1135,10 @@
globby "8.0.0"
signal-exit "3.0.2"
"@zeit/ncc@0.18.2":
version "0.18.2"
resolved "https://registry.yarnpkg.com/@zeit/ncc/-/ncc-0.18.2.tgz#b5f721ec1d23bfe531f3568633689ddab7c05638"
integrity sha512-liiuVTcxLaOIGQDftpZ2qhSS/vdEbuvmi2tkBWMfIwIyeKd/sh/jw+l8yONT3/unx/sSmfMTDnwfwUlY+saKiw==
"@zeit/ncc@0.18.5":
version "0.18.5"
resolved "https://registry.yarnpkg.com/@zeit/ncc/-/ncc-0.18.5.tgz#5687df6c32f1a2e2486aa110b3454ccebda4fb9c"
integrity sha512-F+SbvEAh8rchiRXqQbmD1UmbePY7dCOKTbvfFtbVbK2xMH/tyri5YKfNxXKK7eL9EWkkbqB3NTVQO6nokApeBA==
JSONStream@^1.0.4, JSONStream@^1.3.4:
version "1.3.5"
@@ -2534,16 +2541,7 @@ create-error-class@^3.0.1:
dependencies:
capture-stack-trace "^1.0.0"
cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
dependencies:
lru-cache "^4.0.1"
shebang-command "^1.2.0"
which "^1.2.9"
cross-spawn@^6.0.0, cross-spawn@^6.0.5:
cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
@@ -2554,6 +2552,15 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5:
shebang-command "^1.2.0"
which "^1.2.9"
cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
dependencies:
lru-cache "^4.0.1"
shebang-command "^1.2.0"
which "^1.2.9"
cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
version "0.3.4"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.4.tgz#8cd52e8a3acfd68d3aed38ee0a640177d2f9d797"