fix(rspack): ensure nest applications generated correctly #31204 (#31424)

- fix(rspack): choosing nest as framework should not result in error
#31204
- fix(rspack): add deprecation message for application generator
- fix(rspack): ensure application generated projects are added to
excludes

## Current Behavior
Running the `@nx/rspack:application` generator with `--framework=nest`
results in an error due to mix of inferred and executor usage throughout
the generation process.

## Expected Behavior
Running the generator should pass without failure and create a working
project.
Deprecate the generator in favour of using project specific packages
(@nx/react etc)

## Related Issue(s)

Fixes #31204

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Coly010 <Coly010@users.noreply.github.com>
This commit is contained in:
Colum Ferry 2025-06-03 14:41:17 +01:00 committed by GitHub
parent d3faf53c56
commit 34cf5a243f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 122 additions and 23 deletions

View File

@ -7,6 +7,7 @@
"title": "Application generator for React + rspack", "title": "Application generator for React + rspack",
"type": "object", "type": "object",
"description": "React + Rspack application generator.", "description": "React + Rspack application generator.",
"x-deprecated": "This generator will be removed in Nx 22. Please use the equivalent generator for your application type instead.",
"examples": [ "examples": [
{ {
"command": "nx g app myorg/myapp", "command": "nx g app myorg/myapp",
@ -90,6 +91,7 @@
"aliases": ["app"], "aliases": ["app"],
"x-type": "application", "x-type": "application",
"description": "React application generator.", "description": "React application generator.",
"x-deprecated": "This generator will be removed in Nx 22. Please use the equivalent generator for your application type instead.",
"implementation": "/packages/rspack/src/generators/application/application.ts", "implementation": "/packages/rspack/src/generators/application/application.ts",
"hidden": false, "hidden": false,
"path": "/packages/rspack/src/generators/application/schema.json", "path": "/packages/rspack/src/generators/application/schema.json",

View File

@ -197,7 +197,9 @@ describe('app', () => {
"targets": { "targets": {
"build": { "build": {
"configurations": { "configurations": {
"development": {}, "development": {
"outputHashing": "none",
},
"production": {}, "production": {},
}, },
"defaultConfiguration": "production", "defaultConfiguration": "production",
@ -366,7 +368,9 @@ describe('app', () => {
"targets": { "targets": {
"build": { "build": {
"configurations": { "configurations": {
"development": {}, "development": {
"outputHashing": "none",
},
"production": {}, "production": {},
}, },
"defaultConfiguration": "production", "defaultConfiguration": "production",

View File

@ -31,7 +31,9 @@ describe('node app generator (legacy)', () => {
expect(project.targets.build).toMatchInlineSnapshot(` expect(project.targets.build).toMatchInlineSnapshot(`
{ {
"configurations": { "configurations": {
"development": {}, "development": {
"outputHashing": "none",
},
"production": {}, "production": {},
}, },
"defaultConfiguration": "production", "defaultConfiguration": "production",

View File

@ -95,7 +95,9 @@ function getWebpackBuildConfig(
generatePackageJson: options.isUsingTsSolutionConfig ? undefined : true, generatePackageJson: options.isUsingTsSolutionConfig ? undefined : true,
}, },
configurations: { configurations: {
development: {}, development: {
outputHashing: 'none',
},
production: { production: {
...(options.docker && { generateLockfile: true }), ...(options.docker && { generateLockfile: true }),
}, },
@ -197,7 +199,7 @@ function addProject(tree: Tree, options: NormalizedSchema) {
addBuildTargetDefaults(tree, '@nx/esbuild:esbuild'); addBuildTargetDefaults(tree, '@nx/esbuild:esbuild');
project.targets.build = getEsBuildConfig(project, options); project.targets.build = getEsBuildConfig(project, options);
} else if (options.bundler === 'webpack') { } else if (options.bundler === 'webpack') {
if (!hasWebpackPlugin(tree)) { if (!hasWebpackPlugin(tree) && options.addPlugin === false) {
addBuildTargetDefaults(tree, `@nx/webpack:webpack`); addBuildTargetDefaults(tree, `@nx/webpack:webpack`);
project.targets.build = getWebpackBuildConfig(project, options); project.targets.build = getWebpackBuildConfig(project, options);
} else if (options.isNest) { } else if (options.isNest) {
@ -253,7 +255,8 @@ function addAppFiles(tree: Tree, options: NormalizedSchema) {
tree, tree,
options.appProjectRoot options.appProjectRoot
), ),
webpackPluginOptions: hasWebpackPlugin(tree) webpackPluginOptions:
hasWebpackPlugin(tree) && options.addPlugin !== false
? { ? {
outputPath: options.isUsingTsSolutionConfig outputPath: options.isUsingTsSolutionConfig
? 'dist' ? 'dist'

View File

@ -25,7 +25,8 @@
"schema": "./src/generators/application/schema.json", "schema": "./src/generators/application/schema.json",
"aliases": ["app"], "aliases": ["app"],
"x-type": "application", "x-type": "application",
"description": "React application generator." "description": "React application generator.",
"x-deprecated": "This generator will be removed in Nx 22. Please use the equivalent generator for your application type instead."
}, },
"convert-webpack": { "convert-webpack": {
"alias": "convert-to-rspack", "alias": "convert-to-rspack",

View File

@ -1,14 +1,82 @@
import { ensurePackage, formatFiles, runTasksInSerial, Tree } from '@nx/devkit'; import {
ensurePackage,
formatFiles,
logger,
readNxJson,
runTasksInSerial,
Tree,
updateNxJson,
} from '@nx/devkit';
import { version as nxVersion } from 'nx/package.json'; import { version as nxVersion } from 'nx/package.json';
import configurationGenerator from '../configuration/configuration'; import configurationGenerator from '../configuration/configuration';
import rspackInitGenerator from '../init/init'; import rspackInitGenerator from '../init/init';
import { normalizeOptions } from './lib/normalize-options'; import { normalizeOptions } from './lib/normalize-options';
import { ApplicationGeneratorSchema } from './schema'; import { ApplicationGeneratorSchema, NormalizedSchema } from './schema';
/**
* Updates the exclude field for any @nx/rspack/plugin registrations in nx.json
*/
function updateRspackPluginExclusion(tree: Tree, options: NormalizedSchema) {
const nxJson = readNxJson(tree);
if (!nxJson.plugins?.length) {
return;
}
let updated = false;
// Loop through all plugins to find @nx/rspack/plugin registrations
for (let i = 0; i < nxJson.plugins.length; i++) {
const plugin = nxJson.plugins[i];
const isRspackPlugin =
typeof plugin === 'string'
? plugin === '@nx/rspack/plugin'
: plugin.plugin === '@nx/rspack/plugin';
if (!isRspackPlugin) {
continue;
}
if (typeof plugin === 'string') {
// Convert string notation to object notation with exclude field
nxJson.plugins[i] = {
plugin: '@nx/rspack/plugin',
exclude: [`${options.appProjectRoot}/**`],
};
updated = true;
} else {
// Object notation
if (!plugin.exclude) {
// Add exclude field if it doesn't exist
plugin.exclude = [`${options.appProjectRoot}/**`];
updated = true;
} else if (Array.isArray(plugin.exclude)) {
// Add to existing exclude field if it's an array
plugin.exclude.push(`${options.appProjectRoot}/**`);
updated = true;
}
}
}
if (updated) {
updateNxJson(tree, nxJson);
}
}
// TODO(v22) - remove this generator
export default async function ( export default async function (
tree: Tree, tree: Tree,
_options: ApplicationGeneratorSchema _options: ApplicationGeneratorSchema
) { ) {
// Add deprecation warning with alternatives based on framework
const framework = _options.framework || 'react'; // Default is react
logger.warn(
`The @nx/rspack:application generator is deprecated and will be removed in Nx 22. ` +
`Please use @nx/${
framework === 'nest' ? 'nest' : framework === 'web' ? 'web' : 'react'
}:application instead.`
);
const tasks = []; const tasks = [];
const initTask = await rspackInitGenerator(tree, { const initTask = await rspackInitGenerator(tree, {
..._options, ..._options,
@ -17,6 +85,10 @@ export default async function (
const options = await normalizeOptions(tree, _options); const options = await normalizeOptions(tree, _options);
if (framework === 'nest') {
updateRspackPluginExclusion(tree, options);
}
options.style ??= 'css'; options.style ??= 'css';
if (options.framework === 'nest') { if (options.framework === 'nest') {
@ -37,6 +109,7 @@ export default async function (
newProject: false, newProject: false,
buildTarget: 'build', buildTarget: 'build',
framework: 'nest', framework: 'nest',
addPlugin: false,
}); });
tasks.push(createAppTask, convertAppTask); tasks.push(createAppTask, convertAppTask);
@ -92,6 +165,7 @@ export default async function (
buildTarget: 'build', buildTarget: 'build',
serveTarget: 'serve', serveTarget: 'serve',
framework: 'react', framework: 'react',
addPlugin: false,
}); });
tasks.push(createAppTask, convertAppTask); tasks.push(createAppTask, convertAppTask);
} }

View File

@ -4,6 +4,7 @@
"title": "Application generator for React + rspack", "title": "Application generator for React + rspack",
"type": "object", "type": "object",
"description": "React + Rspack application generator.", "description": "React + Rspack application generator.",
"x-deprecated": "This generator will be removed in Nx 22. Please use the equivalent generator for your application type instead.",
"examples": [ "examples": [
{ {
"command": "nx g app myorg/myapp", "command": "nx g app myorg/myapp",

View File

@ -190,6 +190,16 @@ export function addOrChangeBuildTarget(
assets, assets,
}; };
const existingProjectConfigurations = {};
const buildTarget = project.targets.build;
if (buildTarget && buildTarget.configurations) {
for (const [configurationName, configuration] of Object.entries(
buildTarget.configurations
)) {
existingProjectConfigurations[configurationName] = configuration;
}
}
project.targets ??= {}; project.targets ??= {};
project.targets[target] = { project.targets[target] = {
@ -199,9 +209,11 @@ export function addOrChangeBuildTarget(
options: buildOptions, options: buildOptions,
configurations: { configurations: {
development: { development: {
...(existingProjectConfigurations['development'] ?? {}),
mode: 'development', mode: 'development',
}, },
production: { production: {
...(existingProjectConfigurations['production'] ?? {}),
mode: 'production', mode: 'production',
optimization: options.target === 'web' ? true : undefined, optimization: options.target === 'web' ? true : undefined,
sourceMap: false, sourceMap: false,
@ -371,7 +383,7 @@ function generateNestConfig(
project: ProjectConfiguration, project: ProjectConfiguration,
buildOptions: RspackExecutorSchema buildOptions: RspackExecutorSchema
): string { ): string {
if (hasPlugin(tree)) { if (hasPlugin(tree) && options.addPlugin !== false) {
return ` return `
const { NxAppRspackPlugin } = require('@nx/rspack/app-plugin'); const { NxAppRspackPlugin } = require('@nx/rspack/app-plugin');
const rspack = require('@rspack/core'); const rspack = require('@rspack/core');