[next][node][static-build] Execute "vercel-build" script if defined (#4863)

* [build-utils] Make `runPackageJsonScript()` run the `vercel-` or `now-` if defined in `package.json`

* [build-utils] Export `getScriptName()`

* [next] Use `getScriptName()` and `remove()`

* [node] Update for `vercel-`

* [static-build] Update for `vercel-`

* Remove debug

* Add `getScriptName()` unit tests

* Test for `vercel-build` in e2e

* Make platform name behavior be opt-in

So that it's not a breaking behavior change.

* Check for only "vercel-build" or "now-build", but not "build"

* Simplify `getScriptName()` to return the first existing script in a possible set

* Revert change

* Fix test

Co-authored-by: Joe Haddad <joe.haddad@zeit.co>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
Nathan Rajlich
2020-07-21 20:04:24 -07:00
committed by GitHub
parent a1d548dfef
commit fadc3f2588
8 changed files with 48 additions and 64 deletions

View File

@@ -7,6 +7,7 @@ const {
getLambdaOptionsFromFunction,
getNodeVersion,
getSpawnOptions,
getScriptName,
glob,
runNpmInstall,
runPackageJsonScript,
@@ -35,13 +36,7 @@ import {
import { nodeFileTrace, NodeFileTraceReasons } from '@zeit/node-file-trace';
import { ChildProcess, fork } from 'child_process';
import escapeStringRegexp from 'escape-string-regexp';
import {
lstat,
pathExists,
readFile,
unlink as unlinkFile,
writeFile,
} from 'fs-extra';
import { lstat, pathExists, readFile, remove, writeFile } from 'fs-extra';
import os from 'os';
import path from 'path';
import resolveFrom from 'resolve-from';
@@ -108,13 +103,7 @@ const MAX_AGE_ONE_YEAR = 31536000;
/**
* Read package.json from files
*/
async function readPackageJson(
entryPath: string
): Promise<{
scripts?: { [key: string]: string };
dependencies?: { [key: string]: string };
devDependencies?: { [key: string]: string };
}> {
async function readPackageJson(entryPath: string): Promise<PackageJson> {
const packagePath = path.join(entryPath, 'package.json');
try {
@@ -329,49 +318,38 @@ export const build = async ({
}
const isLegacy = nextVersionRange && isLegacyNext(nextVersionRange);
let shouldRunScript = 'now-build';
debug(`MODE: ${isLegacy ? 'legacy' : 'serverless'}`);
if (isLegacy) {
try {
await unlinkFile(path.join(entryPath, 'yarn.lock'));
} catch (err) {
debug('no yarn.lock removed');
}
try {
await unlinkFile(path.join(entryPath, 'package-lock.json'));
} catch (err) {
debug('no package-lock.json removed');
}
console.warn(
"WARNING: your application is being deployed in @vercel/next's legacy mode. http://err.sh/vercel/vercel/now-next-legacy-mode"
);
await Promise.all([
remove(path.join(entryPath, 'yarn.lock')),
remove(path.join(entryPath, 'package-lock.json')),
]);
debug('Normalizing package.json');
const packageJson = normalizePackageJson(pkg);
debug('Normalized package.json result: ', packageJson);
await writePackageJson(entryPath, packageJson);
} else if (pkg.scripts && pkg.scripts['now-build']) {
debug('Found user `now-build` script');
shouldRunScript = 'now-build';
} else if (pkg.scripts && pkg.scripts['build']) {
debug('Found user `build` script');
shouldRunScript = 'build';
} else if (!pkg.scripts || !pkg.scripts['now-build']) {
debug(
}
const buildScriptName = getScriptName(pkg, [
'vercel-build',
'now-build',
'build',
]);
let { buildCommand } = config;
if (!buildScriptName && !buildCommand) {
console.log(
'Your application is being built using `next build`. ' +
'If you need to define a different build step, please create a `now-build` script in your `package.json` ' +
'(e.g. `{ "scripts": { "now-build": "npm run prepare && next build" } }`).'
'If you need to define a different build step, please create a `vercel-build` script in your `package.json` ' +
'(e.g. `{ "scripts": { "vercel-build": "npm run prepare && next build" } }`).'
);
pkg.scripts = {
'now-build': 'next build',
...(pkg.scripts || {}),
};
shouldRunScript = 'now-build';
await writePackageJson(entryPath, pkg);
buildCommand = 'next build';
}
if (process.env.NPM_AUTH_TOKEN) {
@@ -398,12 +376,11 @@ export const build = async ({
await createServerlessConfig(workPath, entryPath, nextVersion);
}
debug('Running user script...');
const memoryToConsume = Math.floor(os.totalmem() / 1024 ** 2) - 128;
const env: { [key: string]: string | undefined } = { ...spawnOpts.env };
env.NODE_OPTIONS = `--max_old_space_size=${memoryToConsume}`;
if (config.buildCommand) {
if (buildCommand) {
// Add `node_modules/.bin` to PATH
const nodeBinPath = await getNodeBinPath({ cwd: entryPath });
env.PATH = `${nodeBinPath}${path.delimiter}${env.PATH}`;
@@ -412,14 +389,14 @@ export const build = async ({
`Added "${nodeBinPath}" to PATH env because a build command was used.`
);
console.log(`Running "${config.buildCommand}"`);
await execCommand(config.buildCommand, {
console.log(`Running "${buildCommand}"`);
await execCommand(buildCommand, {
...spawnOpts,
cwd: entryPath,
env,
});
} else {
await runPackageJsonScript(entryPath, shouldRunScript, {
} else if (buildScriptName) {
await runPackageJsonScript(entryPath, buildScriptName, {
...spawnOpts,
env,
});
@@ -667,7 +644,7 @@ export const build = async ({
}
if (process.env.NPM_AUTH_TOKEN) {
await unlinkFile(path.join(entryPath, '.npmrc'));
await remove(path.join(entryPath, '.npmrc'));
}
const pageLambdaRoutes: Route[] = [];

View File

@@ -1,6 +1,6 @@
{
"scripts": {
"build": "next build && next export"
"now-build": "next build && next export"
},
"dependencies": {
"next": "canary",

View File

@@ -1,6 +1,6 @@
{
"scripts": {
"build": "next build && next export"
"vercel-build": "next build && next export"
},
"dependencies": {
"next": "canary",

View File

@@ -361,10 +361,12 @@ export async function build({
meta,
});
debug('Running user script...');
const runScriptTime = Date.now();
await runPackageJsonScript(entrypointFsDirname, 'now-build', spawnOpts);
debug(`Script complete [${Date.now() - runScriptTime}ms]`);
await runPackageJsonScript(
entrypointFsDirname,
// Don't consider "build" script since its intended for frontend code
['vercel-build', 'now-build'],
spawnOpts
);
debug('Tracing input files...');
const traceTime = Date.now();

View File

@@ -1,4 +1,8 @@
#!/bin/bash
set -euo pipefail
# Start fresh
rm -rf dist
# Build with `ncc`
ncc build src/index.ts -e @vercel/build-utils -e @now/build-utils -o dist

View File

@@ -264,7 +264,6 @@ export async function build({
const pkg = getPkg(entrypoint, workPath);
const devScript = pkg ? getScriptName(pkg, 'dev', config) : null;
const buildScript = pkg ? getScriptName(pkg, 'build', config) : null;
const framework = getFramework(config, pkg);
@@ -431,8 +430,6 @@ export async function build({
if (buildCommand) {
debug(`Executing "${buildCommand}"`);
} else {
debug(`Running "${buildScript}" in "${entrypoint}"`);
}
const found =
@@ -441,12 +438,16 @@ export async function build({
...spawnOpts,
cwd: entrypointDir,
})
: await runPackageJsonScript(entrypointDir, buildScript!, spawnOpts);
: await runPackageJsonScript(
entrypointDir,
['vercel-build', 'now-build', 'build'],
spawnOpts
);
if (!found) {
throw new Error(
`Missing required "${
buildCommand || buildScript
buildCommand || 'vercel-build'
}" script in "${entrypoint}"`
);
}

View File

@@ -1,5 +1,5 @@
{
"scripts": {
"now-build": "mkdir dist && node build.js"
"vercel-build": "mkdir dist && node build.js"
}
}

View File

@@ -35,7 +35,7 @@ const testsThatFailToBuild = new Map([
['05-empty-dist-dir', 'The Output Directory "dist" is empty.'],
[
'06-missing-script',
'Missing required "now-build" script in "package.json"',
'Missing required "vercel-build" script in "package.json"',
],
['07-nonzero-sh', 'Command "./build.sh" exited with 1'],
[