Skip to content

Commit

Permalink
feat(nextjs): remove the "root" option from Next.js build executor si…
Browse files Browse the repository at this point in the history
…nce it can be inferred (#17248)
  • Loading branch information
jaysoo authored May 26, 2023
1 parent ac41730 commit 9881efa
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 33 deletions.
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
11 changes: 6 additions & 5 deletions packages/next/src/executors/build/lib/create-next-config-file.ts
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();
});

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

1 comment on commit 9881efa

@vercel
Copy link

@vercel vercel bot commented on 9881efa May 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-dev-nrwl.vercel.app
nx-five.vercel.app
nx-dev-git-master-nrwl.vercel.app
nx.dev

Please sign in to comment.