Skip to content

Commit

Permalink
fix(nextjs): refactor how webpack config is loaded with nextjs (#15650)
Browse files Browse the repository at this point in the history
Co-authored-by: FrozenPandaz <jasonjean1993@gmail.com>

(cherry picked from commit be81405)
  • Loading branch information
ndcunningham authored and FrozenPandaz committed Mar 15, 2023
1 parent 932ed44 commit b81c398
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 272 deletions.
2 changes: 1 addition & 1 deletion nx-dev/nx-dev/next.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// nx-ignore-next-line
const withNx = require('@nrwl/next/plugins/with-nx');
const { withNx } = require('@nrwl/next/plugins/with-nx');
const { copySync } = require('fs-extra');
const path = require('path');
const redirectRules = require('./redirect-rules.config');
Expand Down
6 changes: 3 additions & 3 deletions packages/next/plugins/with-nx.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { NextConfigComplete } from 'next/dist/server/config-shared';
import { withNx } from './with-nx';
import { getNextConfig } from './with-nx';

describe('withNx', () => {
describe('svgr', () => {
it('should be used by default', () => {
const config = withNx({});
const config = getNextConfig();

const result = config.webpack(
{
Expand Down Expand Up @@ -32,7 +32,7 @@ describe('withNx', () => {
});

it('should not be used when disabled', () => {
const config = withNx({
const config = getNextConfig({
nx: {
svgr: false,
},
Expand Down
139 changes: 139 additions & 0 deletions packages/next/plugins/with-nx.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
import {
createProjectGraphAsync,
joinPathFragments,
offsetFromRoot,
parseTargetString,
ProjectGraph,
ProjectGraphProjectNode,
Target,
workspaceRoot,
} from '@nrwl/devkit';
import {
calculateProjectDependencies,
DependentBuildableProjectNode,
} from '@nrwl/js/src/utils/buildable-libs-utils';
import type { NextConfig } from 'next';

import path = require('path');
import { createWebpackConfig } from '../src/utils/config';
import { NextBuildBuilderOptions } from '@nrwl/next';
export interface WithNxOptions extends NextConfig {
nx?: {
svgr?: boolean;
Expand Down Expand Up @@ -34,7 +51,128 @@ function getWithNxContext(): WithNxContext {
};
}

function getTargetConfig(graph: ProjectGraph, target: Target) {
const projectNode = graph.nodes[target.project];
return projectNode.data.targets[target.target];
}

function getOptions(graph: ProjectGraph, target: Target) {
const targetConfig = getTargetConfig(graph, target);
const options = targetConfig.options;
if (target.configuration) {
Object.assign(options, targetConfig.configurations[target.configuration]);
}

return options;
}

function getNxContext(
graph: ProjectGraph,
target: Target
): {
node: ProjectGraphProjectNode;
options: NextBuildBuilderOptions;
projectName: string;
targetName: string;
configurationName?: string;
} {
const targetConfig = getTargetConfig(graph, target);

if ('@nrwl/next:build' === targetConfig.executor) {
return {
node: graph.nodes[target.project],
options: getOptions(graph, target),
projectName: target.project,
targetName: target.target,
configurationName: target.configuration,
};
}

const targetOptions = getOptions(graph, target);

// If we are running serve or export pull the options from the dependent target first (ex. build)
if (targetOptions.devServerTarget) {
const devServerTarget = parseTargetString(
targetOptions.devServerTarget,
graph
);

return getNxContext(graph, devServerTarget);
} else if (
['@nrwl/next:server', '@nrwl/next:export'].includes(targetConfig.executor)
) {
const buildTarget = parseTargetString(targetOptions.buildTarget, graph);
return getNxContext(graph, buildTarget);
} else {
throw new Error(
'Could not determine the config for this Next application.'
);
}
}

export function withNx(
_nextConfig = {} as WithNxOptions,
context: WithNxContext = getWithNxContext()
): () => Promise<NextConfig> {
return async () => {
let dependencies: DependentBuildableProjectNode[] = [];

const graph = await createProjectGraphAsync();

const originalTarget = {
project: process.env.NX_TASK_TARGET_PROJECT,
target: process.env.NX_TASK_TARGET_TARGET,
configuration: process.env.NX_TASK_TARGET_CONFIGURATION,
};

const {
node: projectNode,
options,
projectName: project,
targetName,
configurationName,
} = getNxContext(graph, originalTarget);
const projectDirectory = projectNode.data.root;

if (!options.buildLibsFromSource && targetName) {
const result = calculateProjectDependencies(
graph,
workspaceRoot,
project,
targetName,
configurationName
);
dependencies = result.dependencies;
}

// Get next config
const nextConfig = getNextConfig(_nextConfig, context);

const outputDir = `${offsetFromRoot(projectDirectory)}${
options.outputPath
}`;
nextConfig.distDir =
nextConfig.distDir && nextConfig.distDir !== '.next'
? joinPathFragments(outputDir, nextConfig.distDir)
: joinPathFragments(outputDir, '.next');

const userWebpackConfig = nextConfig.webpack;

nextConfig.webpack = (a, b) =>
createWebpackConfig(
workspaceRoot,
options.root,
options.fileReplacements,
options.assets,
dependencies,
path.join(workspaceRoot, context.libsDir)
)(userWebpackConfig ? userWebpackConfig(a, b) : a, b);

return nextConfig;
};
}

export function getNextConfig(
nextConfig = {} as WithNxOptions,
context: WithNxContext = getWithNxContext()
): NextConfig {
Expand Down Expand Up @@ -223,3 +361,4 @@ function addNxEnvVariables(config: any) {
module.exports = withNx;
// Support for newer generated code: `const { withNx } = require(...);`
module.exports.withNx = withNx;
module.exports.getNextConfig = getNextConfig;
33 changes: 3 additions & 30 deletions packages/next/src/executors/build/build.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'dotenv/config';
import {
ExecutorContext,
readJsonFile,
workspaceLayout,
workspaceRoot,
writeJsonFile,
} from '@nrwl/devkit';
Expand All @@ -12,18 +11,13 @@ 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/workspace/src/utilities/buildable-libs-utils';
import { checkAndCleanWithSemver } from '@nrwl/workspace/src/utilities/version-utils';
import { checkAndCleanWithSemver } from '@nrwl/workspace/src/utils/version-utils';

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(
Expand All @@ -33,23 +27,10 @@ export default async function buildExecutor(
// 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)
Expand All @@ -66,15 +47,7 @@ export default async function buildExecutor(
(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);
await build(root);

if (!directoryExists(options.outputPath)) {
mkdir(options.outputPath);
Expand Down
42 changes: 24 additions & 18 deletions packages/next/src/executors/export/export.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,27 @@ import {
ExecutorContext,
parseTargetString,
readTargetOptions,
runExecutor,
workspaceLayout,
} from '@nrwl/devkit';
import exportApp from 'next/dist/export';
import { join, resolve } from 'path';
import {
calculateProjectDependencies,
DependentBuildableProjectNode,
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
import { workspaceLayout } from '@nrwl/devkit';

import { prepareConfig } from '../../utils/config';
import {
NextBuildBuilderOptions,
NextExportBuilderOptions,
} from '../../utils/types';
import { PHASE_EXPORT } from '../../utils/constants';
import nextTrace = require('next/dist/trace');
import { platform } from 'os';
import { execFileSync } from 'child_process';
import * as chalk from 'chalk';

// platform specific command name
const pmCmd = platform() === 'win32' ? `npx.cmd` : 'npx';

export default async function exportExecutor(
options: NextExportBuilderOptions,
Expand All @@ -38,27 +42,25 @@ export default async function exportExecutor(
}

const libsDir = join(context.root, workspaceLayout().libsDir);
const buildTarget = parseTargetString(options.buildTarget);
const build = await runExecutor(buildTarget, {}, context);
const buildTarget = parseTargetString(
options.buildTarget,
context.projectGraph
);

for await (const result of build) {
if (!result.success) {
return result;
}
try {
const args = getBuildTargetCommand(options);
execFileSync(pmCmd, args, {
stdio: [0, 1, 2],
});
} catch {
throw new Error(`Build target failed: ${chalk.bold(options.buildTarget)}`);
}

const buildOptions = readTargetOptions<NextBuildBuilderOptions>(
buildTarget,
context
);
const root = resolve(context.root, buildOptions.root);
const config = await prepareConfig(
PHASE_EXPORT,
buildOptions,
context,
dependencies,
libsDir
);

// Taken from:
// https://github.com/vercel/next.js/blob/ead56eaab68409e96c19f7d9139747bac1197aa9/packages/next/cli/next-export.ts#L13
Expand All @@ -72,9 +74,13 @@ export default async function exportExecutor(
threads: options.threads,
outdir: `${buildOptions.outputPath}/exported`,
} as any,
nextExportCliSpan,
config
nextExportCliSpan
);

return { success: true };
}

function getBuildTargetCommand(options: NextExportBuilderOptions) {
const cmd = ['nx', 'run', options.buildTarget];
return cmd;
}
Loading

0 comments on commit b81c398

Please sign in to comment.