feat(angular): add --bundler=rspack option to app generator (#30623)

## Current Behavior
The `@nx/angular` app generator currently does not support generating an
Angular Rspack application. This makes it slightly more difficult for
users to get up and running with Angular Rspack


## Expected Behavior
Add `rspack` as a supported `--bundler` option allowing for easy
generationg of new Angular Rspack apps.
This commit is contained in:
Colum Ferry 2025-04-08 09:41:59 +01:00 committed by GitHub
parent 32b48a3a04
commit 27d40a9270
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 116 additions and 4 deletions

View File

@ -168,7 +168,7 @@
"bundler": { "bundler": {
"description": "Bundler to use to build the application.", "description": "Bundler to use to build the application.",
"type": "string", "type": "string",
"enum": ["esbuild", "webpack"], "enum": ["esbuild", "rspack", "webpack"],
"default": "esbuild", "default": "esbuild",
"x-prompt": "Which bundler do you want to use to build the application?", "x-prompt": "Which bundler do you want to use to build the application?",
"x-priority": "important" "x-priority": "important"

View File

@ -149,6 +149,19 @@ describe('Angular Projects', () => {
await killProcessAndPorts(esbProcess.pid, appPort); await killProcessAndPorts(esbProcess.pid, appPort);
}, 1000000); }, 1000000);
it('should successfully work with rspack for build', async () => {
const app = uniq('app');
runCLI(
`generate @nx/angular:app my-dir/${app} --bundler=rspack --no-interactive`
);
runCLI(`build ${app}`);
if (runE2ETests()) {
expect(() => runCLI(`e2e ${app}-e2e`)).not.toThrow();
expect(await killPort(4200)).toBeTruthy();
}
}, 1000000);
it('should successfully work with playwright for e2e tests', async () => { it('should successfully work with playwright for e2e tests', async () => {
const app = uniq('app'); const app = uniq('app');

View File

@ -1,5 +1,74 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`app --minimal should generate a correct setup when --bundler=rspack including a correct config file and no build target 1`] = `
"
import { createConfig }from '@nx/angular-rspack';
export default createConfig({
options: {
root: __dirname,
"outputPath": {
"base": "../dist/app1"
},
"index": "./src/index.html",
"browser": "./src/main.ts",
"polyfills": [
"./zone.js"
],
"tsConfig": "./tsconfig.app.json",
"assets": [
{
"glob": "**/*",
"input": "./public"
}
],
"styles": [
"./src/styles.css"
],
"scripts": [],
"devServer": {}
}
}, {
production: {
options: {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kb",
"maximumError": "8kb"
}
],
"outputHashing": "all",
"devServer": {}
}
},
development: {
options: {
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true,
"devServer": {}
}
}});
"
`;
exports[`app --minimal should skip "nx-welcome.component.ts" file and references for non-standalone apps with routing 1`] = ` exports[`app --minimal should skip "nx-welcome.component.ts" file and references for non-standalone apps with routing 1`] = `
"import { NgModule } from '@angular/core'; "import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';

View File

@ -1242,6 +1242,16 @@ describe('app', () => {
] ]
`); `);
}); });
it('should generate a correct setup when --bundler=rspack including a correct config file and no build target', async () => {
await generateApp(appTree, 'app1', {
bundler: 'rspack',
});
const project = readProjectConfiguration(appTree, 'app1');
expect(project.targets.build).not.toBeDefined();
expect(appTree.exists('app1/rspack.config.ts')).toBeTruthy();
expect(appTree.read('app1/rspack.config.ts', 'utf-8')).toMatchSnapshot();
});
it('should generate target options "browser" and "buildTarget"', async () => { it('should generate target options "browser" and "buildTarget"', async () => {
await generateApp(appTree, 'my-app', { standalone: true }); await generateApp(appTree, 'my-app', { standalone: true });

View File

@ -1,4 +1,5 @@
import { import {
addDependenciesToPackageJson,
formatFiles, formatFiles,
GeneratorCallback, GeneratorCallback,
installPackagesTask, installPackagesTask,
@ -11,6 +12,7 @@ import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-com
import { initGenerator as jsInitGenerator } from '@nx/js'; import { initGenerator as jsInitGenerator } from '@nx/js';
import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup'; import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
import { angularInitGenerator } from '../init/init'; import { angularInitGenerator } from '../init/init';
import { convertToRspack } from '../convert-to-rspack/convert-to-rspack';
import { setupSsr } from '../setup-ssr/setup-ssr'; import { setupSsr } from '../setup-ssr/setup-ssr';
import { setupTailwindGenerator } from '../setup-tailwind/setup-tailwind'; import { setupTailwindGenerator } from '../setup-tailwind/setup-tailwind';
import { ensureAngularDependencies } from '../utils/ensure-angular-dependencies'; import { ensureAngularDependencies } from '../utils/ensure-angular-dependencies';
@ -29,12 +31,17 @@ import {
updateEditorTsConfig, updateEditorTsConfig,
} from './lib'; } from './lib';
import type { Schema } from './schema'; import type { Schema } from './schema';
import { tsNodeVersion } from '../../utils/versions';
export async function applicationGenerator( export async function applicationGenerator(
tree: Tree, tree: Tree,
schema: Partial<Schema> schema: Partial<Schema>
): Promise<GeneratorCallback> { ): Promise<GeneratorCallback> {
assertNotUsingTsSolutionSetup(tree, 'angular', 'application'); assertNotUsingTsSolutionSetup(tree, 'angular', 'application');
const isRspack = schema.bundler === 'rspack';
if (isRspack) {
schema.bundler = 'webpack';
}
const options = await normalizeOptions(tree, schema); const options = await normalizeOptions(tree, schema);
const rootOffset = offsetFromRoot(options.appProjectRoot); const rootOffset = offsetFromRoot(options.appProjectRoot);
@ -102,6 +109,14 @@ export async function applicationGenerator(
}); });
} }
if (isRspack) {
await convertToRspack(tree, {
project: options.name,
skipInstall: options.skipPackageJson,
skipFormat: true,
});
}
if (!options.skipFormat) { if (!options.skipFormat) {
await formatFiles(tree); await formatFiles(tree);
} }

View File

@ -27,7 +27,7 @@ export interface Schema {
standalone?: boolean; standalone?: boolean;
rootProject?: boolean; rootProject?: boolean;
minimal?: boolean; minimal?: boolean;
bundler?: 'webpack' | 'esbuild'; bundler?: 'webpack' | 'esbuild' | 'rspack';
ssr?: boolean; ssr?: boolean;
serverRouting?: boolean; serverRouting?: boolean;
nxCloudToken?: string; nxCloudToken?: string;

View File

@ -171,7 +171,7 @@
"bundler": { "bundler": {
"description": "Bundler to use to build the application.", "description": "Bundler to use to build the application.",
"type": "string", "type": "string",
"enum": ["esbuild", "webpack"], "enum": ["esbuild", "rspack", "webpack"],
"default": "esbuild", "default": "esbuild",
"x-prompt": "Which bundler do you want to use to build the application?", "x-prompt": "Which bundler do you want to use to build the application?",
"x-priority": "important" "x-priority": "important"

View File

@ -13,7 +13,11 @@ import {
writeJson, writeJson,
} from '@nx/devkit'; } from '@nx/devkit';
import type { ConvertToRspackSchema } from './schema'; import type { ConvertToRspackSchema } from './schema';
import { angularRspackVersion, nxVersion } from '../../utils/versions'; import {
angularRspackVersion,
nxVersion,
tsNodeVersion,
} from '../../utils/versions';
import { createConfig } from './lib/create-config'; import { createConfig } from './lib/create-config';
import { getCustomWebpackConfig } from './lib/get-custom-webpack-config'; import { getCustomWebpackConfig } from './lib/get-custom-webpack-config';
import { updateTsconfig } from './lib/update-tsconfig'; import { updateTsconfig } from './lib/update-tsconfig';
@ -451,6 +455,7 @@ export async function convertToRspack(
{}, {},
{ {
'@nx/angular-rspack': angularRspackVersion, '@nx/angular-rspack': angularRspackVersion,
'ts-node': tsNodeVersion,
} }
); );
tasks.push(installTask); tasks.push(installTask);