fix(angular): convert-to-rspack correctly migrating existing custom webpack configs (#30778)

## Current Behavior
The `createConfig` helper from `@nx/angular-rspack` became an async
function.
This meant that the handling of custom webpack configs in the migration
done by `convert-to-rspack` was incorrect.

## Expected Behavior
Ensure the migration is handled correctly.
Ensure that Module Federation migrations work correctly.

## Related Issues
Fixes https://github.com/nrwl/angular-rspack/issues/53
This commit is contained in:
Colum Ferry 2025-04-22 15:54:51 +01:00 committed by GitHub
parent 3f2a40ffec
commit 745abdaecf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 115 additions and 93 deletions

View File

@ -36,7 +36,7 @@ exports[`app --minimal should generate a correct setup when --bundler=rspack and
}
}, {
production: {
"production": {
options: {
"budgets": [
@ -57,7 +57,7 @@ exports[`app --minimal should generate a correct setup when --bundler=rspack and
}
},
development: {
"development": {
options: {
"optimization": false,
@ -68,9 +68,7 @@ exports[`app --minimal should generate a correct setup when --bundler=rspack and
"devServer": {}
}
}});
"
}});"
`;
exports[`app --minimal should generate a correct setup when --bundler=rspack and ssr 2`] = `
@ -181,7 +179,7 @@ exports[`app --minimal should generate a correct setup when --bundler=rspack inc
}
}, {
production: {
"production": {
options: {
"budgets": [
@ -202,7 +200,7 @@ exports[`app --minimal should generate a correct setup when --bundler=rspack inc
}
},
development: {
"development": {
options: {
"optimization": false,
@ -213,9 +211,7 @@ exports[`app --minimal should generate a correct setup when --bundler=rspack inc
"devServer": {}
}
}});
"
}});"
`;
exports[`app --minimal should skip "nx-welcome.component.ts" file and references for non-standalone apps with routing 1`] = `

View File

@ -429,7 +429,8 @@ describe('convert-to-rspack', () => {
import baseWebpackConfig from './webpack.config';
import webpackMerge from 'webpack-merge';
const baseConfig = createConfig({
export default async () => {
const baseConfig = await createConfig({
options: {
root: __dirname,
@ -452,13 +453,16 @@ describe('convert-to-rspack', () => {
scripts: [],
},
});
export default webpackMerge(baseConfig[0], baseWebpackConfig);
return webpackMerge(baseConfig[0], baseWebpackConfig);
};
"
`);
expect(tree.read('apps/app/webpack.config.js', 'utf-8'))
.toMatchInlineSnapshot(`
"const { NxModuleFederationPlugin } = require('@nx/module-federation/rspack');
"const {
NxModuleFederationPlugin,
NxModuleFederationDevServerPlugin,
} = require('@nx/module-federation/rspack');
const config = require('./module-federation.config');
module.exports = {
@ -469,6 +473,7 @@ describe('convert-to-rspack', () => {
dts: false,
}
),
new NxModuleFederationDevServerPlugin({ config }),
],
};
"
@ -549,7 +554,8 @@ describe('convert-to-rspack', () => {
import baseWebpackConfig from './webpack.config';
import webpackMerge from 'webpack-merge';
const baseConfig = createConfig({
export default async () => {
const baseConfig = await createConfig({
options: {
root: __dirname,
@ -572,8 +578,8 @@ describe('convert-to-rspack', () => {
scripts: [],
},
});
export default webpackMerge(baseConfig[0], baseWebpackConfig);
return webpackMerge(baseConfig[0], baseWebpackConfig);
};
"
`);
});

View File

@ -43,7 +43,14 @@ const RENAMED_OPTIONS = {
ngswConfigPath: 'serviceWorker',
};
const REMOVED_OPTIONS = ['buildOptimizer', 'buildTarget', 'browserTarget'];
const DEFAULT_PORT = 4200;
const REMOVED_OPTIONS = [
'buildOptimizer',
'buildTarget',
'browserTarget',
'publicHost',
];
function normalizeFromProjectRoot(
tree: Tree,
@ -345,6 +352,8 @@ export async function convertToRspack(
validateSupportedBuildExecutor(Object.values(project.targets));
let projectServePort = DEFAULT_PORT;
for (const [targetName, target] of Object.entries(project.targets)) {
if (
target.executor === '@angular-devkit/build-angular:browser' ||
@ -395,6 +404,10 @@ export async function convertToRspack(
createConfigOptions.devServer,
project.root
);
if (target.options.port !== DEFAULT_PORT) {
projectServePort = target.options.port;
}
}
if (target.configurations) {
for (const [configurationName, configuration] of Object.entries(
@ -431,6 +444,12 @@ export async function convertToRspack(
delete project.targets[targetName];
}
if (projectServePort !== DEFAULT_PORT) {
project.targets.serve ??= {};
project.targets.serve.options ??= {};
project.targets.serve.options.port = projectServePort;
}
updateProjectConfiguration(tree, projectName, project);
const { rspackInitGenerator } = ensurePackage<typeof import('@nx/rspack')>(

View File

@ -49,9 +49,7 @@ describe('createConfig', () => {
"skipTypeChecking": false
}
});
"
});"
`);
});
@ -83,16 +81,14 @@ describe('createConfig', () => {
}
}, {
production: {
"production": {
options: {
"index": "src/index.prod.html",
"browser": "src/main.prod.ts"
}
}});
"
}});"
`);
});
});

View File

@ -13,7 +13,7 @@ export function createConfig(
? Object.entries(configurationOptions)
.map(([configurationName, configurationOptions]) => {
return `
${configurationName}: {
"${configurationName}": {
options: {
${JSON.stringify(configurationOptions, undefined, 2).slice(1, -1)}
}
@ -21,6 +21,14 @@ export function createConfig(
})
.join(',\n')
: '';
const createConfigContents = `createConfig({
options: {
root: __dirname,
${JSON.stringify(createConfigOptions, undefined, 2).slice(1, -1)}
}
}${hasConfigurations ? `, {${expandedConfigurationOptions}}` : ''});`;
const configContents = `
import { createConfig }from '@nx/angular-rspack';
${
@ -34,29 +42,18 @@ export function createConfig(
: ''
}
${
existingWebpackConfigPath ? 'const baseConfig = ' : 'export default '
}createConfig({
options: {
root: __dirname,
${JSON.stringify(createConfigOptions, undefined, 2).slice(1, -1)}
}
}${hasConfigurations ? `, {${expandedConfigurationOptions}}` : ''});
${
existingWebpackConfigPath
? `
export default ${
? `export default async () => {
const baseConfig = await ${createConfigContents}
${
isExistingWebpackConfigFunction
? `async function (env, argv) {
const oldConfig = await baseWebpackConfig;
? `const oldConfig = await baseWebpackConfig;
const browserConfig = baseConfig[0];
return oldConfig(browserConfig);
}`
: 'webpackMerge(baseConfig[0], baseWebpackConfig);'
}
`
: ''
}
`;
return oldConfig(browserConfig);`
: 'return webpackMerge(baseConfig[0], baseWebpackConfig);'
}};`
: `export default ${createConfigContents}`
}`;
tree.write(joinPathFragments(root, 'rspack.config.ts'), configContents);
}

View File

@ -16,15 +16,16 @@ describe('convertconvertWebpackConfigToUseNxModuleFederationPlugin', () => {
// ASSERT
expect(newWebpackConfigContents).toMatchInlineSnapshot(`
"
import { NxModuleFederationPlugin } from '@nx/module-federation/rspack';
import { NxModuleFederationPlugin, NxModuleFederationDevServerPlugin } from '@nx/module-federation/rspack';
import config from './module-federation.config';
export default {
plugins: [
new NxModuleFederationPlugin(config, {
new NxModuleFederationPlugin({ config }, {
dts: false,
}),
new NxModuleFederationDevServerPlugin({ config }),
]
}
"
@ -46,7 +47,7 @@ describe('convertconvertWebpackConfigToUseNxModuleFederationPlugin', () => {
// ASSERT
expect(newWebpackConfigContents).toMatchInlineSnapshot(`
"
const { NxModuleFederationPlugin } = require('@nx/module-federation/rspack');
const { NxModuleFederationPlugin, NxModuleFederationDevServerPlugin } = require('@nx/module-federation/rspack');
const config = require('./module-federation.config');
@ -55,6 +56,7 @@ describe('convertconvertWebpackConfigToUseNxModuleFederationPlugin', () => {
new NxModuleFederationPlugin({ config }, {
dts: false,
}),
new NxModuleFederationDevServerPlugin({ config }),
]
}
"

View File

@ -56,7 +56,7 @@ export function convertWebpackConfigToUseNxModuleFederationPlugin(
newWebpackConfigContents = `${webpackConfigContents.slice(
0,
withModuleFederationImportNode.getStart()
)}import { NxModuleFederationPlugin } from '@nx/module-federation/rspack';${webpackConfigContents.slice(
)}import { NxModuleFederationPlugin, NxModuleFederationDevServerPlugin } from '@nx/module-federation/rspack';${webpackConfigContents.slice(
withModuleFederationImportNode.getEnd()
)}`;
@ -76,9 +76,10 @@ export function convertWebpackConfigToUseNxModuleFederationPlugin(
)}
export default {
plugins: [
new NxModuleFederationPlugin(config, {
new NxModuleFederationPlugin({ config }, {
dts: false,
}),
new NxModuleFederationDevServerPlugin({ config }),
]
}
`;
@ -98,7 +99,7 @@ export function convertWebpackConfigToUseNxModuleFederationPlugin(
newWebpackConfigContents = `${webpackConfigContents.slice(
0,
withModuleFederationRequireNode.getStart()
)}const { NxModuleFederationPlugin } = require('@nx/module-federation/rspack');${webpackConfigContents.slice(
)}const { NxModuleFederationPlugin, NxModuleFederationDevServerPlugin } = require('@nx/module-federation/rspack');${webpackConfigContents.slice(
withModuleFederationRequireNode.getEnd()
)}`;
@ -121,6 +122,7 @@ export function convertWebpackConfigToUseNxModuleFederationPlugin(
new NxModuleFederationPlugin({ config }, {
dts: false,
}),
new NxModuleFederationDevServerPlugin({ config }),
]
}
`;

View File

@ -23,6 +23,10 @@ export class NxModuleFederationPlugin implements RspackPluginInstance {
compiler.options.optimization ??= {};
compiler.options.optimization.runtimeChunk = false;
compiler.options.output.uniqueName = this._options.config.name;
if (compiler.options.output.scriptType === 'module') {
compiler.options.output.scriptType = undefined;
compiler.options.output.module = undefined;
}
if (this._options.isServer) {
compiler.options.target = 'async-node';
compiler.options.output.library ??= {