@nx/next
Version:
124 lines (123 loc) • 5.69 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = buildExecutor;
const devkit_1 = require("@nx/devkit");
const js_1 = require("@nx/js");
const path_1 = require("path");
const node_fs_1 = require("node:fs");
const promises_1 = require("node:fs/promises");
const semver_1 = require("semver");
const semver_2 = require("@nx/devkit/src/utils/semver");
const update_package_json_1 = require("./lib/update-package-json");
const create_next_config_file_1 = require("./lib/create-next-config-file");
const check_project_1 = require("./lib/check-project");
const child_process_1 = require("child_process");
const create_cli_options_1 = require("../../utils/create-cli-options");
const exit_codes_1 = require("nx/src/utils/exit-codes");
let childProcess;
async function buildExecutor(options, context) {
// Cast to any to overwrite NODE_ENV
process.env.NODE_ENV ||= 'production';
const projectRoot = context.projectGraph.nodes[context.projectName].data.root;
(0, check_project_1.checkPublicDirectory)(projectRoot);
// Set `__NEXT_REACT_ROOT` based on installed ReactDOM version
const packageJsonPath = (0, path_1.join)(projectRoot, 'package.json');
const packageJson = (0, node_fs_1.existsSync)(packageJsonPath)
? (0, devkit_1.readJsonFile)(packageJsonPath)
: undefined;
const rootPackageJson = (0, devkit_1.readJsonFile)((0, path_1.join)(context.root, 'package.json'));
const reactDomVersion = packageJson?.dependencies?.['react-dom'] ??
rootPackageJson.dependencies?.['react-dom'];
const hasReact18 = reactDomVersion &&
(0, semver_1.gte)((0, semver_2.checkAndCleanWithSemver)('react-dom', reactDomVersion), '18.0.0');
if (hasReact18) {
process.env['__NEXT_REACT_ROOT'] ||= 'true';
}
try {
await runCliBuild(devkit_1.workspaceRoot, projectRoot, options);
}
catch ({ error, code, signal }) {
if (code || signal) {
devkit_1.logger.error(`Build process exited due to ${code ? 'code ' + code : ''} ${code && signal ? 'and' : ''} ${signal ? 'signal ' + signal : ''}`);
}
else {
devkit_1.logger.error(`Error occurred while trying to run the build command`);
devkit_1.logger.error(error);
}
return { success: false };
}
finally {
if (childProcess) {
childProcess.kill();
}
}
await (0, promises_1.mkdir)(options.outputPath, { recursive: true });
const builtPackageJson = (0, js_1.createPackageJson)(context.projectName, context.projectGraph, {
target: context.targetName,
root: context.root,
isProduction: !options.includeDevDependenciesInPackageJson, // By default we remove devDependencies since this is a production build.
skipOverrides: options.skipOverrides,
skipPackageManager: options.skipPackageManager,
});
// Update `package.json` to reflect how users should run the build artifacts
builtPackageJson.scripts = {
start: 'next start',
};
(0, update_package_json_1.updatePackageJson)(builtPackageJson, context);
(0, devkit_1.writeJsonFile)(`${options.outputPath}/package.json`, builtPackageJson);
if (options.generateLockfile) {
const packageManager = (0, devkit_1.detectPackageManager)(context.root);
const lockFile = (0, js_1.createLockFile)(builtPackageJson, context.projectGraph, packageManager);
(0, node_fs_1.writeFileSync)(`${options.outputPath}/${(0, js_1.getLockFileName)(packageManager)}`, lockFile, {
encoding: 'utf-8',
});
}
// If output path is different from source path, then copy over the config and public files.
// This is the default behavior when running `nx build <app>`.
if (options.outputPath.replace(/\/$/, '') !== projectRoot) {
(0, create_next_config_file_1.createNextConfigFile)(options, context);
(0, node_fs_1.cpSync)((0, path_1.join)(projectRoot, 'public'), (0, path_1.join)(options.outputPath, 'public'), {
dereference: true,
recursive: true,
});
}
return { success: true };
}
function runCliBuild(workspaceRoot, projectRoot, options) {
const { experimentalAppOnly, experimentalBuildMode, profile, debug, outputPath, } = options;
// Set output path here since it can also be set via CLI
// We can retrieve it inside plugins/with-nx
process.env.NX_NEXT_OUTPUT_PATH ??= outputPath;
const args = (0, create_cli_options_1.createCliOptions)({
experimentalAppOnly,
experimentalBuildMode,
profile,
debug,
});
return new Promise((resolve, reject) => {
childProcess = (0, child_process_1.fork)(require.resolve('next/dist/bin/next'), ['build', ...args], {
cwd: (0, path_1.resolve)(workspaceRoot, projectRoot),
stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
env: process.env,
});
// Ensure the child process is killed when the parent exits
process.on('exit', () => childProcess.kill());
process.on('SIGTERM', (signal) => {
reject({ code: (0, exit_codes_1.signalToCode)(signal), signal });
});
process.on('SIGINT', (signal) => {
reject({ code: (0, exit_codes_1.signalToCode)(signal), signal });
});
childProcess.on('error', (err) => {
reject({ error: err });
});
childProcess.on('exit', (code, signal) => {
if (code === 0) {
resolve({ code, signal });
}
else {
reject({ code, signal });
}
});
});
}