@nx/webpack 24.0.0-pr.37405.acdaf31 → 24.0.0-rc.0
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/ready-check.d.ts +1 -0
- package/dist/ready-check.js +9 -0
- package/dist/ready-signal.d.ts +1 -0
- package/dist/ready-signal.js +5 -0
- package/dist/src/executors/dev-server/dev-server.impl.js +38 -30
- package/dist/src/executors/ssr-dev-server/schema.json +2 -1
- package/dist/src/executors/ssr-dev-server/ssr-dev-server.impl.js +2 -0
- 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 +97 -57
- package/dist/src/generators/convert-config-to-webpack-plugin/lib/extract-webpack-options.d.ts +2 -0
- package/dist/src/generators/convert-config-to-webpack-plugin/lib/extract-webpack-options.js +46 -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/ast.d.ts +2 -0
- package/dist/src/generators/convert-to-inferred/utils/ast.js +13 -0
- package/dist/src/generators/convert-to-inferred/utils/build-post-target-transformer.js +25 -5
- package/dist/src/generators/convert-to-inferred/utils/serve-post-target-transformer.js +13 -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 +10 -0
- package/dist/src/migrations/update-24-0-0/inventory.js +55 -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 +68 -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 +39 -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.d.ts +2 -0
- package/dist/src/plugins/nx-webpack-plugin/nx-app-webpack-plugin.js +18 -1
- package/dist/src/plugins/plugin.js +18 -0
- package/dist/src/plugins/use-legacy-nx-plugin/use-legacy-nx-plugin.js +3 -0
- package/dist/src/utils/config.d.ts +8 -12
- package/dist/src/utils/config.js +15 -68
- package/dist/src/utils/deprecation.d.ts +3 -6
- package/dist/src/utils/deprecation.js +10 -27
- package/dist/src/utils/dev-server-ready-check.d.ts +36 -0
- package/dist/src/utils/dev-server-ready-check.js +255 -0
- package/dist/src/utils/dev-server-ready-signal.d.ts +29 -0
- package/dist/src/utils/dev-server-ready-signal.js +51 -0
- 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/webpack/read-webpack-options.d.ts +1 -1
- package/dist/src/utils/webpack/read-webpack-options.js +4 -4
- 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 +20 -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
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/schema",
|
|
3
3
|
"$id": "NxWebpackConvertConfigToWebpackPlugin",
|
|
4
|
-
"description": "Convert existing Webpack
|
|
4
|
+
"description": "Convert existing Webpack config(s) that use `withNx` to use `NxAppWebpackPlugin`, for projects on the `@nx/webpack:webpack` executor or the `@nx/webpack/plugin` inference plugin. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
|
5
5
|
"title": "Convert Webpack project using withNx to NxAppWebpackPlugin",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"properties": {
|
|
8
8
|
"project": {
|
|
9
9
|
"type": "string",
|
|
10
|
-
"description": "The project to convert from
|
|
10
|
+
"description": "The project whose webpack config to convert from the `withNx` plugin to `NxAppWebpackPlugin`.",
|
|
11
11
|
"x-priority": "important"
|
|
12
12
|
},
|
|
13
13
|
"skipFormat": {
|
|
@@ -40,7 +40,7 @@ async function convertToInferred(tree, options) {
|
|
|
40
40
|
const convertMessage = [...logger.loggedMessages.values()]
|
|
41
41
|
.flat()
|
|
42
42
|
.find((v) => v.includes('@nx/webpack:convert-config-to-webpack-plugin'));
|
|
43
|
-
if (convertMessage
|
|
43
|
+
if (convertMessage?.length > 0) {
|
|
44
44
|
logger.flushLogs((message) => !convertMessage.includes(message));
|
|
45
45
|
throw new Error(convertMessage);
|
|
46
46
|
}
|
|
@@ -56,9 +56,10 @@ async function convertToInferred(tree, options) {
|
|
|
56
56
|
if (!options.skipFormat) {
|
|
57
57
|
await (0, devkit_1.formatFiles)(tree);
|
|
58
58
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
// Flushed now rather than in the callback so remove-deprecated-executors, which
|
|
60
|
+
// skips the install callback, still prints these.
|
|
61
|
+
migrationContext.logger.flushLogs();
|
|
62
|
+
return installCallback;
|
|
62
63
|
}
|
|
63
64
|
function skipProjectFilterFactory(tree) {
|
|
64
65
|
return function skipProjectFilter(projectConfiguration) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
2
|
export declare function toPropertyAssignment(key: string, value: unknown): ts.PropertyAssignment;
|
|
3
3
|
export declare function toExpression(value: unknown): ts.Expression;
|
|
4
|
+
/** Whether a top-level `configValues` object written by convert-to-inferred has `key`. */
|
|
5
|
+
export declare function hasMigratedConfigValues(sourceFile: ts.SourceFile, key: string): boolean;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.toPropertyAssignment = toPropertyAssignment;
|
|
4
4
|
exports.toExpression = toExpression;
|
|
5
|
+
exports.hasMigratedConfigValues = hasMigratedConfigValues;
|
|
5
6
|
const tslib_1 = require("tslib");
|
|
6
7
|
const ts = tslib_1.__importStar(require("typescript"));
|
|
7
8
|
function toPropertyAssignment(key, value) {
|
|
@@ -38,3 +39,15 @@ function toExpression(value) {
|
|
|
38
39
|
return ts.factory.createObjectLiteralExpression(Object.entries(value).map(([key, value]) => toPropertyAssignment(key, value)));
|
|
39
40
|
}
|
|
40
41
|
}
|
|
42
|
+
/** Whether a top-level `configValues` object written by convert-to-inferred has `key`. */
|
|
43
|
+
function hasMigratedConfigValues(sourceFile, key) {
|
|
44
|
+
return sourceFile.statements.some((statement) => ts.isVariableStatement(statement) &&
|
|
45
|
+
statement.declarationList.declarations.some((declaration) => ts.isIdentifier(declaration.name) &&
|
|
46
|
+
declaration.name.text === 'configValues' &&
|
|
47
|
+
declaration.initializer !== undefined &&
|
|
48
|
+
ts.isObjectLiteralExpression(declaration.initializer) &&
|
|
49
|
+
declaration.initializer.properties.some((property) => ts.isPropertyAssignment(property) &&
|
|
50
|
+
(ts.isIdentifier(property.name) ||
|
|
51
|
+
ts.isStringLiteral(property.name)) &&
|
|
52
|
+
property.name.text === key)));
|
|
53
|
+
}
|
|
@@ -13,6 +13,13 @@ function buildPostTargetTransformerFactory(migrationContext) {
|
|
|
13
13
|
projectName: projectDetails.projectName,
|
|
14
14
|
projectRoot: projectDetails.root,
|
|
15
15
|
};
|
|
16
|
+
// outputPath moves into the config, where `{options.outputPath}` can't resolve.
|
|
17
|
+
const outputPath = target.options?.outputPath;
|
|
18
|
+
if (target.outputs && typeof outputPath === 'string') {
|
|
19
|
+
target.outputs = target.outputs.map((output) => output.startsWith('{options.outputPath}')
|
|
20
|
+
? output.replace('{options.outputPath}', `{workspaceRoot}/${outputPath}`)
|
|
21
|
+
: output.replace('{options.outputPath}', outputPath));
|
|
22
|
+
}
|
|
16
23
|
const { pluginOptions, webpackConfigPath } = processOptions(target, context);
|
|
17
24
|
updateWebpackConfig(tree, webpackConfigPath, pluginOptions);
|
|
18
25
|
if (target.outputs) {
|
|
@@ -109,6 +116,11 @@ function updateWebpackConfig(tree, webpackConfig, pluginOptions) {
|
|
|
109
116
|
sourceFile = (0, tsquery_1.ast)(webpackConfigText);
|
|
110
117
|
};
|
|
111
118
|
updateSources();
|
|
119
|
+
// A target kept on its executor is converted again on rerun, and its options
|
|
120
|
+
// are already in the config.
|
|
121
|
+
if ((0, ast_1.hasMigratedConfigValues)(sourceFile, 'build')) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
112
124
|
setOptionsInWebpackConfig(tree, webpackConfigText, sourceFile, webpackConfig, pluginOptions);
|
|
113
125
|
updateSources();
|
|
114
126
|
setOptionsInNxWebpackPlugin(tree, webpackConfigText, sourceFile, webpackConfig);
|
|
@@ -118,9 +130,17 @@ function updateWebpackConfig(tree, webpackConfig, pluginOptions) {
|
|
|
118
130
|
function setOptionsInWebpackConfig(tree, text, sourceFile, webpackConfig, pluginOptions) {
|
|
119
131
|
const { default: defaultOptions, ...configurationOptions } = pluginOptions;
|
|
120
132
|
const optionsSelector = 'VariableStatement:has(VariableDeclaration:has(Identifier[name=options]))';
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
133
|
+
let optionsVariable = (0, tsquery_1.query)(sourceFile, optionsSelector)[0];
|
|
134
|
+
if (!optionsVariable) {
|
|
135
|
+
const plugin = (0, tsquery_1.query)(sourceFile, 'NewExpression:has(Identifier[name=NxAppWebpackPlugin])')[0];
|
|
136
|
+
const argument = plugin.arguments?.[0];
|
|
137
|
+
if (argument && !ts.isObjectLiteralExpression(argument)) {
|
|
138
|
+
throw new Error('Move NxAppWebpackPlugin options into an object literal before converting this config.');
|
|
139
|
+
}
|
|
140
|
+
text = `const options = ${argument?.getText(sourceFile) ?? '{}'};\n${text}`;
|
|
141
|
+
sourceFile = (0, tsquery_1.ast)(text);
|
|
142
|
+
optionsVariable = (0, tsquery_1.query)(sourceFile, optionsSelector)[0];
|
|
143
|
+
}
|
|
124
144
|
let defaultOptionsObject;
|
|
125
145
|
const optionsObject = (0, tsquery_1.query)(optionsVariable, 'ObjectLiteralExpression')[0];
|
|
126
146
|
if (optionsObject.properties.length === 0) {
|
|
@@ -188,8 +208,8 @@ function setOptionsInNxWebpackPlugin(tree, text, sourceFile, webpackConfig) {
|
|
|
188
208
|
function setOptionsInLegacyNxPlugin(tree, text, sourceFile, webpackConfig) {
|
|
189
209
|
const legacyNxPluginSelector = 'AwaitExpression CallExpression:has(Identifier[name=useLegacyNxPlugin])';
|
|
190
210
|
const legacyNxPlugin = (0, tsquery_1.query)(sourceFile, legacyNxPluginSelector)[0];
|
|
191
|
-
|
|
192
|
-
|
|
211
|
+
if (!legacyNxPlugin)
|
|
212
|
+
return;
|
|
193
213
|
const updatedLegacyNxPlugin = ts.factory.updateCallExpression(legacyNxPlugin, legacyNxPlugin.expression, undefined, [legacyNxPlugin.arguments[0], ts.factory.createIdentifier('buildOptions')]);
|
|
194
214
|
text = `${text.slice(0, legacyNxPlugin.getStart())}${ts
|
|
195
215
|
.createPrinter()
|
|
@@ -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);
|
|
@@ -183,6 +177,11 @@ function updateWebpackConfig(tree, webpackConfigPath, devServerOptions, context)
|
|
|
183
177
|
sourceFile = (0, tsquery_1.ast)(webpackConfigText);
|
|
184
178
|
};
|
|
185
179
|
updateSources();
|
|
180
|
+
// A target kept on its executor is converted again on rerun, and its options
|
|
181
|
+
// are already in the config.
|
|
182
|
+
if ((0, ast_1.hasMigratedConfigValues)(sourceFile, 'serve')) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
186
185
|
setOptionsInWebpackConfig(tree, webpackConfigText, sourceFile, webpackConfigPath, devServerOptions);
|
|
187
186
|
updateSources();
|
|
188
187
|
setDevServerOptionsInWebpackConfig(tree, webpackConfigText, sourceFile, webpackConfigPath, 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,10 @@
|
|
|
1
|
+
import { type Tree } from '@nx/devkit';
|
|
2
|
+
export interface HelperUsage {
|
|
3
|
+
name: string;
|
|
4
|
+
root: string;
|
|
5
|
+
configPaths: string[];
|
|
6
|
+
usesHelpers: boolean;
|
|
7
|
+
usesModuleFederation: boolean;
|
|
8
|
+
usesExecutor: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function inventoryComposePlugins(tree: Tree): HelperUsage[];
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
const MODULE_FEDERATION_HELPERS = /\bwithModuleFederation(ForSSR)?\b/;
|
|
10
|
+
function inventoryComposePlugins(tree) {
|
|
11
|
+
const usages = [];
|
|
12
|
+
for (const [name, project] of (0, devkit_1.getProjects)(tree)) {
|
|
13
|
+
const configs = [...collectConfigPaths(tree, project)].map((path) => ({
|
|
14
|
+
path,
|
|
15
|
+
source: tree.read(path, 'utf-8') ?? '',
|
|
16
|
+
}));
|
|
17
|
+
const configPaths = configs
|
|
18
|
+
.filter(({ source }) => REMOVED_HELPERS.test(source) || LEGACY_WRAPPER.test(source))
|
|
19
|
+
.map(({ path }) => path);
|
|
20
|
+
if (!configPaths.length)
|
|
21
|
+
continue;
|
|
22
|
+
usages.push({
|
|
23
|
+
name,
|
|
24
|
+
root: project.root,
|
|
25
|
+
configPaths: configPaths.sort(),
|
|
26
|
+
usesHelpers: configs.some(({ source }) => REMOVED_HELPERS.test(source)),
|
|
27
|
+
usesModuleFederation: configs.some(({ source }) => MODULE_FEDERATION_HELPERS.test(source)),
|
|
28
|
+
usesExecutor: Object.values(project.targets ?? {}).some((target) => ['@nx/webpack:webpack', '@nrwl/webpack:webpack'].includes(target.executor)),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return usages;
|
|
32
|
+
}
|
|
33
|
+
function collectConfigPaths(tree, project) {
|
|
34
|
+
const paths = new Set();
|
|
35
|
+
for (const target of Object.values(project.targets ?? {})) {
|
|
36
|
+
for (const options of [
|
|
37
|
+
target.options,
|
|
38
|
+
...Object.values(target.configurations ?? {}),
|
|
39
|
+
]) {
|
|
40
|
+
const configured = options?.webpackConfig;
|
|
41
|
+
// A package path such as `@nx/react/plugins/webpack` resolves to the stubs
|
|
42
|
+
// rather than a workspace file; the prompt gives those projects a config.
|
|
43
|
+
if (typeof configured === 'string' && tree.exists(configured)) {
|
|
44
|
+
paths.add(configured);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
for (const extension of CONFIG_EXTENSIONS) {
|
|
49
|
+
const conventional = `${project.root}/webpack.config.${extension}`;
|
|
50
|
+
if (tree.exists(conventional)) {
|
|
51
|
+
paths.add(conventional);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return paths;
|
|
55
|
+
}
|
|
@@ -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,68 @@
|
|
|
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
|
+
const moduleFederation = [];
|
|
16
|
+
for (const usage of usages) {
|
|
17
|
+
if (!usage.usesHelpers) {
|
|
18
|
+
converted.push(usage);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (usage.usesModuleFederation) {
|
|
22
|
+
moduleFederation.push(usage);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
await (0, convert_config_to_webpack_plugin_1.convertConfigToWebpackPluginGenerator)(tree, {
|
|
27
|
+
project: usage.name,
|
|
28
|
+
skipFormat: true,
|
|
29
|
+
});
|
|
30
|
+
converted.push(usage);
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
remaining.push({ usage, reason: e.message });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (converted.length) {
|
|
37
|
+
await (0, devkit_1.formatFiles)(tree);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
nextSteps: nextSteps(converted, remaining),
|
|
41
|
+
agentContext: agentContext(converted, remaining, moduleFederation),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function nextSteps(converted, remaining) {
|
|
45
|
+
const steps = [];
|
|
46
|
+
if (converted.length) {
|
|
47
|
+
steps.push(`Inline the \`webpack.config.old.*\` files that ${CONVERTER} left behind in ${converted.length} project(s), then drop \`useLegacyNxPlugin\`.`);
|
|
48
|
+
}
|
|
49
|
+
if (remaining.length) {
|
|
50
|
+
steps.push(`Migrate ${remaining.length} project(s) off the removed webpack config helpers by hand: ${remaining
|
|
51
|
+
.map(({ usage }) => usage.name)
|
|
52
|
+
.join(', ')}.`);
|
|
53
|
+
}
|
|
54
|
+
return steps;
|
|
55
|
+
}
|
|
56
|
+
function agentContext(converted, remaining, moduleFederation) {
|
|
57
|
+
const context = [];
|
|
58
|
+
if (converted.length) {
|
|
59
|
+
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(', ')}`));
|
|
60
|
+
}
|
|
61
|
+
if (remaining.length) {
|
|
62
|
+
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}`));
|
|
63
|
+
}
|
|
64
|
+
if (moduleFederation.length) {
|
|
65
|
+
context.push(`These projects compose Module Federation helpers, so their configs are untouched. The @nx/module-federation migration handles them; do not convert them here:`, ...moduleFederation.map((usage) => `- ${usage.name} (${usage.root}): ${usage.configPaths.join(', ')}`));
|
|
66
|
+
}
|
|
67
|
+
return context;
|
|
68
|
+
}
|
|
@@ -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,39 @@
|
|
|
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
|
+
// Left for the Module Federation migration, like migrate-compose-plugins.
|
|
27
|
+
if (usage.usesModuleFederation)
|
|
28
|
+
continue;
|
|
29
|
+
try {
|
|
30
|
+
await (0, convert_config_to_webpack_plugin_1.convertConfigToWebpackPluginGenerator)(tree, {
|
|
31
|
+
project: usage.name,
|
|
32
|
+
skipFormat: true,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
devkit_1.logger.warn(`Could not convert the webpack config helpers in ${usage.name}: ${e.message}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|