index.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var path = _interopRequireWildcard(require("path"));
  7. var os = _interopRequireWildcard(require("os"));
  8. var _sourceMap = require("source-map");
  9. var _schemaUtils = require("schema-utils");
  10. var _serializeJavascript = _interopRequireDefault(require("serialize-javascript"));
  11. var _jestWorker = require("jest-worker");
  12. var _utils = require("./utils");
  13. var schema = _interopRequireWildcard(require("./options.json"));
  14. var _minify = require("./minify");
  15. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  16. function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
  17. function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
  18. /** @typedef {import("schema-utils/declarations/validate").Schema} Schema */
  19. /** @typedef {import("webpack").Compiler} Compiler */
  20. /** @typedef {import("webpack").Compilation} Compilation */
  21. /** @typedef {import("webpack").WebpackError} WebpackError */
  22. /** @typedef {import("webpack").Asset} Asset */
  23. /** @typedef {import("./utils.js").TerserECMA} TerserECMA */
  24. /** @typedef {import("./utils.js").TerserOptions} TerserOptions */
  25. /** @typedef {import("./utils.js").CustomOptions} CustomOptions */
  26. /** @typedef {import("jest-worker").Worker} JestWorker */
  27. /** @typedef {import("source-map").RawSourceMap} RawSourceMap */
  28. /** @typedef {RegExp | string} Rule */
  29. /** @typedef {Rule[] | Rule} Rules */
  30. /**
  31. * @callback ExtractCommentsFunction
  32. * @param {any} astNode
  33. * @param {{ value: string, type: 'comment1' | 'comment2' | 'comment3' | 'comment4', pos: number, line: number, col: number }} comment
  34. * @returns {boolean}
  35. */
  36. /**
  37. * @typedef {boolean | 'all' | 'some' | RegExp | ExtractCommentsFunction} ExtractCommentsCondition
  38. */
  39. /**
  40. * @typedef {string | ((fileData: any) => string)} ExtractCommentsFilename
  41. */
  42. /**
  43. * @typedef {boolean | string | ((commentsFile: string) => string)} ExtractCommentsBanner
  44. */
  45. /**
  46. * @typedef {Object} ExtractCommentsObject
  47. * @property {ExtractCommentsCondition} [condition]
  48. * @property {ExtractCommentsFilename} [filename]
  49. * @property {ExtractCommentsBanner} [banner]
  50. */
  51. /**
  52. * @typedef {ExtractCommentsCondition | ExtractCommentsObject} ExtractCommentsOptions
  53. */
  54. /**
  55. * @typedef {{ [file: string]: string }} Input
  56. */
  57. /**
  58. * @typedef {Object} MinimizedResult
  59. * @property {string} code
  60. * @property {RawSourceMap} [map]
  61. * @property {Array<Error | string>} [errors]
  62. * @property {Array<Error | string>} [warnings]
  63. * @property {Array<string>} [extractedComments]
  64. */
  65. /**
  66. * @typedef {Object} PredefinedOptions
  67. * @property {boolean} [module]
  68. * @property {any} [ecma]
  69. */
  70. /**
  71. * @template T
  72. * @typedef {Object} MinimizerImplementationAndOptions
  73. * @property {MinimizerImplementation<T>} implementation
  74. * @property {PredefinedOptions & T} options
  75. */
  76. /**
  77. * @template T
  78. * @typedef {Object} InternalOptions
  79. * @property {string} name
  80. * @property {string} input
  81. * @property {RawSourceMap | undefined} inputSourceMap
  82. * @property {ExtractCommentsOptions | undefined} extractComments
  83. * @property {MinimizerImplementationAndOptions<T>} minimizer
  84. */
  85. /**
  86. * @template T
  87. * @typedef {JestWorker & { transform: (options: string) => MinimizedResult, minify: (options: InternalOptions<T>) => MinimizedResult }} MinimizerWorker
  88. */
  89. /**
  90. * @template T
  91. * @callback BasicMinimizerImplementation
  92. * @param {Input} input
  93. * @param {RawSourceMap | undefined} sourceMap
  94. * @param {T} minifyOptions
  95. * @param {ExtractCommentsOptions | undefined} extractComments
  96. * @returns {Promise<MinimizedResult>}
  97. */
  98. /**
  99. * @typedef {object} MinimizeFunctionHelpers
  100. * @property {() => string | undefined} [getMinimizerVersion]
  101. */
  102. /**
  103. * @template T
  104. * @typedef {BasicMinimizerImplementation<T> & MinimizeFunctionHelpers } MinimizerImplementation
  105. */
  106. /**
  107. * @typedef {undefined | boolean | number} Parallel
  108. */
  109. /**
  110. * @typedef {Object} BasePluginOptions
  111. * @property {Rules} [test]
  112. * @property {Rules} [include]
  113. * @property {Rules} [exclude]
  114. * @property {ExtractCommentsOptions} [extractComments]
  115. * @property {Parallel} [parallel]
  116. */
  117. /**
  118. * @template T
  119. * @typedef {T extends infer U ? U : CustomOptions} InferDefaultType
  120. */
  121. /**
  122. * @template T
  123. * @typedef {InferDefaultType<T> extends TerserOptions ? { minify?: MinimizerImplementation<InferDefaultType<T>> | undefined, terserOptions?: InferDefaultType<T> | undefined } : { minify: MinimizerImplementation<InferDefaultType<T>>, terserOptions?: InferDefaultType<T> | undefined }} DefinedDefaultMinimizerAndOptions
  124. */
  125. /**
  126. * @template T
  127. */
  128. class TerserPlugin {
  129. /**
  130. * @param {BasePluginOptions & DefinedDefaultMinimizerAndOptions<T>} [options]
  131. */
  132. constructor(options) {
  133. (0, _schemaUtils.validate)(
  134. /** @type {Schema} */
  135. schema, options || {}, {
  136. name: "Terser Plugin",
  137. baseDataPath: "options"
  138. }); // TODO make `minimizer` option instead `minify` and `terserOptions` in the next major release, also rename `terserMinify` to `terserMinimize`
  139. const {
  140. minify =
  141. /** @type {MinimizerImplementation<InferDefaultType<T>>} */
  142. _utils.terserMinify,
  143. terserOptions =
  144. /** @type {InferDefaultType<T>} */
  145. {},
  146. test = /\.[cm]?js(\?.*)?$/i,
  147. extractComments = true,
  148. parallel = true,
  149. include,
  150. exclude
  151. } = options || {};
  152. this.options = {
  153. test,
  154. extractComments,
  155. parallel,
  156. include,
  157. exclude,
  158. minimizer: {
  159. implementation: minify,
  160. options: terserOptions
  161. }
  162. };
  163. }
  164. /**
  165. * @private
  166. * @param {any} input
  167. * @returns {boolean}
  168. */
  169. static isSourceMap(input) {
  170. // All required options for `new SourceMapConsumer(...options)`
  171. // https://github.com/mozilla/source-map#new-sourcemapconsumerrawsourcemap
  172. return Boolean(input && input.version && input.sources && Array.isArray(input.sources) && typeof input.mappings === "string");
  173. }
  174. /**
  175. * @private
  176. * @param {Error | string} warning
  177. * @param {string} file
  178. * @returns {Error}
  179. */
  180. static buildWarning(warning, file) {
  181. /**
  182. * @type {Error & { hideStack: true, file: string }}
  183. */
  184. // @ts-ignore
  185. const builtWarning = new Error(warning.toString());
  186. builtWarning.name = "Warning";
  187. builtWarning.hideStack = true;
  188. builtWarning.file = file;
  189. return builtWarning;
  190. }
  191. /**
  192. * @private
  193. * @param {any} error
  194. * @param {string} file
  195. * @param {SourceMapConsumer} [sourceMap]
  196. * @param {Compilation["requestShortener"]} [requestShortener]
  197. * @returns {Error}
  198. */
  199. static buildError(error, file, sourceMap, requestShortener) {
  200. /**
  201. * @type {Error & { file: string }}
  202. */
  203. let builtError;
  204. if (typeof error === "string") {
  205. // @ts-ignore
  206. builtError = new Error(`${file} from Terser plugin\n${error}`);
  207. builtError.file = file;
  208. return builtError;
  209. }
  210. if (error.line) {
  211. const original = sourceMap && sourceMap.originalPositionFor({
  212. line: error.line,
  213. column: error.col
  214. });
  215. if (original && original.source && requestShortener) {
  216. // @ts-ignore
  217. builtError = new Error(`${file} from Terser plugin\n${error.message} [${requestShortener.shorten(original.source)}:${original.line},${original.column}][${file}:${error.line},${error.col}]${error.stack ? `\n${error.stack.split("\n").slice(1).join("\n")}` : ""}`);
  218. builtError.file = file;
  219. return builtError;
  220. } // @ts-ignore
  221. builtError = new Error(`${file} from Terser plugin\n${error.message} [${file}:${error.line},${error.col}]${error.stack ? `\n${error.stack.split("\n").slice(1).join("\n")}` : ""}`);
  222. builtError.file = file;
  223. return builtError;
  224. }
  225. if (error.stack) {
  226. // @ts-ignore
  227. builtError = new Error(`${file} from Terser plugin\n${typeof error.message !== "undefined" ? error.message : ""}\n${error.stack}`);
  228. builtError.file = file;
  229. return builtError;
  230. } // @ts-ignore
  231. builtError = new Error(`${file} from Terser plugin\n${error.message}`);
  232. builtError.file = file;
  233. return builtError;
  234. }
  235. /**
  236. * @private
  237. * @param {Parallel} parallel
  238. * @returns {number}
  239. */
  240. static getAvailableNumberOfCores(parallel) {
  241. // In some cases cpus() returns undefined
  242. // https://github.com/nodejs/node/issues/19022
  243. const cpus = os.cpus() || {
  244. length: 1
  245. };
  246. return parallel === true ? cpus.length - 1 : Math.min(Number(parallel) || 0, cpus.length - 1);
  247. }
  248. /**
  249. * @private
  250. * @param {Compiler} compiler
  251. * @param {Compilation} compilation
  252. * @param {Record<string, import("webpack").sources.Source>} assets
  253. * @param {{availableNumberOfCores: number}} optimizeOptions
  254. * @returns {Promise<void>}
  255. */
  256. async optimize(compiler, compilation, assets, optimizeOptions) {
  257. const cache = compilation.getCache("TerserWebpackPlugin");
  258. let numberOfAssets = 0;
  259. const assetsForMinify = await Promise.all(Object.keys(assets).filter(name => {
  260. const {
  261. info
  262. } =
  263. /** @type {Asset} */
  264. compilation.getAsset(name);
  265. if ( // Skip double minimize assets from child compilation
  266. info.minimized || // Skip minimizing for extracted comments assets
  267. info.extractedComments) {
  268. return false;
  269. }
  270. if (!compiler.webpack.ModuleFilenameHelpers.matchObject.bind( // eslint-disable-next-line no-undefined
  271. undefined, this.options)(name)) {
  272. return false;
  273. }
  274. return true;
  275. }).map(async name => {
  276. const {
  277. info,
  278. source
  279. } =
  280. /** @type {Asset} */
  281. compilation.getAsset(name);
  282. const eTag = cache.getLazyHashedEtag(source);
  283. const cacheItem = cache.getItemCache(name, eTag);
  284. const output = await cacheItem.getPromise();
  285. if (!output) {
  286. numberOfAssets += 1;
  287. }
  288. return {
  289. name,
  290. info,
  291. inputSource: source,
  292. output,
  293. cacheItem
  294. };
  295. }));
  296. if (assetsForMinify.length === 0) {
  297. return;
  298. }
  299. /** @type {undefined | (() => MinimizerWorker<T>)} */
  300. let getWorker;
  301. /** @type {undefined | MinimizerWorker<T>} */
  302. let initializedWorker;
  303. /** @type {undefined | number} */
  304. let numberOfWorkers;
  305. if (optimizeOptions.availableNumberOfCores > 0) {
  306. // Do not create unnecessary workers when the number of files is less than the available cores, it saves memory
  307. numberOfWorkers = Math.min(numberOfAssets, optimizeOptions.availableNumberOfCores); // eslint-disable-next-line consistent-return
  308. getWorker = () => {
  309. if (initializedWorker) {
  310. return initializedWorker;
  311. }
  312. initializedWorker =
  313. /** @type {MinimizerWorker<T>} */
  314. new _jestWorker.Worker(require.resolve("./minify"), {
  315. numWorkers: numberOfWorkers,
  316. enableWorkerThreads: true
  317. }); // https://github.com/facebook/jest/issues/8872#issuecomment-524822081
  318. const workerStdout = initializedWorker.getStdout();
  319. if (workerStdout) {
  320. workerStdout.on("data", chunk => process.stdout.write(chunk));
  321. }
  322. const workerStderr = initializedWorker.getStderr();
  323. if (workerStderr) {
  324. workerStderr.on("data", chunk => process.stderr.write(chunk));
  325. }
  326. return initializedWorker;
  327. };
  328. }
  329. const {
  330. SourceMapSource,
  331. ConcatSource,
  332. RawSource
  333. } = compiler.webpack.sources;
  334. /** @typedef {{ extractedCommentsSource : import("webpack").sources.RawSource, commentsFilename: string }} ExtractedCommentsInfo */
  335. /** @type {Map<string, ExtractedCommentsInfo>} */
  336. const allExtractedComments = new Map();
  337. const scheduledTasks = [];
  338. for (const asset of assetsForMinify) {
  339. scheduledTasks.push(async () => {
  340. const {
  341. name,
  342. inputSource,
  343. info,
  344. cacheItem
  345. } = asset;
  346. let {
  347. output
  348. } = asset;
  349. if (!output) {
  350. let input;
  351. /** @type {RawSourceMap | undefined} */
  352. let inputSourceMap;
  353. const {
  354. source: sourceFromInputSource,
  355. map
  356. } = inputSource.sourceAndMap();
  357. input = sourceFromInputSource;
  358. if (map) {
  359. if (TerserPlugin.isSourceMap(map)) {
  360. inputSourceMap =
  361. /** @type {RawSourceMap} */
  362. map;
  363. } else {
  364. inputSourceMap =
  365. /** @type {RawSourceMap} */
  366. map;
  367. compilation.warnings.push(
  368. /** @type {WebpackError} */
  369. new Error(`${name} contains invalid source map`));
  370. }
  371. }
  372. if (Buffer.isBuffer(input)) {
  373. input = input.toString();
  374. }
  375. const options = {
  376. name,
  377. input,
  378. inputSourceMap,
  379. minimizer: {
  380. implementation: this.options.minimizer.implementation,
  381. // @ts-ignore https://github.com/Microsoft/TypeScript/issues/10727
  382. options: { ...this.options.minimizer.options
  383. }
  384. },
  385. extractComments: this.options.extractComments
  386. };
  387. if (typeof options.minimizer.options.module === "undefined") {
  388. if (typeof info.javascriptModule !== "undefined") {
  389. options.minimizer.options.module = info.javascriptModule;
  390. } else if (/\.mjs(\?.*)?$/i.test(name)) {
  391. options.minimizer.options.module = true;
  392. } else if (/\.cjs(\?.*)?$/i.test(name)) {
  393. options.minimizer.options.module = false;
  394. }
  395. }
  396. if (typeof options.minimizer.options.ecma === "undefined") {
  397. options.minimizer.options.ecma = TerserPlugin.getEcmaVersion(compiler.options.output.environment || {});
  398. }
  399. try {
  400. output = await (getWorker ? getWorker().transform((0, _serializeJavascript.default)(options)) : (0, _minify.minify)(options));
  401. } catch (error) {
  402. const hasSourceMap = inputSourceMap && TerserPlugin.isSourceMap(inputSourceMap);
  403. compilation.errors.push(
  404. /** @type {WebpackError} */
  405. TerserPlugin.buildError(error, name, hasSourceMap ? new _sourceMap.SourceMapConsumer(
  406. /** @type {RawSourceMap} */
  407. inputSourceMap) : // eslint-disable-next-line no-undefined
  408. undefined, // eslint-disable-next-line no-undefined
  409. hasSourceMap ? compilation.requestShortener : undefined));
  410. return;
  411. }
  412. if (typeof output.code === "undefined") {
  413. compilation.errors.push(
  414. /** @type {WebpackError} */
  415. new Error(`${name} from Terser plugin\nMinimizer doesn't return result`));
  416. return;
  417. }
  418. if (output.warnings && output.warnings.length > 0) {
  419. output.warnings = output.warnings.map(
  420. /**
  421. * @param {Error | string} item
  422. */
  423. item => TerserPlugin.buildWarning(item, name));
  424. }
  425. if (output.errors && output.errors.length > 0) {
  426. const hasSourceMap = inputSourceMap && TerserPlugin.isSourceMap(inputSourceMap);
  427. output.errors = output.errors.map(
  428. /**
  429. * @param {Error | string} item
  430. */
  431. item => TerserPlugin.buildError(item, name, hasSourceMap ? new _sourceMap.SourceMapConsumer(
  432. /** @type {RawSourceMap} */
  433. inputSourceMap) : // eslint-disable-next-line no-undefined
  434. undefined, // eslint-disable-next-line no-undefined
  435. hasSourceMap ? compilation.requestShortener : undefined));
  436. }
  437. let shebang;
  438. if (
  439. /** @type {ExtractCommentsObject} */
  440. this.options.extractComments.banner !== false && output.extractedComments && output.extractedComments.length > 0 && output.code.startsWith("#!")) {
  441. const firstNewlinePosition = output.code.indexOf("\n");
  442. shebang = output.code.substring(0, firstNewlinePosition);
  443. output.code = output.code.substring(firstNewlinePosition + 1);
  444. }
  445. if (output.map) {
  446. output.source = new SourceMapSource(output.code, name, output.map, input,
  447. /** @type {RawSourceMap} */
  448. inputSourceMap, true);
  449. } else {
  450. output.source = new RawSource(output.code);
  451. }
  452. if (output.extractedComments && output.extractedComments.length > 0) {
  453. const commentsFilename =
  454. /** @type {ExtractCommentsObject} */
  455. this.options.extractComments.filename || "[file].LICENSE.txt[query]";
  456. let query = "";
  457. let filename = name;
  458. const querySplit = filename.indexOf("?");
  459. if (querySplit >= 0) {
  460. query = filename.substr(querySplit);
  461. filename = filename.substr(0, querySplit);
  462. }
  463. const lastSlashIndex = filename.lastIndexOf("/");
  464. const basename = lastSlashIndex === -1 ? filename : filename.substr(lastSlashIndex + 1);
  465. const data = {
  466. filename,
  467. basename,
  468. query
  469. };
  470. output.commentsFilename = compilation.getPath(commentsFilename, data);
  471. let banner; // Add a banner to the original file
  472. if (
  473. /** @type {ExtractCommentsObject} */
  474. this.options.extractComments.banner !== false) {
  475. banner =
  476. /** @type {ExtractCommentsObject} */
  477. this.options.extractComments.banner || `For license information please see ${path.relative(path.dirname(name), output.commentsFilename).replace(/\\/g, "/")}`;
  478. if (typeof banner === "function") {
  479. banner = banner(output.commentsFilename);
  480. }
  481. if (banner) {
  482. output.source = new ConcatSource(shebang ? `${shebang}\n` : "", `/*! ${banner} */\n`, output.source);
  483. }
  484. }
  485. const extractedCommentsString = output.extractedComments.sort().join("\n\n");
  486. output.extractedCommentsSource = new RawSource(`${extractedCommentsString}\n`);
  487. }
  488. await cacheItem.storePromise({
  489. source: output.source,
  490. errors: output.errors,
  491. warnings: output.warnings,
  492. commentsFilename: output.commentsFilename,
  493. extractedCommentsSource: output.extractedCommentsSource
  494. });
  495. }
  496. if (output.warnings && output.warnings.length > 0) {
  497. for (const warning of output.warnings) {
  498. compilation.warnings.push(
  499. /** @type {WebpackError} */
  500. warning);
  501. }
  502. }
  503. if (output.errors && output.errors.length > 0) {
  504. for (const error of output.errors) {
  505. compilation.errors.push(
  506. /** @type {WebpackError} */
  507. error);
  508. }
  509. }
  510. /** @type {Record<string, any>} */
  511. const newInfo = {
  512. minimized: true
  513. };
  514. const {
  515. source,
  516. extractedCommentsSource
  517. } = output; // Write extracted comments to commentsFilename
  518. if (extractedCommentsSource) {
  519. const {
  520. commentsFilename
  521. } = output;
  522. newInfo.related = {
  523. license: commentsFilename
  524. };
  525. allExtractedComments.set(name, {
  526. extractedCommentsSource,
  527. commentsFilename
  528. });
  529. }
  530. compilation.updateAsset(name, source, newInfo);
  531. });
  532. }
  533. const limit = getWorker && numberOfAssets > 0 ?
  534. /** @type {number} */
  535. numberOfWorkers : scheduledTasks.length;
  536. await (0, _utils.throttleAll)(limit, scheduledTasks);
  537. if (initializedWorker) {
  538. await initializedWorker.end();
  539. }
  540. /** @typedef {{ source: import("webpack").sources.Source, commentsFilename: string, from: string }} ExtractedCommentsInfoWIthFrom */
  541. await Array.from(allExtractedComments).sort().reduce(
  542. /**
  543. * @param {Promise<unknown>} previousPromise
  544. * @param {[string, ExtractedCommentsInfo]} extractedComments
  545. * @returns {Promise<ExtractedCommentsInfoWIthFrom>}
  546. */
  547. async (previousPromise, [from, value]) => {
  548. const previous =
  549. /** @type {ExtractedCommentsInfoWIthFrom | undefined} **/
  550. await previousPromise;
  551. const {
  552. commentsFilename,
  553. extractedCommentsSource
  554. } = value;
  555. if (previous && previous.commentsFilename === commentsFilename) {
  556. const {
  557. from: previousFrom,
  558. source: prevSource
  559. } = previous;
  560. const mergedName = `${previousFrom}|${from}`;
  561. const name = `${commentsFilename}|${mergedName}`;
  562. const eTag = [prevSource, extractedCommentsSource].map(item => cache.getLazyHashedEtag(item)).reduce((previousValue, currentValue) => cache.mergeEtags(previousValue, currentValue));
  563. let source = await cache.getPromise(name, eTag);
  564. if (!source) {
  565. source = new ConcatSource(Array.from(new Set([...
  566. /** @type {string}*/
  567. prevSource.source().split("\n\n"), ...
  568. /** @type {string}*/
  569. extractedCommentsSource.source().split("\n\n")])).join("\n\n"));
  570. await cache.storePromise(name, eTag, source);
  571. }
  572. compilation.updateAsset(commentsFilename, source);
  573. return {
  574. source,
  575. commentsFilename,
  576. from: mergedName
  577. };
  578. }
  579. const existingAsset = compilation.getAsset(commentsFilename);
  580. if (existingAsset) {
  581. return {
  582. source: existingAsset.source,
  583. commentsFilename,
  584. from: commentsFilename
  585. };
  586. }
  587. compilation.emitAsset(commentsFilename, extractedCommentsSource, {
  588. extractedComments: true
  589. });
  590. return {
  591. source: extractedCommentsSource,
  592. commentsFilename,
  593. from
  594. };
  595. },
  596. /** @type {Promise<unknown>} */
  597. Promise.resolve());
  598. }
  599. /**
  600. * @private
  601. * @param {any} environment
  602. * @returns {TerserECMA}
  603. */
  604. static getEcmaVersion(environment) {
  605. // ES 6th
  606. if (environment.arrowFunction || environment.const || environment.destructuring || environment.forOf || environment.module) {
  607. return 2015;
  608. } // ES 11th
  609. if (environment.bigIntLiteral || environment.dynamicImport) {
  610. return 2020;
  611. }
  612. return 5;
  613. }
  614. /**
  615. * @param {Compiler} compiler
  616. * @returns {void}
  617. */
  618. apply(compiler) {
  619. const pluginName = this.constructor.name;
  620. const availableNumberOfCores = TerserPlugin.getAvailableNumberOfCores(this.options.parallel);
  621. compiler.hooks.compilation.tap(pluginName, compilation => {
  622. const hooks = compiler.webpack.javascript.JavascriptModulesPlugin.getCompilationHooks(compilation);
  623. const data = (0, _serializeJavascript.default)({
  624. minimizer: typeof this.options.minimizer.implementation.getMinimizerVersion !== "undefined" ? this.options.minimizer.implementation.getMinimizerVersion() || "0.0.0" : "0.0.0",
  625. options: this.options.minimizer.options
  626. });
  627. hooks.chunkHash.tap(pluginName, (chunk, hash) => {
  628. hash.update("TerserPlugin");
  629. hash.update(data);
  630. });
  631. compilation.hooks.processAssets.tapPromise({
  632. name: pluginName,
  633. stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE,
  634. additionalAssets: true
  635. }, assets => this.optimize(compiler, compilation, assets, {
  636. availableNumberOfCores
  637. }));
  638. compilation.hooks.statsPrinter.tap(pluginName, stats => {
  639. stats.hooks.print.for("asset.info.minimized").tap("terser-webpack-plugin", (minimized, {
  640. green,
  641. formatFlag
  642. }) => minimized ?
  643. /** @type {Function} */
  644. green(
  645. /** @type {Function} */
  646. formatFlag("minimized")) : "");
  647. });
  648. });
  649. }
  650. }
  651. TerserPlugin.terserMinify = _utils.terserMinify;
  652. TerserPlugin.uglifyJsMinify = _utils.uglifyJsMinify;
  653. TerserPlugin.swcMinify = _utils.swcMinify;
  654. TerserPlugin.esbuildMinify = _utils.esbuildMinify;
  655. var _default = TerserPlugin;
  656. exports.default = _default;