@nx/webpack 24.0.0-beta.2 → 24.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/internal.d.ts +3 -0
- package/dist/internal.js +7 -1
- package/dist/src/executors/dev-server/dev-server.impl.js +38 -30
- package/dist/src/generators/configuration/configuration.js +14 -132
- package/dist/src/generators/configuration/schema.d.ts +0 -1
- package/dist/src/generators/convert-config-to-webpack-plugin/convert-config-to-webpack-plugin.js +82 -49
- package/dist/src/generators/convert-config-to-webpack-plugin/lib/extract-webpack-options.d.ts +1 -0
- package/dist/src/generators/convert-config-to-webpack-plugin/lib/extract-webpack-options.js +31 -2
- package/dist/src/generators/convert-config-to-webpack-plugin/schema.json +2 -2
- package/dist/src/generators/convert-to-inferred/convert-to-inferred.js +5 -4
- package/dist/src/generators/convert-to-inferred/utils/build-post-target-transformer.js +13 -5
- package/dist/src/generators/convert-to-inferred/utils/serve-post-target-transformer.js +8 -14
- package/dist/src/generators/init/init.js +45 -54
- package/dist/src/generators/init/schema.d.ts +0 -1
- package/dist/src/migrations/update-24-0-0/finish-inferred-conversion.md +11 -0
- package/dist/src/migrations/update-24-0-0/inventory.d.ts +9 -0
- package/dist/src/migrations/update-24-0-0/inventory.js +53 -0
- package/dist/src/migrations/update-24-0-0/migrate-compose-plugins.d.ts +10 -0
- package/dist/src/migrations/update-24-0-0/migrate-compose-plugins.js +60 -0
- package/dist/src/migrations/update-24-0-0/migrate-compose-plugins.md +98 -0
- package/dist/src/migrations/update-24-0-0/remove-deprecated-executors.d.ts +6 -0
- package/dist/src/migrations/update-24-0-0/remove-deprecated-executors.js +36 -0
- package/dist/src/migrations/update-24-0-0/remove-deprecated-executors.md +92 -0
- package/dist/src/plugins/nx-webpack-plugin/lib/normalize-options.d.ts +6 -0
- package/dist/src/plugins/nx-webpack-plugin/lib/normalize-options.js +29 -24
- package/dist/src/plugins/nx-webpack-plugin/nx-app-webpack-plugin.js +12 -1
- package/dist/src/utils/config.d.ts +8 -12
- package/dist/src/utils/config.js +15 -68
- package/dist/src/utils/deprecation.d.ts +1 -6
- package/dist/src/utils/deprecation.js +4 -27
- package/dist/src/utils/e2e-web-server-info-utils.d.ts +1 -1
- package/dist/src/utils/e2e-web-server-info-utils.js +2 -2
- package/dist/src/utils/with-nx.d.ts +5 -9
- package/dist/src/utils/with-nx.js +5 -32
- package/dist/src/utils/with-web.d.ts +5 -9
- package/dist/src/utils/with-web.js +5 -24
- package/executors.json +0 -10
- package/migrations.json +13 -0
- package/package.json +4 -4
- package/dist/src/executors/webpack/lib/run-webpack.d.ts +0 -3
- package/dist/src/executors/webpack/lib/run-webpack.js +0 -36
- package/dist/src/executors/webpack/schema.json +0 -459
- package/dist/src/executors/webpack/webpack.impl.d.ts +0 -13
- package/dist/src/executors/webpack/webpack.impl.js +0 -117
- package/dist/src/utils/has-plugin.d.ts +0 -2
- package/dist/src/utils/has-plugin.js +0 -10
|
@@ -16,7 +16,7 @@ function servePostTargetTransformerFactory(migrationContext) {
|
|
|
16
16
|
projectName: projectDetails.projectName,
|
|
17
17
|
projectRoot: projectDetails.root,
|
|
18
18
|
};
|
|
19
|
-
const { devServerOptions, webpackConfigPath } = await processOptions(
|
|
19
|
+
const { devServerOptions, webpackConfigPath } = await processOptions(target, context);
|
|
20
20
|
updateWebpackConfig(tree, webpackConfigPath, devServerOptions, context);
|
|
21
21
|
if (target.outputs) {
|
|
22
22
|
(0, internal_1.processTargetOutputs)(target, [], inferredTarget, {
|
|
@@ -27,20 +27,14 @@ function servePostTargetTransformerFactory(migrationContext) {
|
|
|
27
27
|
return target;
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
-
async function processOptions(
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
acc.projects[projectName] = project.data;
|
|
38
|
-
return acc;
|
|
39
|
-
}, { version: 1, projects: {} }),
|
|
40
|
-
root: context.workspaceRoot,
|
|
30
|
+
async function processOptions(target, context) {
|
|
31
|
+
const buildTarget = (0, devkit_1.parseTargetString)(target.options.buildTarget, context.projectGraph);
|
|
32
|
+
const buildConfiguration = context.projectGraph.nodes[buildTarget.project].data.targets[buildTarget.target];
|
|
33
|
+
// The build executor is removed in v24; do not resolve its schema.
|
|
34
|
+
const buildOptions = {
|
|
35
|
+
...buildConfiguration.options,
|
|
36
|
+
...buildConfiguration.configurations?.[buildTarget.configuration ?? buildConfiguration.defaultConfiguration],
|
|
41
37
|
};
|
|
42
|
-
const buildTarget = (0, devkit_1.parseTargetString)(target.options.buildTarget, executorContext);
|
|
43
|
-
const buildOptions = (0, devkit_1.readTargetOptions)(buildTarget, executorContext);
|
|
44
38
|
// it must exist, we validated it in the project filter
|
|
45
39
|
const webpackConfigPath = buildOptions.webpackConfig;
|
|
46
40
|
const defaultOptions = extractDevServerOptions(target.options, context);
|
|
@@ -7,71 +7,62 @@ const devkit_1 = require("@nx/devkit");
|
|
|
7
7
|
const plugin_1 = require("../../plugins/plugin");
|
|
8
8
|
const versions_1 = require("../../utils/versions");
|
|
9
9
|
function webpackInitGenerator(tree, schema) {
|
|
10
|
-
return webpackInitGeneratorInternal(tree,
|
|
10
|
+
return webpackInitGeneratorInternal(tree, schema);
|
|
11
11
|
}
|
|
12
12
|
async function webpackInitGeneratorInternal(tree, schema) {
|
|
13
13
|
(0, versions_1.assertSupportedWebpackVersion)(tree);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
'serve-static:webpack',
|
|
55
|
-
'webpack-serve-static',
|
|
56
|
-
'serve-static-webpack',
|
|
57
|
-
],
|
|
58
|
-
}, schema.updatePackageScripts);
|
|
59
|
-
}
|
|
14
|
+
await (0, internal_1.addPlugin)(tree, await (0, devkit_1.createProjectGraphAsync)(), '@nx/webpack/plugin', plugin_1.createNodesV2, {
|
|
15
|
+
buildTargetName: [
|
|
16
|
+
'build',
|
|
17
|
+
'webpack:build',
|
|
18
|
+
'build:webpack',
|
|
19
|
+
'webpack-build',
|
|
20
|
+
'build-webpack',
|
|
21
|
+
],
|
|
22
|
+
serveTargetName: [
|
|
23
|
+
'serve',
|
|
24
|
+
'webpack:serve',
|
|
25
|
+
'serve:webpack',
|
|
26
|
+
'webpack-serve',
|
|
27
|
+
'serve-webpack',
|
|
28
|
+
],
|
|
29
|
+
previewTargetName: [
|
|
30
|
+
'preview',
|
|
31
|
+
'webpack:preview',
|
|
32
|
+
'preview:webpack',
|
|
33
|
+
'webpack-preview',
|
|
34
|
+
'preview-webpack',
|
|
35
|
+
],
|
|
36
|
+
buildDepsTargetName: [
|
|
37
|
+
'build-deps',
|
|
38
|
+
'webpack:build-deps',
|
|
39
|
+
'webpack-build-deps',
|
|
40
|
+
],
|
|
41
|
+
watchDepsTargetName: [
|
|
42
|
+
'watch-deps',
|
|
43
|
+
'webpack:watch-deps',
|
|
44
|
+
'webpack-watch-deps',
|
|
45
|
+
],
|
|
46
|
+
serveStaticTargetName: [
|
|
47
|
+
'serve-static',
|
|
48
|
+
'webpack:serve-static',
|
|
49
|
+
'serve-static:webpack',
|
|
50
|
+
'webpack-serve-static',
|
|
51
|
+
'serve-static-webpack',
|
|
52
|
+
],
|
|
53
|
+
}, schema.updatePackageScripts);
|
|
60
54
|
let installTask = () => { };
|
|
61
55
|
if (!schema.skipPackageJson) {
|
|
62
|
-
// webpack and webpack-dev-server are peer dependencies
|
|
63
|
-
//
|
|
64
|
-
// user's runtime).
|
|
56
|
+
// webpack and webpack-dev-server are peer dependencies needed at the
|
|
57
|
+
// user's runtime.
|
|
65
58
|
const devDependencies = {
|
|
66
59
|
'@nx/webpack': versions_1.nxVersion,
|
|
67
60
|
'@nx/web': versions_1.nxVersion,
|
|
68
61
|
webpack: versions_1.webpackVersion,
|
|
69
62
|
'webpack-dev-server': versions_1.webpackDevServerVersion,
|
|
70
|
-
};
|
|
71
|
-
if (schema.addPlugin) {
|
|
72
63
|
// The inferred plugin runs the `webpack-cli` binary.
|
|
73
|
-
|
|
74
|
-
}
|
|
64
|
+
'webpack-cli': versions_1.webpackCliVersion,
|
|
65
|
+
};
|
|
75
66
|
// @nx/webpack depends on sass, which pulls in @parcel/watcher. Its install
|
|
76
67
|
// script only builds from source when npm_config_build_from_source is set,
|
|
77
68
|
// so skip it.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Replace removed @nx/webpack executors with inferred tasks
|
|
2
|
+
|
|
3
|
+
Nx 24 removed `@nx/webpack:webpack`, `@nx/webpack:dev-server` (including old `@nrwl` aliases).
|
|
4
|
+
|
|
5
|
+
Before changing anything, check for leftover work: a project target, a package.json target or a `targetDefaults` entry still uses a removed executor, or the advisory context reports a conversion error. If none applies, make no changes and stop. Treat each advisory-context item as pending work.
|
|
6
|
+
|
|
7
|
+
1. Inspect all affected projects and workspace defaults. Repair config-loading errors first. Do not restore executor registrations or install older Nx packages.
|
|
8
|
+
2. Register `@nx/webpack/plugin` with options/includes that preserve custom target names and mixed inferred/explicit projects. Replace remaining removed-executor targets, including those in package.json and filtered targetDefaults. Transfer their behavior before removing obsolete defaults. Preserve configurations, dependencies, cache inputs/outputs and environment behavior. Do not silently delete unsupported options or unrelated targets. Migrate old @nrwl aliases too.
|
|
9
|
+
3. Run project graph creation with the daemon and graph cache disabled (`NX_DAEMON=false NX_CACHE_PROJECT_GRAPH=false`), using the workspace package manager. Inspect resolved project targets and verify each migrated entrypoint exists and is inferred (a thin alias to an inferred target is acceptable). Resolve graph errors before completing this migration, even if task validation was skipped. Configs must load for subsequent migrations.
|
|
10
|
+
4. By default run relevant real builds/tests with task cache disabled, including alternate configurations. Honor an existing preference to skip expensive task validation; otherwise offer that option when validation is slow. Skipping expensive tasks never skips conversion or the graph check. Report each skipped task as unverified. Never weaken assertions or change application behavior to make checks pass.
|
|
11
|
+
5. Report converted projects, graph validation, task results and any skipped checks. Unconverted targets or a broken graph mean the migration is incomplete. Investigate task failures and distinguish missing environment prerequisites from conversion regressions; do not claim a working workspace with unresolved regressions.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.inventoryComposePlugins = inventoryComposePlugins;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const CONFIG_EXTENSIONS = ['js', 'cjs', 'mjs', 'ts', 'cts', 'mts'];
|
|
6
|
+
const REMOVED_HELPERS = /\b(composePlugins|composePluginsSync|withNx|withWeb|withReact)\b/;
|
|
7
|
+
// Left by an earlier conversion; the wrapped `.old` config still calls the helpers.
|
|
8
|
+
const LEGACY_WRAPPER = /\buseLegacyNxPlugin\b/;
|
|
9
|
+
function inventoryComposePlugins(tree) {
|
|
10
|
+
const usages = [];
|
|
11
|
+
for (const [name, project] of (0, devkit_1.getProjects)(tree)) {
|
|
12
|
+
const configs = [...collectConfigPaths(tree, project)].map((path) => ({
|
|
13
|
+
path,
|
|
14
|
+
source: tree.read(path, 'utf-8') ?? '',
|
|
15
|
+
}));
|
|
16
|
+
const configPaths = configs
|
|
17
|
+
.filter(({ source }) => REMOVED_HELPERS.test(source) || LEGACY_WRAPPER.test(source))
|
|
18
|
+
.map(({ path }) => path);
|
|
19
|
+
if (!configPaths.length)
|
|
20
|
+
continue;
|
|
21
|
+
usages.push({
|
|
22
|
+
name,
|
|
23
|
+
root: project.root,
|
|
24
|
+
configPaths: configPaths.sort(),
|
|
25
|
+
usesHelpers: configs.some(({ source }) => REMOVED_HELPERS.test(source)),
|
|
26
|
+
usesExecutor: Object.values(project.targets ?? {}).some((target) => target.executor === '@nx/webpack:webpack'),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return usages;
|
|
30
|
+
}
|
|
31
|
+
function collectConfigPaths(tree, project) {
|
|
32
|
+
const paths = new Set();
|
|
33
|
+
for (const target of Object.values(project.targets ?? {})) {
|
|
34
|
+
for (const options of [
|
|
35
|
+
target.options,
|
|
36
|
+
...Object.values(target.configurations ?? {}),
|
|
37
|
+
]) {
|
|
38
|
+
const configured = options?.webpackConfig;
|
|
39
|
+
// A package path such as `@nx/react/plugins/webpack` resolves to the stubs
|
|
40
|
+
// rather than a workspace file; the prompt gives those projects a config.
|
|
41
|
+
if (typeof configured === 'string' && tree.exists(configured)) {
|
|
42
|
+
paths.add(configured);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
for (const extension of CONFIG_EXTENSIONS) {
|
|
47
|
+
const conventional = `${project.root}/webpack.config.${extension}`;
|
|
48
|
+
if (tree.exists(conventional)) {
|
|
49
|
+
paths.add(conventional);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return paths;
|
|
53
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Tree } from '@nx/devkit';
|
|
2
|
+
export default function migrateComposePlugins(tree: Tree): Promise<{
|
|
3
|
+
skipAgentic: boolean;
|
|
4
|
+
nextSteps?: undefined;
|
|
5
|
+
agentContext?: undefined;
|
|
6
|
+
} | {
|
|
7
|
+
skipAgentic?: undefined;
|
|
8
|
+
nextSteps: string[];
|
|
9
|
+
agentContext: string[];
|
|
10
|
+
}>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = migrateComposePlugins;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const convert_config_to_webpack_plugin_1 = require("../../generators/convert-config-to-webpack-plugin/convert-config-to-webpack-plugin");
|
|
6
|
+
const inventory_1 = require("./inventory");
|
|
7
|
+
const CONVERTER = '@nx/webpack:convert-config-to-webpack-plugin';
|
|
8
|
+
async function migrateComposePlugins(tree) {
|
|
9
|
+
const usages = (0, inventory_1.inventoryComposePlugins)(tree);
|
|
10
|
+
if (!usages.length) {
|
|
11
|
+
return { skipAgentic: true };
|
|
12
|
+
}
|
|
13
|
+
const converted = [];
|
|
14
|
+
const remaining = [];
|
|
15
|
+
for (const usage of usages) {
|
|
16
|
+
if (!usage.usesHelpers) {
|
|
17
|
+
converted.push(usage);
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
await (0, convert_config_to_webpack_plugin_1.convertConfigToWebpackPluginGenerator)(tree, {
|
|
22
|
+
project: usage.name,
|
|
23
|
+
skipFormat: true,
|
|
24
|
+
});
|
|
25
|
+
converted.push(usage);
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
remaining.push({ usage, reason: e.message });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (converted.length) {
|
|
32
|
+
await (0, devkit_1.formatFiles)(tree);
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
nextSteps: nextSteps(converted, remaining),
|
|
36
|
+
agentContext: agentContext(converted, remaining),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function nextSteps(converted, remaining) {
|
|
40
|
+
const steps = [];
|
|
41
|
+
if (converted.length) {
|
|
42
|
+
steps.push(`Inline the \`webpack.config.old.*\` files that ${CONVERTER} left behind in ${converted.length} project(s), then drop \`useLegacyNxPlugin\`.`);
|
|
43
|
+
}
|
|
44
|
+
if (remaining.length) {
|
|
45
|
+
steps.push(`Migrate ${remaining.length} project(s) off the removed webpack config helpers by hand: ${remaining
|
|
46
|
+
.map(({ usage }) => usage.name)
|
|
47
|
+
.join(', ')}.`);
|
|
48
|
+
}
|
|
49
|
+
return steps;
|
|
50
|
+
}
|
|
51
|
+
function agentContext(converted, remaining) {
|
|
52
|
+
const context = [];
|
|
53
|
+
if (converted.length) {
|
|
54
|
+
context.push(`${CONVERTER} converted these projects. Each one now has a standard config whose \`useLegacyNxPlugin\` call still loads the renamed \`.old\` config, so the helper callbacks it holds have to move into the new config before the \`.old\` file can go:`, ...converted.map((usage) => `- ${usage.name} (${usage.root}): ${usage.configPaths.join(', ')}`));
|
|
55
|
+
}
|
|
56
|
+
if (remaining.length) {
|
|
57
|
+
context.push(`${CONVERTER} could not convert these projects, so their configs are untouched and still call the removed helpers. Rewrite each one by hand:`, ...remaining.map(({ usage, reason }) => `- ${usage.name} (${usage.root}): ${usage.configPaths.join(', ')} - ${reason}`));
|
|
58
|
+
}
|
|
59
|
+
return context;
|
|
60
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Migrate webpack compose helpers
|
|
2
|
+
|
|
3
|
+
Nx v24 removed `composePlugins`, `composePluginsSync`, `withNx`, `withWeb`, and webpack
|
|
4
|
+
`withReact`. Their exports remain as inert stubs so existing configs still load while Nx
|
|
5
|
+
builds the project graph. They do not configure builds, and a config that loads is not a
|
|
6
|
+
working build.
|
|
7
|
+
|
|
8
|
+
`@nx/webpack:convert-config-to-webpack-plugin` already ran. The agent context lists the
|
|
9
|
+
projects it converted, including configs an earlier conversion left wrapped in
|
|
10
|
+
`useLegacyNxPlugin`, and, separately, the projects it could not convert with the reason
|
|
11
|
+
for each. Work only on those projects and the files they import. Do not re-inventory the
|
|
12
|
+
workspace, and do not run the converter again.
|
|
13
|
+
|
|
14
|
+
Preserve the installed webpack version, bundler, targets, configurations, and commands.
|
|
15
|
+
Nx 24 also removed the `@nx/webpack:webpack` executor. Its targets are converted to inferred
|
|
16
|
+
`@nx/webpack/plugin` tasks by a separate migration, so do not add it back or keep a target on it.
|
|
17
|
+
|
|
18
|
+
## Baseline
|
|
19
|
+
|
|
20
|
+
This workspace is already on v24, so the old helpers no longer build here. To compare
|
|
21
|
+
against the previous behavior, check out the commit before the upgrade in a separate git
|
|
22
|
+
worktree, install with its lockfile, and build there. Do not downgrade this workspace. If
|
|
23
|
+
you cannot build a baseline, say so in your report rather than calling the comparison a
|
|
24
|
+
pass. Record build commands, output locations, assets, entry points, and any observable
|
|
25
|
+
custom behavior.
|
|
26
|
+
|
|
27
|
+
## Projects the converter handled
|
|
28
|
+
|
|
29
|
+
Each one now has a standard config built around `NxAppWebpackPlugin`, plus a
|
|
30
|
+
`useLegacyNxPlugin(require('./webpack.config.old.*'), options)` entry that still loads the
|
|
31
|
+
renamed old config. The old config composes its remaining callbacks through a local
|
|
32
|
+
`composeLegacyPlugins` helper the converter inlined. Two things are left:
|
|
33
|
+
|
|
34
|
+
- Move what the old config still does into the new config, then delete the `.old` file and
|
|
35
|
+
drop `useLegacyNxPlugin`.
|
|
36
|
+
- `useLegacyNxPlugin` runs at `beforeCompile`. That is too late to register rules, entries,
|
|
37
|
+
or plugins, so anything the old config set through those is already not applying the way
|
|
38
|
+
it used to. Check each one against the baseline instead of trusting the converted build.
|
|
39
|
+
|
|
40
|
+
Also check the options the converter copied into the new config. It reads them out of the
|
|
41
|
+
old `withNx` call as written, so values that depended on the executor or on environment
|
|
42
|
+
variables are not represented, and workspace-relative paths may need adjusting: native
|
|
43
|
+
plugin options resolve against the project and config directory, while executor options
|
|
44
|
+
were workspace-relative.
|
|
45
|
+
|
|
46
|
+
## Projects the converter skipped
|
|
47
|
+
|
|
48
|
+
The agent context gives a reason per project. Those configs are untouched and still call
|
|
49
|
+
the removed helpers, so rewrite each one.
|
|
50
|
+
|
|
51
|
+
Use a standard webpack configuration with `NxAppWebpackPlugin` from
|
|
52
|
+
`@nx/webpack/app-plugin`. For React apps also use `NxReactWebpackPlugin` from
|
|
53
|
+
`@nx/react/webpack-plugin`. Do not add React configuration to Node or plain web apps, and
|
|
54
|
+
do not change frameworks or upgrade dependencies to simplify the rewrite.
|
|
55
|
+
|
|
56
|
+
Map the effective old options onto those plugins and standard webpack properties, reading
|
|
57
|
+
the installed plugin types rather than assuming. Preserve precedence between target
|
|
58
|
+
options, named configurations, helper options, and custom callbacks. Preserve compiler
|
|
59
|
+
choice, aliases, CSS, SVG rules, assets, externals, file replacements, source maps,
|
|
60
|
+
optimization, public paths, output filenames, and dev-server behavior. Set output hashing
|
|
61
|
+
explicitly where the old helper defaults differ from the plugin defaults; a build that
|
|
62
|
+
succeeds with renamed entry files is not output parity.
|
|
63
|
+
|
|
64
|
+
A `webpackConfig` target option naming a package path rather than a local file, such as
|
|
65
|
+
`@nx/react/plugins/webpack`, resolves to the stubs. Give that project its own config file.
|
|
66
|
+
|
|
67
|
+
Inferred targets run the webpack CLI, which calls a function export with `(env, argv)`.
|
|
68
|
+
Represent the options the executor used to supply in the config itself. Use config
|
|
69
|
+
functions or separate configs where values vary by configuration.
|
|
70
|
+
|
|
71
|
+
For custom composable functions, move the function into user-owned code and supply the
|
|
72
|
+
options and context it needs explicitly. A local adapter exposing `apply(compiler)` can
|
|
73
|
+
work, but an `apply` object belongs in `config.plugins`; exporting it as the whole config
|
|
74
|
+
is invalid, and a top-level config function must return a config object or supported array.
|
|
75
|
+
Preserve execution order and whether each callback mutates its input or returns a
|
|
76
|
+
replacement. Await asynchronous transforms and propagate failures rather than leaving a
|
|
77
|
+
pending promise.
|
|
78
|
+
|
|
79
|
+
## Module Federation
|
|
80
|
+
|
|
81
|
+
Projects using Nx Module Federation are out of scope here. `@nx/module-federation` ships
|
|
82
|
+
its own v24 migration; follow that one. Do not remove federation, disable the project, or
|
|
83
|
+
report success for it from this migration.
|
|
84
|
+
|
|
85
|
+
## Verify and finish
|
|
86
|
+
|
|
87
|
+
1. Rebuild the project graph without its disk cache and confirm the same projects and
|
|
88
|
+
targets are still discoverable.
|
|
89
|
+
2. Run each affected build, development and production and any alternate configurations,
|
|
90
|
+
without reusing cached task outputs. Exercise Node startup where it applies, plus tests
|
|
91
|
+
and dev-server or HMR behavior.
|
|
92
|
+
3. Compare emitted files against the baseline: custom assets and markers, entry points,
|
|
93
|
+
aliases, CSS and SVG handling, externals, and output paths. Compare content where hashes
|
|
94
|
+
differ. A zero exit code is not enough.
|
|
95
|
+
4. Search the active configs and their imports for the removed helpers again. An already
|
|
96
|
+
migrated config needs no further change.
|
|
97
|
+
5. Report the commands you ran, the output you checked, and anything still unresolved. Do
|
|
98
|
+
not delete tests, custom behavior, or targets to get a green result.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = update;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const internal_1 = require("@nx/devkit/internal");
|
|
6
|
+
const convert_config_to_webpack_plugin_1 = require("../../generators/convert-config-to-webpack-plugin/convert-config-to-webpack-plugin");
|
|
7
|
+
const inventory_1 = require("./inventory");
|
|
8
|
+
function update(tree) {
|
|
9
|
+
return (0, internal_1.migrateRemovedExecutors)(tree, [
|
|
10
|
+
'@nx/webpack:webpack',
|
|
11
|
+
'@nx/webpack:dev-server',
|
|
12
|
+
'@nrwl/webpack:webpack',
|
|
13
|
+
'@nrwl/webpack:dev-server',
|
|
14
|
+
], async (tree, options) => {
|
|
15
|
+
await convertHelperConfigs(tree);
|
|
16
|
+
const { convertToInferred } = require('../../generators/convert-to-inferred/convert-to-inferred');
|
|
17
|
+
return convertToInferred(tree, options);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
// convert-to-inferred skips configs built on the removed helpers, so convert
|
|
21
|
+
// those first. migrate-compose-plugins reports the `.old` configs left behind.
|
|
22
|
+
async function convertHelperConfigs(tree) {
|
|
23
|
+
for (const usage of (0, inventory_1.inventoryComposePlugins)(tree)) {
|
|
24
|
+
if (!usage.usesExecutor || !usage.usesHelpers)
|
|
25
|
+
continue;
|
|
26
|
+
try {
|
|
27
|
+
await (0, convert_config_to_webpack_plugin_1.convertConfigToWebpackPluginGenerator)(tree, {
|
|
28
|
+
project: usage.name,
|
|
29
|
+
skipFormat: true,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
devkit_1.logger.warn(`Could not convert the webpack config helpers in ${usage.name}: ${e.message}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#### Convert `@nx/webpack` build and dev-server targets to inferred tasks
|
|
2
|
+
|
|
3
|
+
Nx 24 removes the `@nx/webpack:webpack` and `@nx/webpack:dev-server` executors and their `@nrwl` aliases. The migration registers `@nx/webpack/plugin` in `nx.json` and converts the targets that use those executors to inferred tasks. It moves the executor options into `webpack.config.*`. A target with nothing left to override is removed from project configuration. When several projects share the same remaining options, those options move to a `targetDefaults` entry scoped to the plugin. `targetDefaults` entries keyed by a removed executor are removed once no target uses that executor.
|
|
4
|
+
|
|
5
|
+
The migration lists the targets the converter cannot convert safely. In agentic runs, an AI prompt finishes them and validates the project graph.
|
|
6
|
+
|
|
7
|
+
The `@nx/webpack:ssr-dev-server` executor stays.
|
|
8
|
+
|
|
9
|
+
#### Sample code changes
|
|
10
|
+
|
|
11
|
+
The example shows the `build` target. Its options move into `webpack.config.*`, so only the configuration names stay on the target.
|
|
12
|
+
|
|
13
|
+
##### Before
|
|
14
|
+
|
|
15
|
+
```json title="apps/app1/project.json"
|
|
16
|
+
{
|
|
17
|
+
"name": "app1",
|
|
18
|
+
"targets": {
|
|
19
|
+
"build": {
|
|
20
|
+
"executor": "@nx/webpack:webpack",
|
|
21
|
+
"options": {
|
|
22
|
+
"webpackConfig": "apps/app1/webpack.config.js",
|
|
23
|
+
"outputPath": "dist/apps/app1",
|
|
24
|
+
"index": "apps/app1/src/index.html",
|
|
25
|
+
"main": "apps/app1/src/main.tsx",
|
|
26
|
+
"tsConfig": "apps/app1/tsconfig.app.json",
|
|
27
|
+
"assets": ["apps/app1/src/favicon.ico", "apps/app1/src/assets"],
|
|
28
|
+
"styles": ["apps/app1/src/styles.scss"],
|
|
29
|
+
"scripts": [],
|
|
30
|
+
"compiler": "babel",
|
|
31
|
+
"baseHref": "/"
|
|
32
|
+
},
|
|
33
|
+
"configurations": {
|
|
34
|
+
"development": {
|
|
35
|
+
"extractLicenses": false,
|
|
36
|
+
"optimization": false,
|
|
37
|
+
"sourceMap": true,
|
|
38
|
+
"vendorChunk": true
|
|
39
|
+
},
|
|
40
|
+
"production": {
|
|
41
|
+
"fileReplacements": [
|
|
42
|
+
{
|
|
43
|
+
"replace": "apps/app1/src/environments/environment.ts",
|
|
44
|
+
"with": "apps/app1/src/environments/environment.prod.ts"
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"optimization": true,
|
|
48
|
+
"outputHashing": "all",
|
|
49
|
+
"sourceMap": false,
|
|
50
|
+
"namedChunks": false,
|
|
51
|
+
"extractLicenses": true,
|
|
52
|
+
"vendorChunk": false
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"defaultConfiguration": "production"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
##### After
|
|
62
|
+
|
|
63
|
+
```json title="apps/app1/project.json"
|
|
64
|
+
{
|
|
65
|
+
"name": "app1",
|
|
66
|
+
"targets": {
|
|
67
|
+
"build": {
|
|
68
|
+
"configurations": {
|
|
69
|
+
"development": {},
|
|
70
|
+
"production": {}
|
|
71
|
+
},
|
|
72
|
+
"defaultConfiguration": "production"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```json title="nx.json"
|
|
79
|
+
{
|
|
80
|
+
"plugins": [
|
|
81
|
+
{
|
|
82
|
+
"plugin": "@nx/webpack/plugin",
|
|
83
|
+
"options": {
|
|
84
|
+
"buildTargetName": "build",
|
|
85
|
+
"previewTargetName": "preview",
|
|
86
|
+
"serveStaticTargetName": "serve-static",
|
|
87
|
+
"serveTargetName": "serve"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
```
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { type Target } from '@nx/devkit';
|
|
1
3
|
import { AssetGlobPattern, FileReplacement, NormalizedNxAppWebpackPluginOptions, NxAppWebpackPluginOptions } from '../nx-app-webpack-plugin-options';
|
|
4
|
+
export declare const webpackExecutorContext: AsyncLocalStorage<{
|
|
5
|
+
options: Partial<NxAppWebpackPluginOptions>;
|
|
6
|
+
target: Target;
|
|
7
|
+
}>;
|
|
2
8
|
export declare function normalizeOptions(options: NxAppWebpackPluginOptions): NormalizedNxAppWebpackPluginOptions;
|
|
3
9
|
export declare function normalizeAssets(assets: any[], root: string, sourceRoot: string, projectRoot: string, resolveRelativePathsToProjectRoot?: boolean): AssetGlobPattern[];
|
|
4
10
|
export declare function normalizeFileReplacements(root: string, fileReplacements: FileReplacement[]): FileReplacement[];
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.webpackExecutorContext = void 0;
|
|
3
4
|
exports.normalizeOptions = normalizeOptions;
|
|
4
5
|
exports.normalizeAssets = normalizeAssets;
|
|
5
6
|
exports.normalizeFileReplacements = normalizeFileReplacements;
|
|
7
|
+
const node_async_hooks_1 = require("node:async_hooks");
|
|
6
8
|
const devkit_1 = require("@nx/devkit");
|
|
7
9
|
const internal_1 = require("@nx/js/internal");
|
|
8
10
|
const fs_1 = require("fs");
|
|
9
11
|
const path_1 = require("path");
|
|
12
|
+
exports.webpackExecutorContext = new node_async_hooks_1.AsyncLocalStorage();
|
|
10
13
|
function normalizeOptions(options) {
|
|
11
14
|
const combinedPluginAndMaybeExecutorOptions = {};
|
|
12
15
|
const isProd = process.env.NODE_ENV === 'production';
|
|
13
16
|
// Since this is invoked by the executor, the graph has already been created and cached.
|
|
14
17
|
const projectGraph = (0, devkit_1.readCachedProjectGraph)();
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
const executorContext = exports.webpackExecutorContext.getStore();
|
|
19
|
+
const taskDetailsFromBuildTarget = executorContext?.target ??
|
|
20
|
+
(process.env.NX_BUILD_TARGET
|
|
21
|
+
? (0, devkit_1.parseTargetString)(process.env.NX_BUILD_TARGET, projectGraph)
|
|
22
|
+
: undefined);
|
|
18
23
|
const projectName = taskDetailsFromBuildTarget
|
|
19
24
|
? taskDetailsFromBuildTarget.project
|
|
20
25
|
: process.env.NX_TASK_TARGET_PROJECT;
|
|
@@ -29,26 +34,15 @@ function normalizeOptions(options) {
|
|
|
29
34
|
normalizeRelativePaths(projectNode.data.root, options);
|
|
30
35
|
// Merge options from `@nx/webpack:webpack` into plugin options.
|
|
31
36
|
// Options from `@nx/webpack:webpack` take precedence.
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
Object.assign(buildTargetOptions, targetConfig.configurations?.[configurationName]);
|
|
42
|
-
}
|
|
43
|
-
Object.assign(combinedPluginAndMaybeExecutorOptions, options,
|
|
44
|
-
// executor options take precedence (especially for overriding with CLI args)
|
|
45
|
-
buildTargetOptions);
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
Object.assign(combinedPluginAndMaybeExecutorOptions, options,
|
|
49
|
-
// executor options take precedence (especially for overriding with CLI args)
|
|
50
|
-
originalTargetOptions);
|
|
51
|
-
}
|
|
37
|
+
const targetOptions = {
|
|
38
|
+
...targetConfig.options,
|
|
39
|
+
...(configurationName
|
|
40
|
+
? targetConfig.configurations?.[configurationName]
|
|
41
|
+
: undefined),
|
|
42
|
+
};
|
|
43
|
+
Object.assign(combinedPluginAndMaybeExecutorOptions, options, targetOptions);
|
|
44
|
+
const executorOptions = executorContext?.options;
|
|
45
|
+
Object.assign(combinedPluginAndMaybeExecutorOptions, Object.fromEntries(Object.entries(executorOptions ?? {}).filter(([key, value]) => value !== undefined && !isExecutorSchemaDefault(key, value))));
|
|
52
46
|
const sourceRoot = (0, internal_1.getProjectSourceRoot)(projectNode.data);
|
|
53
47
|
if (!combinedPluginAndMaybeExecutorOptions.main) {
|
|
54
48
|
throw new Error(`Missing "main" option for the entry file. Set this option in your Nx webpack plugin.`);
|
|
@@ -56,7 +50,7 @@ function normalizeOptions(options) {
|
|
|
56
50
|
return {
|
|
57
51
|
...combinedPluginAndMaybeExecutorOptions,
|
|
58
52
|
assets: combinedPluginAndMaybeExecutorOptions.assets
|
|
59
|
-
? normalizeAssets(combinedPluginAndMaybeExecutorOptions.assets, devkit_1.workspaceRoot, sourceRoot, projectNode.data.root)
|
|
53
|
+
? normalizeAssets(combinedPluginAndMaybeExecutorOptions.assets, devkit_1.workspaceRoot, sourceRoot, projectNode.data.root, executorOptions?.assets === undefined)
|
|
60
54
|
: [],
|
|
61
55
|
baseHref: combinedPluginAndMaybeExecutorOptions.baseHref ?? '/',
|
|
62
56
|
buildLibsFromSource: combinedPluginAndMaybeExecutorOptions.buildLibsFromSource ?? true,
|
|
@@ -163,6 +157,17 @@ function normalizeRelativePaths(projectRoot, options) {
|
|
|
163
157
|
}
|
|
164
158
|
}
|
|
165
159
|
}
|
|
160
|
+
// Executor-style callers pass options with the removed @nx/webpack:webpack
|
|
161
|
+
// schema defaults applied. Those defaults must not override the plugin's own
|
|
162
|
+
// options; target options still apply above.
|
|
163
|
+
const executorSchemaDefaults = {
|
|
164
|
+
outputFileName: 'main.js',
|
|
165
|
+
standardWebpackConfigFunction: false,
|
|
166
|
+
buildLibsFromSource: true,
|
|
167
|
+
};
|
|
168
|
+
function isExecutorSchemaDefault(key, value) {
|
|
169
|
+
return key in executorSchemaDefaults && executorSchemaDefaults[key] === value;
|
|
170
|
+
}
|
|
166
171
|
function isRelativePath(val) {
|
|
167
172
|
return (typeof val === 'string' &&
|
|
168
173
|
(val.startsWith('./') ||
|