Skip to content

Commit fa81bb8

Browse files
committed
refactor(@angular/build): move postcss stylesheet processor to an optional peer dependency
The Postcss stylesheet processor is now an optional peer dependency of the newly introduced `@angular/build` build system package. This change removes the need for the package to have a direct dependency on the `postcss` package and allows some version customization of the optional `postcss` package if needed. Postcss plugins (including Tailwind) are still fully supported but usage will now require installing the `postcss` package within the Angular workspace. An error with instructions to install the package will be generated during a build if the package is not present. This change only affects direct usage of the new `@angular/build` package. The `@angular-devkit/build-angular` package which is currently used for all existing projects will continue to contain and directly depend on the `postcss` package. No changes are required for existing applications. (cherry picked from commit 6b5c51a)
1 parent 44b4017 commit fa81bb8

File tree

2 files changed

+38
-8
lines changed

2 files changed

+38
-8
lines changed

packages/angular/build/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"@angular/platform-server": "^18.0.0 || ^18.0.0-next.0",
5151
"@angular/service-worker": "^18.0.0 || ^18.0.0-next.0",
5252
"less": "^4.2.0",
53+
"postcss": "^8.4.0",
5354
"tailwindcss": "^2.0.0 || ^3.0.0",
5455
"typescript": ">=5.4 <5.5"
5556
},
@@ -66,6 +67,9 @@
6667
"less": {
6768
"optional": true
6869
},
70+
"postcss": {
71+
"optional": true
72+
},
6973
"tailwindcss": {
7074
"optional": true
7175
}

packages/angular/build/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.ts

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ import { extname } from 'node:path';
1414
import type { PostcssConfiguration } from '../../../utils/postcss-configuration';
1515
import { LoadResultCache, createCachedLoad } from '../load-result-cache';
1616

17+
/**
18+
* Convenience type for a postcss processor.
19+
*/
20+
type PostcssProcessor = import('postcss').Processor;
21+
1722
/**
1823
* The lazy-loaded instance of the postcss stylesheet postprocessor.
1924
* It is only imported and initialized if postcss is needed.
@@ -88,10 +93,10 @@ export interface StylesheetLanguage {
8893
/**
8994
* Cached postcss instances that can be re-used between various StylesheetPluginFactory instances.
9095
*/
91-
const postcssProcessor = new Map<string, WeakRef<import('postcss').Processor>>();
96+
const postcssProcessors = new Map<string, WeakRef<PostcssProcessor>>();
9297

9398
export class StylesheetPluginFactory {
94-
private postcssProcessor?: import('postcss').Processor;
99+
private postcssProcessor?: PostcssProcessor;
95100

96101
constructor(
97102
private readonly options: StylesheetPluginOptions,
@@ -121,7 +126,7 @@ export class StylesheetPluginFactory {
121126
if (options.postcssConfiguration) {
122127
const postCssInstanceKey = JSON.stringify(options.postcssConfiguration);
123128

124-
this.postcssProcessor = postcssProcessor.get(postCssInstanceKey)?.deref();
129+
this.postcssProcessor = postcssProcessors.get(postCssInstanceKey)?.deref();
125130

126131
if (!this.postcssProcessor) {
127132
postcss ??= (await import('postcss')).default;
@@ -135,18 +140,18 @@ export class StylesheetPluginFactory {
135140
this.postcssProcessor.use(plugin(pluginOptions));
136141
}
137142

138-
postcssProcessor.set(postCssInstanceKey, new WeakRef(this.postcssProcessor));
143+
postcssProcessors.set(postCssInstanceKey, new WeakRef(this.postcssProcessor));
139144
}
140145
} else if (options.tailwindConfiguration) {
141146
const { package: tailwindPackage, file: config } = options.tailwindConfiguration;
142147
const postCssInstanceKey = tailwindPackage + ':' + config;
143-
this.postcssProcessor = postcssProcessor.get(postCssInstanceKey)?.deref();
148+
this.postcssProcessor = postcssProcessors.get(postCssInstanceKey)?.deref();
144149

145150
if (!this.postcssProcessor) {
146151
postcss ??= (await import('postcss')).default;
147152
const tailwind = await import(tailwindPackage);
148153
this.postcssProcessor = postcss().use(tailwind.default({ config }));
149-
postcssProcessor.set(postCssInstanceKey, new WeakRef(this.postcssProcessor));
154+
postcssProcessors.set(postCssInstanceKey, new WeakRef(this.postcssProcessor));
150155
}
151156
}
152157

@@ -157,7 +162,28 @@ export class StylesheetPluginFactory {
157162
name: 'angular-' + language.name,
158163
async setup(build) {
159164
// Setup postcss if needed
160-
const postcssProcessor = await setupPostcss();
165+
let postcssProcessor: PostcssProcessor | undefined;
166+
build.onStart(async () => {
167+
try {
168+
postcssProcessor = await setupPostcss();
169+
} catch {
170+
return {
171+
errors: [
172+
{
173+
text: 'Unable to load the "postcss" stylesheet processor.',
174+
location: null,
175+
notes: [
176+
{
177+
text:
178+
'Ensure that the "postcss" Node.js package is installed within the project. ' +
179+
"If not present, installation via the project's package manager should resolve the error.",
180+
},
181+
],
182+
},
183+
],
184+
};
185+
}
186+
});
161187

162188
// Add a load callback to support inline Component styles
163189
build.onLoad(
@@ -212,7 +238,7 @@ async function processStylesheet(
212238
format: string,
213239
options: StylesheetPluginOptions,
214240
build: PluginBuild,
215-
postcssProcessor: import('postcss').Processor | undefined,
241+
postcssProcessor: PostcssProcessor | undefined,
216242
) {
217243
let result: OnLoadResult;
218244

0 commit comments

Comments
 (0)