Compare commits

..

5 Commits

Author SHA1 Message Date
Igor Klopov
b7c47fa587 Publish
- @now/build-utils@0.4.29
 - @now/cgi@0.0.12
 - @now/go@0.2.9
 - @now/html-minifier@1.0.5
 - @now/lambda@0.4.6
 - @now/md@0.4.6
 - @now/mdx-deck@0.4.15
 - @now/next@0.0.78
 - @now/node-bridge@0.1.8
 - @now/node-server@0.4.23
 - @now/node@0.4.25
 - @now/optipng@0.4.6
 - @now/php@0.4.10
 - @now/python@0.0.39
 - @now/static-build@0.4.14
2018-11-18 04:44:36 +03:00
Tim Neutkens
f3bcefe822 @now/next exclude package-lock.json / yarn.lock from entry directory (#39)
* @now/next exclude package-lock.json / yarn.lock

* Add typescript to now-next

* Add download and file-fs-ref types

* Add types for now-next

* Remove yarn.lock
2018-11-17 23:10:14 +01:00
Tim Neutkens
7a481c9515 Make PR builds reliable again (#45)
* Run yarn prettier --write --single-quote packages/**/*.js and yarn lint --fix

* Fix tsc error

* Try and make publish-stable only trigger on tag
2018-11-17 22:53:59 +01:00
Igor Klopov
af275c60bf Publish
- @now/node-server@0.4.23-canary.4
 - @now/node@0.4.25-canary.4
2018-11-17 02:37:03 +03:00
Igor Klopov
ded377dfc2 [node] bump ncc 2018-11-17 02:34:51 +03:00
36 changed files with 735 additions and 180 deletions

View File

@@ -78,6 +78,8 @@ workflows:
filters:
tags:
only: /^.*(\d+\.)(\d+\.)(\*|\d+)$/
branches:
ignore: /.*/
# - publish-canary:
# requires:
# - build

View File

@@ -5,6 +5,20 @@
"import/no-unresolved": 0,
"import/no-dynamic-require": 0,
"global-require": 0
}
},
"overrides": [
{
"files": ["test/**"],
"rules": {
"import/no-extraneous-dependencies": 0
},
"globals": {
"describe": true,
"it": true,
"test": true,
"expect": true
}
}
]
}

View File

@@ -1,4 +1,5 @@
module.exports = {
testEnvironment: 'node',
rootDir: 'test',
collectCoverageFrom: ['packages/**/*.{js,jsx}', '!**/node_modules/**'],
};

View File

@@ -26,6 +26,8 @@
]
},
"devDependencies": {
"@types/fs-extra": "^5.0.4",
"@types/glob": "^7.1.1",
"@types/multistream": "^2.1.1",
"@types/node": "^10.12.8",
"eslint": "^5.9.0",

View File

@@ -1,20 +1,38 @@
const assert = require('assert');
const fs = require('fs-extra');
const MultiStream = require('multistream');
const multiStream = require('multistream');
const path = require('path');
const Sema = require('async-sema');
/** @typedef {{[filePath: string]: FileFsRef}} FsFiles */
const semaToPreventEMFILE = new Sema(30);
/**
* @constructor
* @argument {Object} options
* @argument {number} [options.mode=0o100644]
* @argument {string} options.fsPath
*/
class FileFsRef {
constructor({ mode = 0o100644, fsPath }) {
assert(typeof mode === 'number');
assert(typeof fsPath === 'string');
/** @type {string} */
this.type = 'FileFsRef';
/** @type {number} */
this.mode = mode;
/** @type {string} */
this.fsPath = fsPath;
}
/**
* @argument {Object} options
* @argument {number} [options.mode=0o100644]
* @argument {NodeJS.ReadableStream} options.stream
* @argument {string} options.fsPath
* @returns {Promise<FileFsRef>}
*/
static async fromStream({ mode = 0o100644, stream, fsPath }) {
assert(typeof mode === 'number');
assert(typeof stream.pipe === 'function'); // is-stream
@@ -33,6 +51,9 @@ class FileFsRef {
return new FileFsRef({ mode, fsPath });
}
/**
* @returns {Promise<NodeJS.ReadableStream>}
*/
async toStreamAsync() {
await semaToPreventEMFILE.acquire();
const release = () => semaToPreventEMFILE.release();
@@ -42,19 +63,24 @@ class FileFsRef {
return stream;
}
/**
* @returns {NodeJS.ReadableStream}
*/
toStream() {
let flag;
// eslint-disable-next-line consistent-return
return new MultiStream((cb) => {
if (flag) return cb();
return multiStream((cb) => {
if (flag) return cb(null, null);
flag = true;
this.toStreamAsync().then((stream) => {
cb(undefined, stream);
}).catch((error) => {
cb(error);
});
this.toStreamAsync()
.then((stream) => {
cb(null, stream);
})
.catch((error) => {
cb(error, null);
});
});
}
}

View File

@@ -4,6 +4,8 @@ const multiStream = require('multistream');
const retry = require('async-retry');
const Sema = require('async-sema');
/** @typedef {{[filePath: string]: FileRef}} Files */
const semaToDownloadFromS3 = new Sema(10);
class BailableError extends Error {

View File

@@ -1,20 +1,38 @@
const path = require('path');
const FileFsRef = require('../file-fs-ref.js');
/** @typedef {import('../file-ref')} FileRef */
/** @typedef {import('../file-fs-ref')} FileFsRef */
/** @typedef {{[filePath: string]: FileRef|FileFsRef}} Files */
/** @typedef {{[filePath: string]: FileFsRef}|{}} DownloadedFiles */
/**
* @param {FileRef|FileFsRef} file
* @param {string} fsPath
* @returns {Promise<FileFsRef>}
*/
async function downloadFile(file, fsPath) {
const { mode } = file;
const stream = file.toStream();
return FileFsRef.fromStream({ mode, stream, fsPath });
}
/**
* Download files to disk
* @argument {Files} files
* @argument {string} basePath
* @returns {Promise<DownloadedFiles>}
*/
module.exports = async function download(files, basePath) {
const files2 = {};
await Promise.all(Object.keys(files).map(async (name) => {
const file = files[name];
const fsPath = path.join(basePath, name);
files2[name] = await downloadFile(file, fsPath);
}));
await Promise.all(
Object.keys(files).map(async (name) => {
const file = files[name];
const fsPath = path.join(basePath, name);
files2[name] = await downloadFile(file, fsPath);
}),
);
return files2;
};

View File

@@ -3,15 +3,30 @@ const path = require('path');
const vanillaGlob = require('glob');
const FileFsRef = require('../file-fs-ref.js');
/** @typedef {import('fs').Stats} Stats */
/** @typedef {import('glob').IOptions} GlobOptions */
/** @typedef {import('../file-fs-ref').FsFiles|{}} GlobFiles */
/**
* @argument {string} pattern
* @argument {GlobOptions|string} opts
* @argument {string} [mountpoint]
* @returns {Promise<GlobFiles>}
*/
module.exports = function glob(pattern, opts = {}, mountpoint) {
return new Promise((resolve, reject) => {
let options = opts;
if (typeof options === 'string') {
options = { cwd: options };
/** @type {GlobOptions} */
let options;
if (typeof opts === 'string') {
options = { cwd: opts };
} else {
options = opts;
}
if (!options.cwd) {
throw new Error('Second argument (basePath) must be specified for names of resulting files');
throw new Error(
'Second argument (basePath) must be specified for names of resulting files',
);
}
if (!path.isAbsolute(options.cwd)) {
@@ -26,21 +41,27 @@ module.exports = function glob(pattern, opts = {}, mountpoint) {
vanillaGlob(pattern, options, (error, files) => {
if (error) return reject(error);
resolve(files.reduce((files2, relativePath) => {
const fsPath = path.join(options.cwd, relativePath);
const stat = options.statCache[fsPath];
assert(stat, `statCache does not contain value for ${relativePath} (resolved to ${fsPath})`);
if (stat.isFile()) {
let finalPath = relativePath;
if (mountpoint) finalPath = path.join(mountpoint, finalPath);
return {
...files2,
[finalPath]: new FileFsRef({ mode: stat.mode, fsPath }),
};
}
resolve(
files.reduce((files2, relativePath) => {
const fsPath = path.join(options.cwd, relativePath);
/** @type {Stats|any} */
const stat = options.statCache[fsPath];
assert(
stat,
`statCache does not contain value for ${relativePath} (resolved to ${fsPath})`,
);
if (stat && stat.isFile()) {
let finalPath = relativePath;
if (mountpoint) finalPath = path.join(mountpoint, finalPath);
return {
...files2,
[finalPath]: new FileFsRef({ mode: stat.mode, fsPath }),
};
}
return files2;
}, {}));
return files2;
}, {}),
);
});
});
};

View File

@@ -1,6 +1,25 @@
/** @typedef { import('@now/build-utils/file-ref') } FileRef */
/** @typedef { import('@now/build-utils/file-fs-ref') } FileFsRef */
/** @typedef {{[filePath: string]: FileRef|FileFsRef}} Files */
/**
* @callback delegate
* @argument {string} name
* @returns {string}
*/
/**
* Rename files using delegate function
* @argument {Files} files
* @argument {delegate} delegate
* @returns {Files}
*/
module.exports = function rename(files, delegate) {
return Object.keys(files).reduce((newFiles, name) => ({
...newFiles,
[delegate(name)]: files[name],
}), {});
return Object.keys(files).reduce(
(newFiles, name) => ({
...newFiles,
[delegate(name)]: files[name],
}),
{},
);
};

View File

@@ -25,16 +25,21 @@ async function createLambda({
assert(typeof environment === 'object', '"environment" is not an object');
const zipFile = new ZipFile();
Object.keys(files).sort().forEach((name) => {
const file = files[name];
const stream = file.toStream();
zipFile.addReadStream(stream, name, { mode: file.mode, mtime });
});
Object.keys(files)
.sort()
.forEach((name) => {
const file = files[name];
const stream = file.toStream();
zipFile.addReadStream(stream, name, { mode: file.mode, mtime });
});
zipFile.end();
const zipBuffer = await streamToBuffer(zipFile.outputStream);
return new Lambda({
zipBuffer, handler, runtime, environment,
zipBuffer,
handler,
runtime,
environment,
});
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/build-utils",
"version": "0.4.29-canary.2",
"version": "0.4.29",
"dependencies": {
"async-retry": "1.2.3",
"async-sema": "2.1.4",

View File

@@ -1,6 +1,6 @@
{
"name": "@now/cgi",
"version": "0.0.12-canary.1",
"version": "0.0.12",
"scripts": {
"test": "best -I test/*.js",
"prepublish": "./build.sh"

View File

@@ -1,6 +1,6 @@
{
"name": "@now/go",
"version": "0.2.9-canary.1",
"version": "0.2.9",
"scripts": {
"test": "best -I test/*.js",
"prepublish": "./build.sh"

View File

@@ -1,6 +1,6 @@
{
"name": "@now/html-minifier",
"version": "1.0.5-canary.0",
"version": "1.0.5",
"dependencies": {
"html-minifier": "3.5.21"
},

View File

@@ -1,6 +1,6 @@
{
"name": "@now/lambda",
"version": "0.4.6-canary.0",
"version": "0.4.6",
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}

View File

@@ -22,12 +22,17 @@ exports.build = async ({ files, entrypoint, config }) => {
.use(markdown)
.use(remark2rehype)
.use(doc, {
title, language, meta, css,
title,
language,
meta,
css,
})
.use(format)
.use(html);
const result = await FileBlob.fromStream({ stream: stream.pipe(unifiedStream(processor)) });
const result = await FileBlob.fromStream({
stream: stream.pipe(unifiedStream(processor)),
});
console.log(result.data.toString());

View File

@@ -1,6 +1,6 @@
{
"name": "@now/md",
"version": "0.4.6-canary.0",
"version": "0.4.6",
"dependencies": {
"rehype-document": "^2.2.0",
"rehype-format": "^2.3.0",

View File

@@ -19,13 +19,19 @@ exports.build = async ({ files, entrypoint, workPath }) => {
await writeFile(packageJsonPath, JSON.stringify(packageJson));
console.log('running npm install...');
process.env.PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = '1'; // TODO opts argument for runNpmInstall
await runNpmInstall(path.dirname(packageJsonPath), ['--prod', '--prefer-offline']);
await runNpmInstall(path.dirname(packageJsonPath), [
'--prod',
'--prefer-offline',
]);
console.log('building...');
const outDir = await getWritableDirectory();
const entrypointFsPath = downloadedFiles[entrypoint].fsPath;
const mountpoint = path.dirname(entrypoint);
const build = require(path.join(workPath, 'node_modules/mdx-deck/lib/build.js'));
const build = require(path.join(
workPath,
'node_modules/mdx-deck/lib/build.js',
));
await build({
html: true,
@@ -48,8 +54,8 @@ exports.prepareCache = async ({ cachePath }) => {
await runNpmInstall(path.dirname(packageJsonPath), ['--prod']);
return {
...await glob('node_modules/**', cachePath),
...await glob('package-lock.json', cachePath),
...await glob('yarn.lock', cachePath),
...(await glob('node_modules/**', cachePath)),
...(await glob('package-lock.json', cachePath)),
...(await glob('yarn.lock', cachePath)),
};
};

View File

@@ -1,6 +1,6 @@
{
"name": "@now/mdx-deck",
"version": "0.4.15-canary.0",
"version": "0.4.15",
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}

View File

@@ -4,128 +4,100 @@ const FileFsRef = require('@now/build-utils/file-fs-ref.js');
const FileBlob = require('@now/build-utils/file-blob');
const path = require('path');
const { readFile, writeFile, unlink } = require('fs.promised');
const rename = require('@now/build-utils/fs/rename.js');
const {
runNpmInstall,
runPackageJsonScript,
} = require('@now/build-utils/fs/run-user-scripts.js');
const glob = require('@now/build-utils/fs/glob.js');
const {
excludeFiles,
validateEntrypoint,
includeOnlyEntryDirectory,
moveEntryDirectoryToRoot,
excludeLockFiles,
normalizePackageJson,
excludeStaticDirectory,
} = require('./utils');
// Exclude certain files from the files object
function excludeFiles(files, matchFn) {
return Object.keys(files).reduce((newFiles, fileName) => {
if (matchFn(fileName)) {
return newFiles;
}
return {
...newFiles,
[fileName]: files[fileName],
};
}, {});
/** @typedef { import('@now/build-utils/file-ref').Files } Files */
/** @typedef { import('@now/build-utils/fs/download').DownloadedFiles } DownloadedFiles */
/**
* @typedef {Object} BuildParamsType
* @property {Files} files - Files object
* @property {string} entrypoint - Entrypoint specified for the builder
* @property {string} workPath - Working directory for this build
*/
/**
* Read package.json from files
* @param {DownloadedFiles} files
*/
async function readPackageJson(files) {
if (!files['package.json']) {
return {};
}
const packageJsonPath = files['package.json'].fsPath;
return JSON.parse(await readFile(packageJsonPath, 'utf8'));
}
function shouldExcludeFile(entryDirectory) {
return (file) => {
// If the file is not in the entry directory
if (entryDirectory !== '.' && !file.startsWith(entryDirectory)) {
return true;
}
// Exclude static directory
if (file.startsWith(path.join(entryDirectory, 'static'))) {
return true;
}
if (file === 'package-lock.json') {
return true;
}
if (file === 'yarn.lock') {
return true;
}
return false;
};
}
exports.build = async ({ files, workPath, entrypoint }) => {
if (
!/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"',
);
}
console.log('downloading user files...');
const entryDirectory = path.dirname(entrypoint);
const filesToDownload = excludeFiles(
files,
shouldExcludeFile(entryDirectory),
);
const entrypointHandledFilesToDownload = rename(filesToDownload, (file) => {
if (entryDirectory !== '.') {
return file.replace(new RegExp(`^${entryDirectory}/`), '');
}
return file;
});
let downloadedFiles = await download(
entrypointHandledFilesToDownload,
workPath,
);
let packageJson = {};
if (downloadedFiles['package.json']) {
console.log('found package.json, overwriting');
const packageJsonPath = downloadedFiles['package.json'].fsPath;
packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8'));
}
packageJson = {
...packageJson,
dependencies: {
...packageJson.dependencies,
'next-server': 'canary',
},
devDependencies: {
...packageJson.devDependencies,
next: 'canary',
},
scripts: {
...packageJson.scripts,
'now-build': 'next build',
},
};
if (!packageJson.dependencies.react) {
console.log(
'"react" not found in dependencies, adding to "package.json" "dependencies"',
);
packageJson.dependencies.react = 'latest';
}
if (!packageJson.dependencies['react-dom']) {
console.log(
'"react-dom" not found in dependencies, adding to "package.json" "dependencies"',
);
packageJson.dependencies['react-dom'] = 'latest';
}
// in case the user has `next` on their `dependencies`, we remove it
delete packageJson.dependencies.next;
/**
* Write package.json
* @param {string} workPath
* @param {Object} packageJson
*/
async function writePackageJson(workPath, packageJson) {
await writeFile(
path.join(workPath, 'package.json'),
JSON.stringify(packageJson, null, 2),
);
}
/**
* Write .npmrc with npm auth token
* @param {string} workPath
* @param {string} token
*/
async function writeNpmRc(workPath, token) {
await writeFile(
path.join(workPath, '.npmrc'),
`//registry.npmjs.org/:_authToken=${token}`,
);
}
/**
* @param {BuildParamsType} buildParams
* @returns {Promise<Files>}
*/
exports.build = async ({ files, workPath, entrypoint }) => {
validateEntrypoint(entrypoint);
console.log('downloading user files...');
const entryDirectory = path.dirname(entrypoint);
const filesOnlyEntryDirectory = includeOnlyEntryDirectory(
files,
entryDirectory,
);
const filesWithEntryDirectoryRoot = moveEntryDirectoryToRoot(
filesOnlyEntryDirectory,
entryDirectory,
);
const filesWithoutLockfiles = excludeLockFiles(filesWithEntryDirectoryRoot);
const filesWithoutStaticDirectory = excludeStaticDirectory(
filesWithoutLockfiles,
);
let downloadedFiles = await download(filesWithoutStaticDirectory, workPath);
console.log('normalizing package.json');
const packageJson = normalizePackageJson(readPackageJson(downloadedFiles));
await writePackageJson(workPath, packageJson);
if (process.env.NPM_AUTH_TOKEN) {
console.log('found NPM_AUTH_TOKEN in environment, creating .npmrc');
await writeFile(
path.join(workPath, '.npmrc'),
`//registry.npmjs.org/:_authToken=${process.env.NPM_AUTH_TOKEN}`,
);
await writeNpmRc(workPath, process.env.NPM_AUTH_TOKEN);
}
downloadedFiles = await glob('**', workPath);
console.log('running npm install...');
@@ -137,6 +109,7 @@ exports.build = async ({ files, workPath, entrypoint }) => {
if (process.env.NPM_AUTH_TOKEN) {
await unlink(path.join(workPath, '.npmrc'));
}
downloadedFiles = await glob('**', workPath);
console.log('preparing lambda files...');
@@ -203,6 +176,7 @@ exports.build = async ({ files, workPath, entrypoint }) => {
],
};
console.log(`Creating lambda for page: "${page}"...`);
lambdas[path.join(entryDirectory, pathname)] = await createLambda({
files: {
...nextFiles,
@@ -212,6 +186,7 @@ exports.build = async ({ files, workPath, entrypoint }) => {
handler: 'now__launcher.launcher',
runtime: 'nodejs8.10',
});
console.log(`Created lambda for page: "${page}"`);
}),
);

View File

@@ -1,6 +1,6 @@
{
"name": "@now/next",
"version": "0.0.78-canary.1",
"version": "0.0.78",
"dependencies": {
"@now/node-bridge": "0.1.4",
"execa": "^1.0.0",

151
packages/now-next/utils.js Normal file
View File

@@ -0,0 +1,151 @@
const rename = require('@now/build-utils/fs/rename.js');
/** @typedef { import('@now/build-utils/file-ref') } FileRef */
/** @typedef { import('@now/build-utils/file-fs-ref') } FileFsRef */
/** @typedef {{[filePath: string]: FileRef|FileFsRef}} Files */
/**
* Validate if the entrypoint is allowed to be used
* @param {string} entrypoint
* @throws {Error}
*/
function validateEntrypoint(entrypoint) {
if (
!/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"',
);
}
}
/**
* This callback type is called `requestCallback` and is displayed as a global symbol.
*
* @callback matcher
* @param {string} filePath
* @returns {boolean}
*/
/**
* Exclude certain files from the files object
* @param {Files} files
* @param {matcher} matcher
* @returns {Files}
*/
function excludeFiles(files, matcher) {
return Object.keys(files).reduce((newFiles, filePath) => {
if (matcher(filePath)) {
return newFiles;
}
return {
...newFiles,
[filePath]: files[filePath],
};
}, {});
}
/**
* Creates a new Files object holding only the entrypoint files
* @param {Files} files
* @param {string} entryDirectory
* @returns {Files}
*/
function includeOnlyEntryDirectory(files, entryDirectory) {
if (entryDirectory === '.') {
return files;
}
function matcher(filePath) {
return !filePath.startsWith(entryDirectory);
}
return excludeFiles(files, matcher);
}
/**
* Moves all files under the entry directory to the root directory
* @param {Files} files
* @param {string} entryDirectory
* @returns {Files}
*/
function moveEntryDirectoryToRoot(files, entryDirectory) {
if (entryDirectory === '.') {
return files;
}
function delegate(filePath) {
return filePath.replace(new RegExp(`^${entryDirectory}/`), '');
}
return rename(files, delegate);
}
/**
* Exclude package manager lockfiles from files
* @param {Files} files
* @returns {Files}
*/
function excludeLockFiles(files) {
const newFiles = files;
if (newFiles['package-lock.json']) {
delete newFiles['package-lock.json'];
}
if (newFiles['yarn.lock']) {
delete newFiles['yarn.lock'];
}
return files;
}
/**
* Exclude the static directory from files
* @param {Files} files
* @returns {Files}
*/
function excludeStaticDirectory(files) {
function matcher(filePath) {
return filePath.startsWith('static');
}
return excludeFiles(files, matcher);
}
/**
* Enforce specific package.json configuration for smallest possible lambda
* @param {Object} defaultPackageJson
*/
function normalizePackageJson(defaultPackageJson = {}) {
return {
...defaultPackageJson,
dependencies: {
// react and react-dom can be overwritten
react: 'latest',
'react-dom': 'latest',
...defaultPackageJson.dependencies,
// next-server is forced to canary
'next-server': 'canary',
next: undefined,
},
devDependencies: {
...defaultPackageJson.devDependencies,
// next is forced to canary
next: 'canary',
'next-server': undefined,
},
scripts: {
...defaultPackageJson.scripts,
'now-build': 'next build',
},
};
}
module.exports = {
excludeFiles,
validateEntrypoint,
includeOnlyEntryDirectory,
moveEntryDirectoryToRoot,
excludeLockFiles,
normalizePackageJson,
excludeStaticDirectory,
};

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node-bridge",
"version": "0.1.8-canary.1",
"version": "0.1.8",
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}

View File

@@ -37,7 +37,7 @@ async function commonForTwo({
'package.json': new FileBlob({
data: JSON.stringify({
dependencies: {
'@zeit/ncc': '0.1.3-webpack',
'@zeit/ncc': '0.1.4-webpack',
},
}),
}),

View File

@@ -1,8 +1,8 @@
{
"name": "@now/node-server",
"version": "0.4.23-canary.3",
"version": "0.4.23",
"dependencies": {
"@now/node-bridge": "^0.1.8-canary.1",
"@now/node-bridge": "^0.1.8",
"fs-extra": "7.0.1"
},
"peerDependencies": {

View File

@@ -2,7 +2,8 @@ const { createLambda } = require('@now/build-utils/lambda.js');
const download = require('@now/build-utils/fs/download.js');
const FileBlob = require('@now/build-utils/file-blob.js');
const FileFsRef = require('@now/build-utils/file-fs-ref.js');
const fs = require('fs-extra');
const fsExtra = require('fs-extra');
const fs = require('fs');
const glob = require('@now/build-utils/fs/glob.js');
const path = require('path');
const { promisify } = require('util');
@@ -48,7 +49,7 @@ async function downloadInstallAndBundle(
'package.json': new FileBlob({
data: JSON.stringify({
dependencies: {
'@zeit/ncc': '0.1.3-webpack',
'@zeit/ncc': '0.1.4-webpack',
},
}),
}),
@@ -122,7 +123,7 @@ exports.build = async ({ files, entrypoint, workPath }) => {
exports.prepareCache = async ({
files, entrypoint, workPath, cachePath,
}) => {
await fs.remove(workPath);
await fsExtra.remove(workPath);
await downloadInstallAndBundle({ files, entrypoint, workPath: cachePath });
return {

View File

@@ -1,8 +1,8 @@
{
"name": "@now/node",
"version": "0.4.25-canary.3",
"version": "0.4.25",
"dependencies": {
"@now/node-bridge": "^0.1.8-canary.1",
"@now/node-bridge": "^0.1.8",
"fs-extra": "7.0.1"
},
"peerDependencies": {

View File

@@ -6,7 +6,10 @@ exports.analyze = ({ files, entrypoint }) => files[entrypoint].digest;
exports.build = async ({ files, entrypoint }) => {
const optimizer = new OptiPng(['-o9']);
const stream = pipe(files[entrypoint].toStream(), optimizer);
const stream = pipe(
files[entrypoint].toStream(),
optimizer,
);
const result = await FileBlob.fromStream({ stream });
return { [entrypoint]: result };
};

View File

@@ -1,6 +1,6 @@
{
"name": "@now/optipng",
"version": "0.4.6-canary.0",
"version": "0.4.6",
"dependencies": {
"multipipe": "2.0.3",
"optipng": "1.1.0"

View File

@@ -1,6 +1,6 @@
{
"name": "@now/php",
"version": "0.4.10-canary.1",
"version": "0.4.10",
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}

View File

@@ -37,7 +37,9 @@ async function downloadAndInstallPip() {
// 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');
throw new Error(
'Could not install "pip": "PYTHONUSERBASE" env var is not set',
);
}
const getPipFilePath = await downloadGetPipScript();

View File

@@ -1,6 +1,6 @@
{
"name": "@now/python",
"version": "0.0.39-canary.1",
"version": "0.0.39",
"main": "index.js",
"license": "MIT",
"dependencies": {

View File

@@ -2,7 +2,9 @@ const download = require('@now/build-utils/fs/download.js');
const glob = require('@now/build-utils/fs/glob.js');
const path = require('path');
const {
runNpmInstall, runPackageJsonScript, runShellScript,
runNpmInstall,
runPackageJsonScript,
runShellScript,
} = require('@now/build-utils/fs/run-user-scripts.js');
exports.build = async ({

View File

@@ -1,6 +1,6 @@
{
"name": "@now/static-build",
"version": "0.4.14-canary.0",
"version": "0.4.14",
"peerDependencies": {
"@now/build-utils": ">=0.0.1"
}

View File

@@ -0,0 +1,292 @@
const {
excludeFiles,
validateEntrypoint,
includeOnlyEntryDirectory,
moveEntryDirectoryToRoot,
excludeLockFiles,
normalizePackageJson,
excludeStaticDirectory,
} = require('@now/next/utils');
const FileRef = require('@now/build-utils/file-ref');
describe('excludeFiles', () => {
it('should exclude files', () => {
const files = {
'pages/index.js': new FileRef({ digest: 'index' }),
'package.json': new FileRef({ digest: 'package' }),
'package-lock.json': new FileRef({ digest: 'package-lock' }),
};
const result = excludeFiles(
files,
filePath => filePath === 'package-lock.json',
);
expect(result['pages/index.js']).toBeDefined();
expect(result['package.json']).toBeDefined();
expect(result['package-lock.json']).toBeUndefined();
});
});
describe('validateEntrypoint', () => {
it('should allow package.json', () => {
expect(validateEntrypoint('package.json')).toBeUndefined();
});
it('should allow nested package.json', () => {
expect(validateEntrypoint('frontend/package.json')).toBeUndefined();
});
it('should allow next.config.js', () => {
expect(validateEntrypoint('next.config.js')).toBeUndefined();
});
it('should allow nested next.config.js', () => {
expect(validateEntrypoint('frontend/next.config.js')).toBeUndefined();
});
it('should not allow pages/index.js', () => {
expect(() => validateEntrypoint('pages/index.js')).toThrow();
});
});
describe('includeOnlyEntryDirectory', () => {
it('should exclude 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();
});
it('should handle entry directory being dot', () => {
const entryDirectory = '.';
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']).toBeDefined();
expect(result['package-lock.json']).toBeDefined();
});
});
describe('moveEntryDirectoryToRoot', () => {
it('should move entrydirectory files to the root', () => {
const entryDirectory = 'frontend';
const files = {
'frontend/pages/index.js': new FileRef({ digest: 'index' }),
};
const result = moveEntryDirectoryToRoot(files, entryDirectory);
expect(result['pages/index.js']).toBeDefined();
});
it('should work with deep nested subdirectories', () => {
const entryDirectory = 'frontend/my/app';
const files = {
'frontend/my/app/pages/index.js': new FileRef({ digest: 'index' }),
};
const result = moveEntryDirectoryToRoot(files, entryDirectory);
expect(result['pages/index.js']).toBeDefined();
});
it('should do nothing when entry directory is dot', () => {
const entryDirectory = '.';
const files = {
'frontend/pages/index.js': new FileRef({ digest: 'index' }),
};
const result = moveEntryDirectoryToRoot(files, entryDirectory);
expect(result['frontend/pages/index.js']).toBeDefined();
});
});
describe('excludeLockFiles', () => {
it('should remove package-lock.json', () => {
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 = excludeLockFiles(files);
expect(result['frontend/pages/index.js']).toBeDefined();
expect(result['package-lock.json']).toBeUndefined();
});
it('should remove yarn.lock', () => {
const files = {
'frontend/pages/index.js': new FileRef({ digest: 'index' }),
'package.json': new FileRef({ digest: 'package' }),
'yarn.lock': new FileRef({ digest: 'yarn-lock' }),
};
const result = excludeLockFiles(files);
expect(result['frontend/pages/index.js']).toBeDefined();
expect(result['yarn.lock']).toBeUndefined();
});
it('should remove both package-lock.json and yarn.lock', () => {
const files = {
'frontend/pages/index.js': new FileRef({ digest: 'index' }),
'package.json': new FileRef({ digest: 'package' }),
'yarn.lock': new FileRef({ digest: 'yarn-lock' }),
'package-lock.json': new FileRef({ digest: 'package-lock' }),
};
const result = excludeLockFiles(files);
expect(result['frontend/pages/index.js']).toBeDefined();
expect(result['yarn.lock']).toBeUndefined();
expect(result['package-lock.json']).toBeUndefined();
});
});
describe('excludeStaticDirectory', () => {
it('should remove the /static directory files', () => {
const files = {
'frontend/pages/index.js': new FileRef({ digest: 'index' }),
'package.json': new FileRef({ digest: 'package' }),
'yarn.lock': new FileRef({ digest: 'yarn-lock' }),
'package-lock.json': new FileRef({ digest: 'package-lock' }),
'static/image.png': new FileRef({ digest: 'image' }),
};
const result = excludeStaticDirectory(files);
expect(result['frontend/pages/index.js']).toBeDefined();
expect(result['yarn.lock']).toBeDefined();
expect(result['package-lock.json']).toBeDefined();
expect(result['static/image.png']).toBeUndefined();
});
it('should remove the nested /static directory files', () => {
const files = {
'frontend/pages/index.js': new FileRef({ digest: 'index' }),
'package.json': new FileRef({ digest: 'package' }),
'yarn.lock': new FileRef({ digest: 'yarn-lock' }),
'package-lock.json': new FileRef({ digest: 'package-lock' }),
'static/images/png/image.png': new FileRef({ digest: 'image' }),
};
const result = excludeStaticDirectory(files);
expect(result['frontend/pages/index.js']).toBeDefined();
expect(result['yarn.lock']).toBeDefined();
expect(result['package-lock.json']).toBeDefined();
expect(result['static/images/png/image.png']).toBeUndefined();
});
});
describe('normalizePackageJson', () => {
it('should work without a package.json being supplied', () => {
const result = normalizePackageJson();
expect(result).toEqual({
dependencies: {
'next-server': 'canary',
react: 'latest',
'react-dom': 'latest',
},
devDependencies: {
next: 'canary',
},
scripts: {
'now-build': 'next build',
},
});
});
it('should work with a package.json being supplied', () => {
const defaultPackage = {
dependencies: {
'next-server': 'canary',
react: 'latest',
'react-dom': 'latest',
},
devDependencies: {
next: 'canary',
},
scripts: {
'now-build': 'next build',
},
};
const result = normalizePackageJson(defaultPackage);
expect(result).toEqual({
dependencies: {
'next-server': 'canary',
react: 'latest',
'react-dom': 'latest',
},
devDependencies: {
next: 'canary',
},
scripts: {
'now-build': 'next build',
},
});
});
it('should force next@canary to be a devDependency', () => {
const defaultPackage = {
dependencies: {
react: 'latest',
'react-dom': 'latest',
next: 'latest',
},
};
const result = normalizePackageJson(defaultPackage);
expect(result).toEqual({
dependencies: {
'next-server': 'canary',
react: 'latest',
'react-dom': 'latest',
},
devDependencies: {
next: 'canary',
},
scripts: {
'now-build': 'next build',
},
});
});
it('should force next-server@canary to be a dependency', () => {
const defaultPackage = {
dependencies: {
react: 'latest',
'react-dom': 'latest',
next: 'latest',
},
};
const result = normalizePackageJson(defaultPackage);
expect(result).toEqual({
dependencies: {
'next-server': 'canary',
react: 'latest',
'react-dom': 'latest',
},
devDependencies: {
next: 'canary',
},
scripts: {
'now-build': 'next build',
},
});
});
it('should force now-build script', () => {
const defaultPackage = {
dependencies: {
react: 'latest',
'react-dom': 'latest',
next: 'latest',
},
};
const result = normalizePackageJson(defaultPackage);
expect(result).toEqual({
dependencies: {
'next-server': 'canary',
react: 'latest',
'react-dom': 'latest',
},
devDependencies: {
next: 'canary',
},
scripts: {
'now-build': 'next build',
},
});
});
});

View File

@@ -12,6 +12,14 @@
},
"include": [
"./packages/now-node/index.js",
"./packages/now-build-utils/file-ref.js"
"./packages/now-build-utils/file-ref.js",
"./packages/now-build-utils/file-fs-ref.js",
"./packages/now-build-utils/fs/rename.js",
"./packages/now-build-utils/fs/download.js",
"./packages/now-build-utils/fs/glob.js",
"./packages/now-next"
],
"exclude": [
"./packages/now-next/launcher.js"
]
}