UNPKG

@nx/next

Version:

The Next.js plugin for Nx contains executors and generators for managing Next.js applications and libraries within an Nx workspace. It provides: - Scaffolding for creating, building, serving, linting, and testing Next.js applications. - Integration wit

124 lines (123 loc) 5.69 kB
"use strict"; 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 }); } }); }); }