minify-plugin.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const esbuild_1 = require("esbuild");
  4. const webpack_sources_1 = require("webpack-sources");
  5. const ModuleFilenameHelpers_js_1 = require("webpack/lib/ModuleFilenameHelpers.js");
  6. const isWebpack5 = (compilation) => ('processAssets' in compilation.hooks);
  7. // eslint-disable-next-line @typescript-eslint/no-var-requires
  8. const { version } = require('../package.json');
  9. const isJsFile = /\.[cm]?js(?:\?.*)?$/i;
  10. const isCssFile = /\.css(?:\?.*)?$/i;
  11. const pluginName = 'esbuild-minify';
  12. const granularMinifyConfigs = ['minifyIdentifiers', 'minifySyntax', 'minifyWhitespace'];
  13. class ESBuildMinifyPlugin {
  14. constructor(options = {}) {
  15. var _a;
  16. const { implementation, ...remainingOptions } = options;
  17. if (implementation && typeof implementation.transform !== 'function') {
  18. throw new TypeError(`ESBuildMinifyPlugin: implementation.transform must be an ESBuild transform function. Received ${typeof implementation.transform}`);
  19. }
  20. this.transform = (_a = implementation === null || implementation === void 0 ? void 0 : implementation.transform) !== null && _a !== void 0 ? _a : esbuild_1.transform;
  21. this.options = remainingOptions;
  22. const hasGranularMinificationConfig = granularMinifyConfigs.some(minifyConfig => minifyConfig in options);
  23. if (!hasGranularMinificationConfig) {
  24. this.options.minify = true;
  25. }
  26. }
  27. apply(compiler) {
  28. const meta = JSON.stringify({
  29. name: 'esbuild-loader',
  30. version,
  31. options: this.options,
  32. });
  33. compiler.hooks.compilation.tap(pluginName, (compilation) => {
  34. compilation.hooks.chunkHash.tap(pluginName, (_, hash) => hash.update(meta));
  35. if (isWebpack5(compilation)) {
  36. compilation.hooks.processAssets.tapPromise({
  37. name: pluginName,
  38. stage: compilation.constructor.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE,
  39. // @ts-expect-error TODO: modify type
  40. additionalAssets: true,
  41. }, async () => await this.transformAssets(compilation));
  42. compilation.hooks.statsPrinter.tap(pluginName, (statsPrinter) => {
  43. statsPrinter.hooks.print
  44. .for('asset.info.minimized')
  45. .tap(pluginName, (minimized, { green, formatFlag }) => (minimized
  46. ? green(formatFlag('minimized'))
  47. : undefined));
  48. });
  49. }
  50. else {
  51. compilation.hooks.optimizeChunkAssets.tapPromise(pluginName, async () => await this.transformAssets(compilation));
  52. }
  53. });
  54. }
  55. async transformAssets(compilation) {
  56. var _a;
  57. const { compiler } = compilation;
  58. const { options: { devtool } } = compiler;
  59. // @ts-expect-error Only exists on Webpack 5
  60. const sources = (_a = compiler.webpack) === null || _a === void 0 ? void 0 : _a.sources;
  61. const SourceMapSource = (sources ? sources.SourceMapSource : webpack_sources_1.SourceMapSource);
  62. const RawSource = (sources ? sources.RawSource : webpack_sources_1.RawSource);
  63. const sourcemap = (
  64. // TODO: drop support for esbuild sourcemap in future so it all goes through WP API
  65. // Might still be necessary when SourceMap plugin is used
  66. this.options.sourcemap === undefined
  67. ? Boolean(devtool && devtool.includes('source-map'))
  68. : this.options.sourcemap);
  69. const { css: minifyCss, include, exclude, ...transformOptions } = this.options;
  70. const assets = compilation.getAssets().filter(asset => (
  71. // Filter out already minimized
  72. !asset.info.minimized
  73. // Filter out by file type
  74. && (isJsFile.test(asset.name)
  75. || (minifyCss && isCssFile.test(asset.name)))
  76. && (0, ModuleFilenameHelpers_js_1.matchObject)({ include, exclude }, asset.name)));
  77. await Promise.all(assets.map(async (asset) => {
  78. const assetIsCss = isCssFile.test(asset.name);
  79. let source;
  80. let map = null;
  81. if (asset.source.sourceAndMap) {
  82. const sourceAndMap = asset.source.sourceAndMap();
  83. source = sourceAndMap.source;
  84. map = sourceAndMap.map;
  85. }
  86. else {
  87. source = asset.source.source();
  88. if (asset.source.map) {
  89. map = asset.source.map();
  90. }
  91. }
  92. const sourceAsString = source.toString();
  93. const result = await this.transform(sourceAsString, {
  94. ...transformOptions,
  95. loader: (assetIsCss
  96. ? 'css'
  97. : transformOptions.loader),
  98. sourcemap,
  99. sourcefile: asset.name,
  100. });
  101. if (result.legalComments) {
  102. compilation.emitAsset(`${asset.name}.LEGAL.txt`, new RawSource(result.legalComments));
  103. }
  104. compilation.updateAsset(asset.name, (sourcemap
  105. ? new SourceMapSource(result.code, asset.name, result.map, sourceAsString, map, true)
  106. : new RawSource(result.code)), {
  107. ...asset.info,
  108. minimized: true,
  109. });
  110. }));
  111. }
  112. }
  113. exports.default = ESBuildMinifyPlugin;