Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(nextjs): remove the "root" option from Next.js build executor since it can be inferred #17248

Merged
merged 1 commit into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/next/migrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@
"version": "16.0.0-beta.1",
"description": "Replace @nrwl/next with @nx/next",
"implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages"
},
"update-16-3-0-remove-root-build-option": {
"cli": "nx",
"version": "16.3.0-beta.9",
"description": "Remove root build option from project configurations since it is not needed.",
"implementation": "./src/migrations/update-16-3-0/remove-root-build-option"
}
},
"packageJsonUpdates": {
Expand Down
2 changes: 1 addition & 1 deletion packages/next/plugins/with-nx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ function withNx(
nextConfig.webpack = (a, b) =>
createWebpackConfig(
workspaceRoot,
options.root,
projectDirectory,
options.fileReplacements,
options.assets,
dependencies,
Expand Down
21 changes: 10 additions & 11 deletions packages/next/src/executors/build/build.impl.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import 'dotenv/config';
import {
ExecutorContext,
logger,
readJsonFile,
workspaceRoot,
writeJsonFile,
logger,
} from '@nx/devkit';
import { createLockFile, createPackageJson, getLockFileName } from '@nx/js';
import { join, resolve } from 'path';
import { join } from 'path';
import { copySync, existsSync, mkdir, writeFileSync } from 'fs-extra';
import { lt, gte } from 'semver';
import { gte } from 'semver';
import { directoryExists } from '@nx/workspace/src/utilities/fileutils';
import { checkAndCleanWithSemver } from '@nx/devkit/src/utils/semver';

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 { ExecSyncOptions, execSync } from 'child_process';
import { execSync, ExecSyncOptions } from 'child_process';
import { createCliOptions } from '../../utils/create-cli-options';

export default async function buildExecutor(
Expand All @@ -27,16 +26,16 @@ export default async function buildExecutor(
// Cast to any to overwrite NODE_ENV
(process.env as any).NODE_ENV ||= 'production';

const root = resolve(context.root, options.root);
const projectRoot = context.projectGraph.nodes[context.projectName].data.root;

checkPublicDirectory(root);
checkPublicDirectory(projectRoot);

// Set `__NEXT_REACT_ROOT` based on installed ReactDOM version
const packageJsonPath = join(root, 'package.json');
const packageJsonPath = join(projectRoot, 'package.json');
const packageJson = existsSync(packageJsonPath)
? readJsonFile(packageJsonPath)
: undefined;
const rootPackageJson = readJsonFile(join(workspaceRoot, 'package.json'));
const rootPackageJson = readJsonFile(join(context.root, 'package.json'));
const reactDomVersion =
packageJson?.dependencies?.['react-dom'] ??
rootPackageJson.dependencies?.['react-dom'];
Expand All @@ -54,7 +53,7 @@ export default async function buildExecutor(
const execSyncOptions: ExecSyncOptions = {
stdio: 'inherit',
encoding: 'utf-8',
cwd: root,
cwd: projectRoot,
};
try {
execSync(command, execSyncOptions);
Expand Down Expand Up @@ -89,7 +88,7 @@ export default async function buildExecutor(

createNextConfigFile(options, context);

copySync(join(root, 'public'), join(options.outputPath, 'public'), {
copySync(join(projectRoot, 'public'), join(options.outputPath, 'public'), {
dereference: true,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ export function createNextConfigFile(
options: NextBuildBuilderOptions,
context: ExecutorContext
) {
const projectRoot = context.projectGraph.nodes[context.projectName].data.root;
const configRelativeToProjectRoot = findNextConfigPath(
options.root,
projectRoot,
// If user passed a config then it is relative to the workspace root, need to normalize it to be relative to the project root.
options.nextConfig ? relative(options.root, options.nextConfig) : undefined
options.nextConfig ? relative(projectRoot, options.nextConfig) : undefined
);
const configAbsolutePath = join(options.root, configRelativeToProjectRoot);
const configAbsolutePath = join(projectRoot, configRelativeToProjectRoot);

if (!existsSync(configAbsolutePath)) {
throw new Error('next.config.js not found');
Expand Down Expand Up @@ -65,12 +66,12 @@ export function createNextConfigFile(
// Find all relative imports needed by next.config.js and copy them to the dist folder.
const moduleFilesToCopy = getRelativeFilesToCopy(
configRelativeToProjectRoot,
options.root
projectRoot
);
for (const moduleFile of moduleFilesToCopy) {
ensureDirSync(dirname(join(context.root, options.outputPath, moduleFile)));
copyFileSync(
join(context.root, options.root, moduleFile),
join(context.root, projectRoot, moduleFile),
join(context.root, options.outputPath, moduleFile)
);
}
Expand Down
7 changes: 1 addition & 6 deletions packages/next/src/executors/build/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@
"description": "Build a Next.js app.",
"type": "object",
"properties": {
"root": {
"description": "The source root",
"type": "string",
"x-priority": "important"
},
"outputPath": {
"type": "string",
"description": "The output path of the generated files.",
Expand Down Expand Up @@ -76,5 +71,5 @@
"description": "Only build 'app' routes"
}
},
"required": ["root", "outputPath"]
"required": ["outputPath"]
}
4 changes: 2 additions & 2 deletions packages/next/src/executors/export/export.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ export default async function exportExecutor(
buildTarget,
context
);
const root = resolve(context.root, buildOptions.root);
const projectRoot = context.projectGraph.nodes[context.projectName].data.root;

// Taken from:
// https://github.com/vercel/next.js/blob/ead56eaab68409e96c19f7d9139747bac1197aa9/packages/next/cli/next-export.ts#L13
const nextExportCliSpan = nextTrace.trace('next-export-cli');

await exportApp(
root,
projectRoot,
{
statusMessage: 'Exporting',
silent: options.silent,
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/executors/server/custom-server.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ export default async function* serveExecutor(
parseTargetString(options.buildTarget, context.projectGraph),
context
);
const root = resolve(context.root, buildOptions.root);
const projectRoot = context.projectGraph.nodes[context.projectName].data.root;

yield* runCustomServer(root, options, context);
yield* runCustomServer(projectRoot, options, context);
}

async function* runCustomServer(
Expand Down
6 changes: 3 additions & 3 deletions packages/next/src/executors/server/server.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ export default async function* serveExecutor(
parseTargetString(options.buildTarget, context.projectGraph),
context
);
const root = resolve(context.root, buildOptions.root);
const projectRoot = context.workspace.projects[context.projectName].root;

const { port, keepAliveTimeout, hostname } = options;

// This is required for the default custom server to work. See the @nx/next:app generator.
process.env.NX_NEXT_DIR = root;
process.env.NX_NEXT_DIR = projectRoot;

// Cast to any to overwrite NODE_ENV
(process.env as any).NODE_ENV = process.env.NODE_ENV
Expand All @@ -56,7 +56,7 @@ export default async function* serveExecutor(
yield* createAsyncIterable<{ success: boolean; baseUrl: string }>(
async ({ done, next, error }) => {
const server = fork(nextBin, [mode, ...args, turbo], {
cwd: options.dev ? root : nextDir,
cwd: options.dev ? projectRoot : nextDir,
stdio: 'inherit',
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,6 @@ describe('app', () => {
'@nx/next:build'
);
expect(projectConfiguration.targets.build.options).toEqual({
root: 'apps/my-app',
outputPath: 'dist/apps/my-app',
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export function addProject(host: Tree, options: NormalizedSchema) {
outputs: ['{options.outputPath}'],
defaultConfiguration: 'production',
options: {
root: options.appProjectRoot,
outputPath: options.outputPath,
},
configurations: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import update from './remove-root-build-option';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing-pre16';
import {
addProjectConfiguration,
readProjectConfiguration,
Tree,
} from '@nx/devkit';

describe('remove-root-build-option', () => {
let tree: Tree;

beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
});

it('should remove the root option from the build target for @nx/next:build executor', async () => {
addProjectConfiguration(tree, 'my-app', {
root: 'my-app',
targets: {
build: {
executor: '@nx/next:build',
options: {
root: 'my-app',
},
},
},
});

await update(tree);

const updatedConfig = readProjectConfiguration(tree, 'my-app');
expect(updatedConfig.targets.build.options.root).toBeUndefined();
});

it('should remove the root option from the build target for @nrwl/next:build executor', async () => {
addProjectConfiguration(tree, 'my-app', {
root: 'my-app',
targets: {
build: {
executor: '@nrwl/next:build',
options: {
root: 'my-app',
},
},
},
});

await update(tree);

const updatedConfig = readProjectConfiguration(tree, 'my-app');
expect(updatedConfig.targets.build.options.root).toBeUndefined();
jaysoo marked this conversation as resolved.
Show resolved Hide resolved
});

it('should leave other executors alone', async () => {
addProjectConfiguration(tree, 'my-app', {
root: 'my-app',
targets: {
build: {
executor: '@acme/foo:bar',
options: {
root: 'my-app',
},
},
},
});

await update(tree);

const updatedConfig = readProjectConfiguration(tree, 'my-app');
expect(updatedConfig.targets.build.options.root).toEqual('my-app');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {
readProjectConfiguration,
Tree,
updateProjectConfiguration,
} from '@nx/devkit';
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';

export default async function update(tree: Tree) {
forEachExecutorOptions(
tree,
'@nx/next:build',
(options, projectName, targetName) => {
const projectConfig = readProjectConfiguration(tree, projectName);
delete projectConfig.targets[targetName].options.root;
updateProjectConfiguration(tree, projectName, projectConfig);
}
);
forEachExecutorOptions(
tree,
'@nrwl/next:build',
(options, projectName, targetName) => {
const projectConfig = readProjectConfiguration(tree, projectName);
delete projectConfig.targets[targetName].options.root;
updateProjectConfiguration(tree, projectName, projectConfig);
}
);
}
1 change: 0 additions & 1 deletion packages/next/src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export interface FileReplacement {
}

export interface NextBuildBuilderOptions {
root: string;
outputPath: string;
fileReplacements: FileReplacement[];
assets?: any[];
Expand Down