nx/packages/angular/src/generators/convert-to-rspack/lib/get-custom-webpack-config.ts
Colum Ferry 43a20e2ecc
feat(angular): add support for rspack module federation (#31231)
## Current Behavior
We currently have no method for generating Angular Rspack Module
Federation applications

## Expected Behavior
Update the `host` and `remote` generators to support a `--bundler` flag
to allow users to select Rspack as their bundler method
2025-05-21 09:45:58 +01:00

138 lines
4.8 KiB
TypeScript

import { logger, Tree } from '@nx/devkit';
import { loadConfigFile } from '@nx/devkit/src/utils/config-utils';
import { join, relative } from 'path';
import { tsquery } from '@phenomnomnominal/tsquery';
const FILE_EXTENSION_REGEX = /\.[^.]+$/;
export async function getCustomWebpackConfig(
tree: Tree,
projectRoot: string,
pathToCustomWebpackConfig: string
) {
const webpackConfigContents = tree.read(pathToCustomWebpackConfig, 'utf-8');
if (
webpackConfigContents.includes('@nx/module-federation/angular') &&
webpackConfigContents.includes('withModuleFederation')
) {
tree.write(
pathToCustomWebpackConfig,
convertWebpackConfigToUseNxModuleFederationPlugin(webpackConfigContents)
);
return {
isWebpackConfigFunction: false,
normalizedPathToCustomWebpackConfig: `./${relative(
projectRoot,
pathToCustomWebpackConfig
).replace(FILE_EXTENSION_REGEX, '')}`,
};
}
const configFile = await loadConfigFile(
join(tree.root, pathToCustomWebpackConfig)
);
const webpackConfig =
'default' in configFile ? configFile.default : configFile;
return {
isWebpackConfigFunction: typeof webpackConfig === 'function',
normalizedPathToCustomWebpackConfig: `./${relative(
projectRoot,
pathToCustomWebpackConfig
).replace(FILE_EXTENSION_REGEX, '')}`,
};
}
export function convertWebpackConfigToUseNxModuleFederationPlugin(
webpackConfigContents: string
): string {
let newWebpackConfigContents = webpackConfigContents;
let ast = tsquery.ast(webpackConfigContents);
const withModuleFederationImportNodes = tsquery(
ast,
'ImportDeclaration:has(StringLiteral[value=@nx/module-federation/angular])'
);
if (withModuleFederationImportNodes.length > 0) {
const withModuleFederationImportNode = withModuleFederationImportNodes[0];
newWebpackConfigContents = `${webpackConfigContents.slice(
0,
withModuleFederationImportNode.getStart()
)}import { NxModuleFederationPlugin, NxModuleFederationDevServerPlugin } from '@nx/module-federation/angular';${webpackConfigContents.slice(
withModuleFederationImportNode.getEnd()
)}`;
ast = tsquery.ast(newWebpackConfigContents);
const exportedWithModuleFederationNodes = tsquery(
ast,
'ExportAssignment:has(CallExpression > Identifier[name=withModuleFederation])'
);
if (exportedWithModuleFederationNodes.length > 0) {
const exportedWithModuleFederationNode =
exportedWithModuleFederationNodes[0];
newWebpackConfigContents = `${newWebpackConfigContents.slice(
0,
exportedWithModuleFederationNode.getStart()
)}${newWebpackConfigContents.slice(
exportedWithModuleFederationNode.getEnd()
)}
export default {
plugins: [
new NxModuleFederationPlugin({ config }, {
dts: false,
}),
new NxModuleFederationDevServerPlugin({ config }),
]
}
`;
} else {
logger.warn(
"Could not find 'export default withModuleFederation' in the webpack config file. Skipping conversion."
);
}
}
const withModuleFederationRequireNodes = tsquery(
ast,
'VariableStatement:has(CallExpression > Identifier[name=withModuleFederation], StringLiteral[value=@nx/module-federation/angular])'
);
if (withModuleFederationRequireNodes.length > 0) {
const withModuleFederationRequireNode = withModuleFederationRequireNodes[0];
newWebpackConfigContents = `${webpackConfigContents.slice(
0,
withModuleFederationRequireNode.getStart()
)}const { NxModuleFederationPlugin, NxModuleFederationDevServerPlugin } = require('@nx/module-federation/rspack');${webpackConfigContents.slice(
withModuleFederationRequireNode.getEnd()
)}`;
ast = tsquery.ast(newWebpackConfigContents);
const exportedWithModuleFederationNodes = tsquery(
ast,
'ExpressionStatement:has(BinaryExpression > PropertyAccessExpression:has(Identifier[name=module], Identifier[name=exports]), CallExpression:has(Identifier[name=withModuleFederation]))'
);
if (exportedWithModuleFederationNodes.length > 0) {
const exportedWithModuleFederationNode =
exportedWithModuleFederationNodes[0];
newWebpackConfigContents = `${newWebpackConfigContents.slice(
0,
exportedWithModuleFederationNode.getStart()
)}${newWebpackConfigContents.slice(
exportedWithModuleFederationNode.getEnd()
)}
module.exports = {
plugins: [
new NxModuleFederationPlugin({ config }, {
dts: false,
}),
new NxModuleFederationDevServerPlugin({ config }),
]
}
`;
} else {
logger.warn(
"Could not find 'module.exports = withModuleFederation' in the webpack config file. Skipping conversion."
);
}
}
return newWebpackConfigContents;
}