refactor(@angular-devkit/build-angular): use built-in CSS vendor prefixing in esbuild

As of esbuild v0.18.9, vendor prefixing of CSS is now supported. The esbuild target option
is generated from a project's browserslist and the vendor prefixing will reflect the configured
browsers for a project. This improvement allows for the removal of the postcss autoprefixer
plugin from the build pipeline. This can provide a performance benefit for projects especially
when project stylesheets contain nothing that would require prefixing since postcss processing can
potentially be skipped completely.
This commit is contained in:
Charles Lyding 2023-06-27 10:50:46 -04:00 committed by Alan Agius
parent 9c02306e6a
commit 145a8e7c66

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import createAutoPrefixerPlugin from 'autoprefixer';
import type { OnLoadResult, Plugin, PluginBuild } from 'esbuild'; import type { OnLoadResult, Plugin, PluginBuild } from 'esbuild';
import glob from 'fast-glob'; import glob from 'fast-glob';
import assert from 'node:assert'; import assert from 'node:assert';
@ -62,57 +61,35 @@ export interface StylesheetLanguage {
} }
export class StylesheetPluginFactory { export class StylesheetPluginFactory {
private autoprefixer: import('postcss').Plugin | undefined;
constructor( constructor(
private readonly options: StylesheetPluginOptions, private readonly options: StylesheetPluginOptions,
private readonly cache?: LoadResultCache, private readonly cache?: LoadResultCache,
) { ) {}
const autoprefixer = createAutoPrefixerPlugin({
overrideBrowserslist: options.browsers,
ignoreUnknownVersions: true,
});
// Autoprefixer currently does not contain a method to check if autoprefixer is required
// based on the provided list of browsers. However, it does contain a method that returns
// informational text that can be used as a replacement. The text "Awesome!" will be present
// when autoprefixer determines no actions are needed.
// ref: https://github.com/postcss/autoprefixer/blob/e2f5c26ff1f3eaca95a21873723ce1cdf6e59f0e/lib/info.js#L118
const autoprefixerInfo = autoprefixer.info();
const skipAutoprefixer = autoprefixerInfo.includes('Awesome!');
if (!skipAutoprefixer) {
this.autoprefixer = autoprefixer;
}
}
create(language: Readonly<StylesheetLanguage>): Plugin { create(language: Readonly<StylesheetLanguage>): Plugin {
// Return a noop plugin if no load actions are required // Return a noop plugin if no load actions are required
if (!language.process && !this.autoprefixer && !this.options.tailwindConfiguration) { if (!language.process && !this.options.tailwindConfiguration) {
return { return {
name: 'angular-' + language.name, name: 'angular-' + language.name,
setup() {}, setup() {},
}; };
} }
const { autoprefixer, cache, options } = this; const { cache, options } = this;
return { return {
name: 'angular-' + language.name, name: 'angular-' + language.name,
async setup(build) { async setup(build) {
// Setup postcss if needed by either autoprefixer or tailwind // Setup postcss if needed by tailwind
// TODO: Move this into the plugin factory to avoid repeat setup per created plugin // TODO: Move this into the plugin factory to avoid repeat setup per created plugin
let postcssProcessor: import('postcss').Processor | undefined; let postcssProcessor: import('postcss').Processor | undefined;
if (autoprefixer || options.tailwindConfiguration) { if (options.tailwindConfiguration) {
postcss ??= (await import('postcss')).default; postcss ??= (await import('postcss')).default;
postcssProcessor = postcss(); postcssProcessor = postcss();
if (options.tailwindConfiguration) { if (options.tailwindConfiguration) {
const tailwind = await import(options.tailwindConfiguration.package); const tailwind = await import(options.tailwindConfiguration.package);
postcssProcessor.use(tailwind.default({ config: options.tailwindConfiguration.file })); postcssProcessor.use(tailwind.default({ config: options.tailwindConfiguration.file }));
} }
if (autoprefixer) {
postcssProcessor.use(autoprefixer);
}
} }
// Add a load callback to support inline Component styles // Add a load callback to support inline Component styles