@angular-devkit/build-angular
Version:
Angular Webpack Build Facade
124 lines (123 loc) • 5.79 kB
JavaScript
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.CssOptimizerPlugin = void 0;
const private_1 = require("@angular/build/private");
const webpack_diagnostics_1 = require("../../../utils/webpack-diagnostics");
const esbuild_executor_1 = require("./esbuild-executor");
/**
* The name of the plugin provided to Webpack when tapping Webpack compiler hooks.
*/
const PLUGIN_NAME = 'angular-css-optimizer';
/**
* A Webpack plugin that provides CSS optimization capabilities.
*
* The plugin uses both `esbuild` to provide both fast and highly-optimized
* code output.
*/
class CssOptimizerPlugin {
targets;
esbuild = new esbuild_executor_1.EsbuildExecutor();
constructor(options) {
if (options?.supportedBrowsers) {
this.targets = (0, private_1.transformSupportedBrowsersToTargets)(options.supportedBrowsers);
}
}
apply(compiler) {
const { OriginalSource, SourceMapSource } = compiler.webpack.sources;
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
const logger = compilation.getLogger('build-angular.CssOptimizerPlugin');
compilation.hooks.processAssets.tapPromise({
name: PLUGIN_NAME,
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE,
}, async (compilationAssets) => {
const cache = compilation.options.cache && compilation.getCache(PLUGIN_NAME);
logger.time('optimize css assets');
for (const assetName of Object.keys(compilationAssets)) {
if (!/\.(?:css|scss|sass|less)$/.test(assetName)) {
continue;
}
const asset = compilation.getAsset(assetName);
// Skip assets that have already been optimized or are verbatim copies (project assets)
if (!asset || asset.info.minimized || asset.info.copied) {
continue;
}
const { source: styleAssetSource, name } = asset;
let cacheItem;
if (cache) {
const eTag = cache.getLazyHashedEtag(styleAssetSource);
cacheItem = cache.getItemCache(name, eTag);
const cachedOutput = await cacheItem.getPromise();
if (cachedOutput) {
logger.debug(`${name} restored from cache`);
await this.addWarnings(compilation, cachedOutput.warnings);
compilation.updateAsset(name, cachedOutput.source, (assetInfo) => ({
...assetInfo,
minimized: true,
}));
continue;
}
}
const { source, map: inputMap } = styleAssetSource.sourceAndMap();
const input = typeof source === 'string' ? source : source.toString();
const optimizeAssetLabel = `optimize asset: ${asset.name}`;
logger.time(optimizeAssetLabel);
const { code, warnings, map } = await this.optimize(input, asset.name, inputMap, this.targets);
logger.timeEnd(optimizeAssetLabel);
await this.addWarnings(compilation, warnings);
const optimizedAsset = map
? new SourceMapSource(code, name, map)
: new OriginalSource(code, name);
compilation.updateAsset(name, optimizedAsset, (assetInfo) => ({
...assetInfo,
minimized: true,
}));
await cacheItem?.storePromise({
source: optimizedAsset,
warnings,
});
}
logger.timeEnd('optimize css assets');
});
});
}
/**
* Optimizes a CSS asset using esbuild.
*
* @param input The CSS asset source content to optimize.
* @param name The name of the CSS asset. Used to generate source maps.
* @param inputMap Optionally specifies the CSS asset's original source map that will
* be merged with the intermediate optimized source map.
* @param target Optionally specifies the target browsers for the output code.
* @returns A promise resolving to the optimized CSS, source map, and any warnings.
*/
optimize(input, name, inputMap, target) {
let sourceMapLine;
if (inputMap) {
// esbuild will automatically remap the sourcemap if provided
sourceMapLine = `\n/*# sourceMappingURL=data:application/json;charset=utf-8;base64,${Buffer.from(JSON.stringify(inputMap)).toString('base64')} */`;
}
return this.esbuild.transform(sourceMapLine ? input + sourceMapLine : input, {
loader: 'css',
legalComments: 'inline',
minify: true,
sourcemap: !!inputMap && 'external',
sourcefile: name,
target,
});
}
async addWarnings(compilation, warnings) {
if (warnings.length > 0) {
for (const warning of await this.esbuild.formatMessages(warnings, { kind: 'warning' })) {
(0, webpack_diagnostics_1.addWarning)(compilation, warning);
}
}
}
}
exports.CssOptimizerPlugin = CssOptimizerPlugin;
;