UNPKG

@angular-devkit/build-angular

Version:
124 lines (123 loc) 5.79 kB
"use strict"; /** * @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;