nx/packages/next/src/executors/build/build.impl.ts

108 lines
3.4 KiB
TypeScript

import 'dotenv/config';
import {
ExecutorContext,
readJsonFile,
workspaceLayout,
workspaceRoot,
writeJsonFile,
} from '@nrwl/devkit';
import { createLockFile, createPackageJson } from '@nrwl/js';
import build from 'next/dist/build';
import { join, resolve } from 'path';
import { copySync, existsSync, mkdir, writeFileSync } from 'fs-extra';
import { gte } from 'semver';
import { directoryExists } from '@nrwl/workspace/src/utilities/fileutils';
import {
calculateProjectDependencies,
DependentBuildableProjectNode,
} from '@nrwl/js/src/utils/buildable-libs-utils';
import { checkAndCleanWithSemver } from '@nrwl/devkit/src/utils/semver';
import { prepareConfig } from '../../utils/config';
import { updatePackageJson } from './lib/update-package-json';
import { createNextConfigFile } from './lib/create-next-config-file';
import { checkPublicDirectory } from './lib/check-project';
import { NextBuildBuilderOptions } from '../../utils/types';
import { PHASE_PRODUCTION_BUILD } from '../../utils/constants';
import { getLockFileName } from 'nx/src/lock-file/lock-file';
export default async function buildExecutor(
options: NextBuildBuilderOptions,
context: ExecutorContext
) {
// Cast to any to overwrite NODE_ENV
(process.env as any).NODE_ENV ||= 'production';
let dependencies: DependentBuildableProjectNode[] = [];
const root = resolve(context.root, options.root);
const libsDir = join(context.root, workspaceLayout().libsDir);
checkPublicDirectory(root);
if (!options.buildLibsFromSource && context.targetName) {
const result = calculateProjectDependencies(
context.projectGraph,
context.root,
context.projectName,
context.targetName,
context.configurationName
);
dependencies = result.dependencies;
}
// Set `__NEXT_REACT_ROOT` based on installed ReactDOM version
const packageJsonPath = join(root, 'package.json');
const packageJson = existsSync(packageJsonPath)
? readJsonFile(packageJsonPath)
: undefined;
const rootPackageJson = readJsonFile(join(workspaceRoot, 'package.json'));
const reactDomVersion =
packageJson?.dependencies?.['react-dom'] ??
rootPackageJson.dependencies?.['react-dom'];
const hasReact18 =
reactDomVersion &&
gte(checkAndCleanWithSemver('react-dom', reactDomVersion), '18.0.0');
if (hasReact18) {
(process.env as any).__NEXT_REACT_ROOT ||= 'true';
}
const config = await prepareConfig(
PHASE_PRODUCTION_BUILD,
options,
context,
dependencies,
libsDir
);
await build(root, config as any);
if (!directoryExists(options.outputPath)) {
mkdir(options.outputPath);
}
const builtPackageJson = 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.
}
);
updatePackageJson(builtPackageJson, context);
writeJsonFile(`${options.outputPath}/package.json`, builtPackageJson);
if (options.generateLockfile) {
const lockFile = createLockFile(builtPackageJson);
writeFileSync(`${options.outputPath}/${getLockFileName()}`, lockFile, {
encoding: 'utf-8',
});
}
createNextConfigFile(options, context);
copySync(join(root, 'public'), join(options.outputPath, 'public'));
return { success: true };
}