-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Description
Current Behavior
When using @nx/angular-rspack with Tailwind CSS, @apply directives in component styles (.css/.scss files referenced via styleUrl/styleUrls) are not processed. The raw @apply syntax appears unchanged in the browser.
Example:
/* app.component.css */
:host {
@apply bg-red-500 hover:brightness-110;
}Browser output:
/* The @apply directive is NOT processed */
:host {
@apply bg-red-500 hover:brightness-110;
}Global styles (styles.scss) work correctly because they go through Rspack's PostCSS loader chain which includes Tailwind.
Expected Behavior
@apply directives in component stylesheets should be processed by Tailwind CSS, matching the behavior of:
- Angular CLI's esbuild-based builder (
@angular/build) - The webpack-based
@angular-devkit/build-angular
Root Cause
In @nx/angular-rspack-compiler, the setupCompilation function creates a ComponentStylesheetBundler but doesn't pass tailwindConfiguration or postcssConfiguration:
Current code (packages/angular-rspack-compiler/src/compilation/setup-compilation.ts or the compiled .js):
const componentStylesheetBundler = new ComponentStylesheetBundler({
workspaceRoot: options.root,
optimization: config.mode === 'production',
// ... other options
includePaths: options.includePaths,
sass: options.sass,
// MISSING: tailwindConfiguration
// MISSING: postcssConfiguration
}, options.inlineStyleLanguage, false);However, @angular/build's ComponentStylesheetBundler does support these options and uses them internally:
// From @angular/build/src/tools/esbuild/stylesheets/bundle-options.ts
const pluginFactory = new StylesheetPluginFactory({
sourcemap: !!options.sourcemap,
includePaths,
inlineComponentData,
tailwindConfiguration: options.tailwindConfiguration, // ← Used here
postcssConfiguration: options.postcssConfiguration, // ← Used here
sass: options.sass,
}, cache);Steps to Reproduce
-
Create an Angular app with
@nx/angular-rspack:npx create-nx-workspace@latest myapp --preset=angular-standalone --bundler=rspack
-
Add Tailwind CSS:
npm install -D tailwindcss postcss autoprefixer npx tailwindcss init
-
Configure Tailwind in
tailwind.config.js:module.exports = { content: ['./src/**/*.{html,ts}'], theme: { extend: {} }, plugins: [], };
-
Add Tailwind to
src/styles.css:@tailwind base; @tailwind components; @tailwind utilities;
-
Create a component with
@applyin its stylesheet:// app.component.ts @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], }) export class AppComponent {}
/* app.component.css */ :host { @apply bg-red-500 text-white p-4; }
-
Run
nx serveornx build -
Inspect the component in browser DevTools →
@apply bg-red-500 text-white p-4appears unchanged
Environment
- Nx: 22.3.3
- @nx/angular-rspack: 22.3.3
- @nx/angular-rspack-compiler: 22.3.3
- Angular: 21.0.8
- Tailwind CSS: 3.4.18
- Node: 23.x
- Package Manager: pnpm 9.15.3
Suggested Fix
Load Tailwind/PostCSS configuration in setupCompilation and pass it to ComponentStylesheetBundler, similar to how @angular/build does it:
// In packages/angular-rspack-compiler/src/compilation/setup-compilation.ts
import {
ComponentStylesheetBundler,
generateSearchDirectories,
loadPostcssConfiguration,
findTailwindConfiguration,
} from '@angular/build/private';
import { createRequire } from 'node:module';
async function setupCompilation(config, options) {
// ... existing code ...
// Load PostCSS and Tailwind configuration for component styles
const searchDirectories = await generateSearchDirectories([options.root]);
const postcssConfiguration = await loadPostcssConfiguration(searchDirectories);
// Skip tailwind configuration if postcss is customized
let tailwindConfiguration;
if (!postcssConfiguration) {
const tailwindConfigPath = findTailwindConfiguration(searchDirectories);
if (tailwindConfigPath) {
const resolver = createRequire(tailwindConfigPath);
try {
tailwindConfiguration = {
file: tailwindConfigPath,
package: resolver.resolve('tailwindcss'),
};
} catch (e) {
// Tailwind config found but package not installed - warning already shown by Angular build
}
}
}
const componentStylesheetBundler = new ComponentStylesheetBundler({
workspaceRoot: options.root,
// ... existing options ...
tailwindConfiguration, // ← Add this
postcssConfiguration, // ← Add this
}, options.inlineStyleLanguage, false);
// ... rest of the function
}Workaround
We've created a pnpm patch that implements the suggested fix:
diff --git a/dist/compilation/setup-compilation.js b/dist/compilation/setup-compilation.js
index 556fbb5456dc55dd0baa8553119c841aa3e096d8..34487b39f2cb9fecade5233003304f77c1edd6d3 100644
--- a/dist/compilation/setup-compilation.js
+++ b/dist/compilation/setup-compilation.js
@@ -7,6 +7,7 @@ const utils_1 = require("../utils");
const private_1 = require("@angular/build/private");
const targets_from_browsers_1 = require("../utils/targets-from-browsers");
const private_2 = require("@angular/build/private");
+const node_module_1 = require("node:module");
exports.DEFAULT_NG_COMPILER_OPTIONS = {
suppressOutputPathCheck: true,
outDir: undefined,
@@ -35,6 +36,30 @@ async function setupCompilation(config, options) {
: {}),
});
const compilerOptions = tsCompilerOptions;
+
+ // Load PostCSS and Tailwind configuration for component styles
+ // This enables @apply directives and other PostCSS features in component stylesheets
+ const searchDirectories = await (0, private_1.generateSearchDirectories)([options.root]);
+ const postcssConfiguration = await (0, private_1.loadPostcssConfiguration)(searchDirectories);
+
+ // Skip tailwind configuration if postcss is customized
+ let tailwindConfiguration;
+ if (!postcssConfiguration) {
+ const tailwindConfigurationPath = (0, private_1.findTailwindConfiguration)(searchDirectories);
+ if (tailwindConfigurationPath) {
+ const resolver = (0, node_module_1.createRequire)(tailwindConfigurationPath);
+ try {
+ tailwindConfiguration = {
+ file: tailwindConfigurationPath,
+ package: resolver.resolve('tailwindcss'),
+ };
+ }
+ catch (e) {
+ // Tailwind config found but package not installed - warning already shown by Angular build
+ }
+ }
+ }
+
const componentStylesheetBundler = new private_1.ComponentStylesheetBundler({
workspaceRoot: options.root,
optimization: config.mode === 'production',
@@ -50,6 +75,8 @@ async function setupCompilation(config, options) {
})),
includePaths: options.includePaths,
sass: options.sass,
+ tailwindConfiguration,
+ postcssConfiguration,
}, options.inlineStyleLanguage, false);
return {
rootNames,
Additional Context
- This issue affects only component styles (
styleUrl,styleUrls) - Global styles in
styles.scsswork correctly (processed by Rspack's PostCSS loader) - The
ComponentStylesheetBundlerfrom@angular/buildalready supports these options - This is not an Rspack limitation - it's a missing feature in the Nx wrapper
Nx Report
Node : 22.16.0
OS : linux-x64
Native Target : x86_64-linux
pnpm : 9.15.3
nx : 22.3.3
@nx/js : 22.3.3
@nx/eslint : 22.3.3
@nx/workspace : 22.3.3
@nx/angular : 22.3.3
@nx/jest : 22.3.3
@nx/cypress : 22.3.3
@nx/devkit : 22.3.3
@nx/eslint-plugin : 22.3.3
@nx/module-federation : 22.3.3
@nx/nest : 22.3.3
@nx/node : 22.3.3
@nx/plugin : 22.3.3
@nx/rspack : 22.3.3
@nx/storybook : 22.3.3
@nx/web : 22.3.3
@nx/webpack : 22.3.3
typescript : 5.9.3
---------------------------------------
Registered Plugins:
@nx/eslint/plugin
@nx/rspack/plugin
---------------------------------------
Community plugins:
@jscutlery/semver : 5.7.1
@ngrx/component-store : 21.0.1
@ngrx/effects : 21.0.1
@ngrx/operators : 21.0.1
@ngrx/router-store : 21.0.1
@ngrx/store : 21.0.1
@ngrx/store-devtools : 21.0.1
@storybook/angular : 10.1.11
angular-eslint : 21.1.0
apollo-angular : 12.1.0
---------------------------------------
Local workspace plugins:
@datera/nx
---------------------------------------
Cache Usage: 0.00 B / 11.56 GB