source: frontend/node_modules/terser-webpack-plugin/dist/index.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 29.2 KB
Line 
1"use strict";
2
3const os = require("os");
4const path = require("path");
5const {
6 validate
7} = require("schema-utils");
8const {
9 minify
10} = require("./minify");
11const schema = require("./options.json");
12const {
13 cleanCssMinify,
14 cssnanoMinify,
15 cssoMinify,
16 esbuildMinify,
17 esbuildMinifyCss,
18 getEcmaVersion,
19 htmlMinifierTerser,
20 jsonMinify,
21 lightningCssMinify,
22 memoize,
23 minifyHtmlNode,
24 swcMinify,
25 swcMinifyCss,
26 swcMinifyHtml,
27 swcMinifyHtmlFragment,
28 terserMinify,
29 throttleAll,
30 uglifyJsMinify
31} = require("./utils");
32
33/** @typedef {import("schema-utils/declarations/validate").Schema} Schema */
34/** @typedef {import("webpack").Compiler} Compiler */
35/** @typedef {import("webpack").Compilation} Compilation */
36/** @typedef {import("webpack").Asset} Asset */
37/** @typedef {import("webpack").AssetInfo} AssetInfo */
38/** @typedef {import("webpack").TemplatePath} TemplatePath */
39/** @typedef {import("jest-worker").Worker} JestWorker */
40/** @typedef {import("@jridgewell/trace-mapping").EncodedSourceMap & { sources: string[], sourcesContent?: string[], file: string }} RawSourceMap */
41/** @typedef {import("@jridgewell/trace-mapping").TraceMap} TraceMap */
42
43/** @typedef {RegExp | string} Rule */
44/** @typedef {Rule[] | Rule} Rules */
45
46// eslint-disable-next-line jsdoc/reject-any-type
47/** @typedef {any} EXPECTED_ANY */
48// eslint-disable-next-line jsdoc/require-property
49/** @typedef {object} EXPECTED_OBJECT */
50
51/**
52 * @callback ExtractCommentsFunction
53 * @param {EXPECTED_ANY} astNode ast Node
54 * @param {{ value: string, type: "comment1" | "comment2" | "comment3" | "comment4", pos: number, line: number, col: number }} comment comment node
55 * @returns {boolean} true when need to extract comment, otherwise false
56 */
57
58/**
59 * @typedef {boolean | "all" | "some" | RegExp | ExtractCommentsFunction} ExtractCommentsCondition
60 */
61
62/**
63 * @typedef {TemplatePath} ExtractCommentsFilename
64 */
65
66/**
67 * @typedef {boolean | string | ((commentsFile: string) => string)} ExtractCommentsBanner
68 */
69
70/**
71 * @typedef {object} ExtractCommentsObject
72 * @property {ExtractCommentsCondition=} condition condition which comments need to be expected
73 * @property {ExtractCommentsFilename=} filename filename for extracted comments
74 * @property {ExtractCommentsBanner=} banner banner in filename for extracted comments
75 */
76
77/**
78 * @typedef {ExtractCommentsCondition | ExtractCommentsObject} ExtractCommentsOptions
79 */
80
81/**
82 * @typedef {object} ErrorObject
83 * @property {string} message message
84 * @property {number=} line line number
85 * @property {number=} column column number
86 * @property {string=} stack error stack trace
87 */
88
89/**
90 * @typedef {object} MinimizedResult
91 * @property {string=} code code
92 * @property {RawSourceMap=} map source map
93 * @property {(Error | string)[]=} errors errors
94 * @property {(Error | string)[]=} warnings warnings
95 * @property {string[]=} extractedComments extracted comments
96 */
97
98/**
99 * @typedef {{ [file: string]: string }} Input
100 */
101
102/**
103 * @typedef {{ [key: string]: EXPECTED_ANY }} CustomOptions
104 */
105
106/**
107 * @template T
108 * @typedef {T extends infer U ? U : CustomOptions} InferDefaultType
109 */
110
111/**
112 * @template T
113 * @typedef {T extends EXPECTED_ANY[] ? { [P in keyof T]?: T[P] & InferDefaultType<T[P]> } : T & InferDefaultType<T>} MinimizerOptions
114 */
115
116/**
117 * @template T
118 * @callback BasicMinimizerImplementation
119 * @param {Input} input
120 * @param {RawSourceMap | undefined} sourceMap
121 * @param {MinimizerOptions<T>} minifyOptions
122 * @param {ExtractCommentsOptions | undefined} extractComments
123 * @returns {Promise<MinimizedResult> | MinimizedResult}
124 */
125
126/**
127 * @typedef {object} MinimizeFunctionHelpers
128 * @property {() => string | undefined=} getMinimizerVersion function that returns version of minimizer
129 * @property {() => boolean | undefined=} supportsWorkerThreads true when minimizer support worker threads, otherwise false
130 * @property {() => boolean | undefined=} supportsWorker true when minimizer support worker, otherwise false
131 * @property {(name: string, info?: AssetInfo) => boolean | undefined=} filter return true when the minimizer supports the asset, otherwise false. When an array of minimizers is configured, each asset is dispatched only to the minimizers whose `filter` accepts it. Assets rejected by every minimizer in the array are skipped entirely.
132 */
133
134/**
135 * @template T
136 * @typedef {T extends EXPECTED_ANY[] ? { [P in keyof T]: BasicMinimizerImplementation<T[P]> & MinimizeFunctionHelpers } : BasicMinimizerImplementation<T> & MinimizeFunctionHelpers} MinimizerImplementation
137 */
138
139/**
140 * @template T
141 * @typedef {object} InternalOptions
142 * @property {string} name name
143 * @property {string} input input
144 * @property {RawSourceMap | undefined} inputSourceMap input source map
145 * @property {ExtractCommentsOptions | undefined} extractComments extract comments option
146 * @property {{ implementation: MinimizerImplementation<T>, options: MinimizerOptions<T> }} minimizer minimizer
147 * @property {boolean=} module true when code is a EC module, otherwise false
148 * @property {number | string=} ecma ecma version
149 */
150
151/**
152 * @template T
153 * @typedef {JestWorker & { transform: (options: string) => Promise<MinimizedResult>, minify: (options: InternalOptions<T>) => Promise<MinimizedResult> }} MinimizerWorker
154 */
155
156/**
157 * @typedef {undefined | boolean | number} Parallel
158 */
159
160/**
161 * @typedef {object} BasePluginOptions
162 * @property {Rules=} test test rule
163 * @property {Rules=} include include rile
164 * @property {Rules=} exclude exclude rule
165 * @property {ExtractCommentsOptions=} extractComments extract comments options
166 * @property {Parallel=} parallel parallel option
167 */
168
169/**
170 * @template T
171 * @typedef {T extends import("terser").MinifyOptions ? { minify?: MinimizerImplementation<T> | undefined, minimizerOptions?: MinimizerOptions<T> | undefined, terserOptions?: MinimizerOptions<T> | undefined } : { minify: MinimizerImplementation<T>, minimizerOptions?: MinimizerOptions<T> | undefined, terserOptions?: MinimizerOptions<T> | undefined }} DefinedDefaultMinimizerAndOptions
172 */
173
174/**
175 * @template T
176 * @typedef {BasePluginOptions & { minimizer: { implementation: MinimizerImplementation<T>, options: MinimizerOptions<T> } }} InternalPluginOptions
177 */
178
179const getTraceMapping = memoize(() => require("@jridgewell/trace-mapping"));
180const getSerializeJavascript = memoize(() => require("./serialize-javascript"));
181
182/**
183 * @template [T=import("terser").MinifyOptions]
184 */
185class TerserPlugin {
186 /**
187 * @param {BasePluginOptions & DefinedDefaultMinimizerAndOptions<T>=} options options
188 */
189 constructor(options) {
190 validate(/** @type {Schema} */schema, options || {}, {
191 name: "Terser Plugin",
192 baseDataPath: "options"
193 });
194
195 // TODO handle json and etc in the next major release
196 // TODO make `minimizer` option instead `minify` and `terserOptions` in the next major release, also rename `terserMinify` to `terserMinimize`
197 const {
198 minify = (/** @type {MinimizerImplementation<T>} */
199 /** @type {unknown} */terserMinify),
200 minimizerOptions,
201 terserOptions,
202 test = /\.[cm]?js(\?.*)?$/i,
203 extractComments = true,
204 parallel = true,
205 include,
206 exclude
207 } = options || {};
208
209 // `terserOptions` is a deprecated alias of `minimizerOptions`; prefer the
210 // new name when both are provided.
211 const resolvedMinimizerOptions = /** @type {MinimizerOptions<T>} */
212
213 typeof minimizerOptions !== "undefined" ? minimizerOptions : terserOptions || {};
214
215 /**
216 * @private
217 * @type {InternalPluginOptions<T>}
218 */
219 this.options = {
220 test,
221 extractComments,
222 parallel,
223 include,
224 exclude,
225 minimizer: {
226 implementation: minify,
227 options: resolvedMinimizerOptions
228 }
229 };
230 }
231
232 /**
233 * @private
234 * @param {unknown} input Input to check
235 * @returns {boolean} Whether input is a source map
236 */
237 static isSourceMap(input) {
238 // All required options for `new TraceMap(...options)`
239 // https://github.com/jridgewell/trace-mapping#usage
240 return Boolean(input && typeof input === "object" && input !== null && "version" in input && "sources" in input && Array.isArray(input.sources) && "mappings" in input && typeof input.mappings === "string");
241 }
242
243 /**
244 * @private
245 * @param {unknown} warning warning
246 * @param {string} file file
247 * @returns {Error} built warning
248 */
249 static buildWarning(warning, file) {
250 /**
251 * @type {Error & { hideStack: true, file: string }}
252 */
253 // @ts-expect-error
254 const builtWarning = new Error(warning.toString());
255 builtWarning.name = "Warning";
256 builtWarning.hideStack = true;
257 builtWarning.file = file;
258 return builtWarning;
259 }
260
261 /**
262 * @private
263 * @param {Error | ErrorObject | string} error error
264 * @param {string} file file
265 * @param {TraceMap=} sourceMap source map
266 * @param {Compilation["requestShortener"]=} requestShortener request shortener
267 * @returns {Error} built error
268 */
269 static buildError(error, file, sourceMap, requestShortener) {
270 /**
271 * @type {Error & { file?: string }}
272 */
273 let builtError;
274 if (typeof error === "string") {
275 builtError = new Error(`${file} from Terser plugin\n${error}`);
276 builtError.file = file;
277 return builtError;
278 }
279 if (/** @type {ErrorObject} */error.line) {
280 const {
281 line,
282 column
283 } = /** @type {ErrorObject & { line: number, column: number }} */error;
284 const original = sourceMap && getTraceMapping().originalPositionFor(sourceMap, {
285 line,
286 column
287 });
288 if (original && original.source && requestShortener) {
289 builtError = new Error(`${file} from Terser plugin\n${error.message} [${requestShortener.shorten(original.source)}:${original.line},${original.column}][${file}:${line},${column}]${error.stack ? `\n${error.stack.split("\n").slice(1).join("\n")}` : ""}`);
290 builtError.file = file;
291 return builtError;
292 }
293 builtError = new Error(`${file} from Terser plugin\n${error.message} [${file}:${line},${column}]${error.stack ? `\n${error.stack.split("\n").slice(1).join("\n")}` : ""}`);
294 builtError.file = file;
295 return builtError;
296 }
297 if (error.stack) {
298 builtError = new Error(`${file} from Terser plugin\n${typeof error.message !== "undefined" ? error.message : ""}\n${error.stack}`);
299 builtError.file = file;
300 return builtError;
301 }
302 builtError = new Error(`${file} from Terser plugin\n${error.message}`);
303 builtError.file = file;
304 return builtError;
305 }
306
307 /**
308 * @private
309 * @param {Parallel} parallel value of the `parallel` option
310 * @returns {number} number of cores for parallelism
311 */
312 static getAvailableNumberOfCores(parallel) {
313 // In some cases cpus() returns undefined
314 // https://github.com/nodejs/node/issues/19022
315 const cpus =
316 // eslint-disable-next-line n/no-unsupported-features/node-builtins
317 typeof os.availableParallelism === "function" ?
318 // eslint-disable-next-line n/no-unsupported-features/node-builtins
319 {
320 length: os.availableParallelism()
321 } : os.cpus() || {
322 length: 1
323 };
324 return parallel === true || typeof parallel === "undefined" ? cpus.length - 1 : Math.min(parallel || 0, cpus.length - 1);
325 }
326
327 /**
328 * @private
329 * @param {Compiler} compiler compiler
330 * @param {Compilation} compilation compilation
331 * @param {Record<string, import("webpack").sources.Source>} assets assets
332 * @param {{ availableNumberOfCores: number }} optimizeOptions optimize options
333 * @returns {Promise<void>}
334 */
335 async optimize(compiler, compilation, assets, optimizeOptions) {
336 const cache = compilation.getCache("TerserWebpackPlugin");
337 let numberOfAssets = 0;
338
339 // Normalize the implementation list to an array so dispatch and the
340 // worker-pool capability checks below can iterate uniformly. The
341 // original shape on `this.options.minimizer.implementation` is preserved
342 // for chunk hashing.
343 const implementations = Array.isArray(this.options.minimizer.implementation) ? this.options.minimizer.implementation : [this.options.minimizer.implementation];
344
345 /**
346 * Collect the indices of minimizers whose `filter` accepts `name`.
347 * Filters returning `undefined` are treated as accept (matches the
348 * convention used by `supportsWorkerThreads`).
349 * @param {string} name asset name
350 * @param {AssetInfo} info asset info
351 * @returns {number[]} indices into `implementations` that accept the asset
352 */
353 const matchingMinimizers = (name, info) => {
354 const matched = [];
355 for (let i = 0; i < implementations.length; i++) {
356 const impl = implementations[i];
357 if (typeof impl.filter !== "function" ||
358 // eslint-disable-next-line unicorn/no-array-method-this-argument
359 impl.filter(name, info) !== false) {
360 matched.push(i);
361 }
362 }
363 return matched;
364 };
365 /** @type {Map<string, number[]>} */
366 const matchedByName = new Map();
367 const assetsForMinify = await Promise.all(Object.keys(assets).filter(name => {
368 const {
369 info
370 } = /** @type {Asset} */compilation.getAsset(name);
371 if (
372 // Skip double minimize assets from child compilation
373 info.minimized ||
374 // Skip minimizing for extracted comments assets
375 info.extractedComments) {
376 return false;
377 }
378 if (!compiler.webpack.ModuleFilenameHelpers.matchObject.bind(undefined, this.options)(name)) {
379 return false;
380 }
381
382 // Compute the matching minimizers once and carry the result to the
383 // per-asset task via `matchedByName` so the regexes don't run again.
384 const matched = matchingMinimizers(name, info);
385 if (matched.length === 0) {
386 return false;
387 }
388 matchedByName.set(name, matched);
389 return true;
390 }).map(async name => {
391 const {
392 info,
393 source
394 } = /** @type {Asset} */
395 compilation.getAsset(name);
396 const eTag = cache.getLazyHashedEtag(source);
397 const cacheItem = cache.getItemCache(name, eTag);
398 const output = await cacheItem.getPromise();
399 if (!output) {
400 numberOfAssets += 1;
401 }
402 return {
403 name,
404 info,
405 inputSource: source,
406 output,
407 cacheItem,
408 matched: (/** @type {number[]} */matchedByName.get(name))
409 };
410 }));
411 if (assetsForMinify.length === 0) {
412 return;
413 }
414
415 /** @type {undefined | (() => MinimizerWorker<T>)} */
416 let getWorker;
417 /** @type {undefined | MinimizerWorker<T>} */
418 let initializedWorker;
419 /** @type {undefined | number} */
420 let numberOfWorkers;
421 const needCreateWorker = optimizeOptions.availableNumberOfCores > 0 && implementations.every(impl => typeof impl.supportsWorker === "undefined" || typeof impl.supportsWorker === "function" && impl.supportsWorker());
422 if (needCreateWorker) {
423 // Do not create unnecessary workers when the number of files is less than the available cores, it saves memory
424 numberOfWorkers = Math.min(numberOfAssets, optimizeOptions.availableNumberOfCores);
425 getWorker = () => {
426 if (initializedWorker) {
427 return initializedWorker;
428 }
429 const {
430 Worker
431 } = require("jest-worker");
432 initializedWorker = /** @type {MinimizerWorker<T>} */
433
434 new Worker(require.resolve("./minify"), {
435 numWorkers: numberOfWorkers,
436 enableWorkerThreads: implementations.every(impl => typeof impl.supportsWorkerThreads === "undefined" || impl.supportsWorkerThreads() !== false)
437 });
438
439 // https://github.com/facebook/jest/issues/8872#issuecomment-524822081
440 const workerStdout = initializedWorker.getStdout();
441 if (workerStdout) {
442 workerStdout.on("data", chunk => process.stdout.write(chunk));
443 }
444 const workerStderr = initializedWorker.getStderr();
445 if (workerStderr) {
446 workerStderr.on("data", chunk => process.stderr.write(chunk));
447 }
448 return initializedWorker;
449 };
450 }
451 const {
452 SourceMapSource,
453 ConcatSource,
454 RawSource
455 } = compiler.webpack.sources;
456
457 /** @typedef {{ extractedCommentsSource: import("webpack").sources.RawSource, commentsFilename: string }} ExtractedCommentsInfo */
458 /** @type {Map<string, ExtractedCommentsInfo>} */
459 const allExtractedComments = new Map();
460 const scheduledTasks = [];
461 for (const asset of assetsForMinify) {
462 scheduledTasks.push(async () => {
463 const {
464 name,
465 inputSource,
466 info,
467 cacheItem,
468 matched
469 } = asset;
470 let {
471 output
472 } = asset;
473 if (!output) {
474 let input;
475 /** @type {RawSourceMap | undefined} */
476 let inputSourceMap;
477 const {
478 source: sourceFromInputSource,
479 map
480 } = inputSource.sourceAndMap();
481 input = sourceFromInputSource;
482 if (map) {
483 if (!TerserPlugin.isSourceMap(map)) {
484 compilation.warnings.push(new Error(`${name} contains invalid source map`));
485 } else {
486 inputSourceMap = /** @type {RawSourceMap} */map;
487 }
488 }
489 if (Buffer.isBuffer(input)) {
490 input = input.toString();
491 }
492
493 // Dispatch to only the minimizers whose `filter` accepted this
494 // asset (computed once when collecting `assetsForMinify`).
495 // `minify.js` already normalizes a single implementation into a
496 // one-element array, so we always hand it the matching subset.
497 // Options are sliced as references — `minify.js` overlays
498 // `module`/`ecma` without mutating the caller's object.
499 const assetImplementation = /** @type {MinimizerImplementation<T>} */
500 matched.map(i => implementations[i]);
501 const sourceOptions = this.options.minimizer.options;
502 const assetMinimizerOptions = /** @type {MinimizerOptions<T>} */
503
504 Array.isArray(sourceOptions) ? matched.map(i => sourceOptions[i] || {}) : sourceOptions;
505
506 /**
507 * @type {InternalOptions<T>}
508 */
509 const options = {
510 name,
511 input,
512 inputSourceMap,
513 minimizer: {
514 implementation: assetImplementation,
515 options: assetMinimizerOptions
516 },
517 extractComments: this.options.extractComments
518 };
519 if (typeof info.javascriptModule !== "undefined") {
520 options.module = info.javascriptModule;
521 } else if (/\.mjs(\?.*)?$/i.test(name)) {
522 options.module = true;
523 } else if (/\.cjs(\?.*)?$/i.test(name)) {
524 options.module = false;
525 }
526 options.ecma = getEcmaVersion(compiler.options.output.environment);
527 try {
528 output = await (getWorker ? getWorker().transform(getSerializeJavascript()(options)) : minify(options));
529 } catch (error) {
530 const hasSourceMap = inputSourceMap && TerserPlugin.isSourceMap(inputSourceMap);
531 compilation.errors.push(TerserPlugin.buildError(/** @type {Error | ErrorObject | string} */
532 error, name, hasSourceMap ? new (getTraceMapping().TraceMap)(/** @type {RawSourceMap} */
533 inputSourceMap) : undefined, hasSourceMap ? compilation.requestShortener : undefined));
534 return;
535 }
536 if (typeof output.code === "undefined") {
537 compilation.errors.push(new Error(`${name} from Terser plugin\nMinimizer doesn't return result`));
538 }
539 if (output.warnings && output.warnings.length > 0) {
540 output.warnings = output.warnings.map(
541 /**
542 * @param {Error | string} item a warning
543 * @returns {Error} built warning with extra info
544 */
545 item => TerserPlugin.buildWarning(item, name));
546 }
547 if (output.errors && output.errors.length > 0) {
548 const hasSourceMap = inputSourceMap && TerserPlugin.isSourceMap(inputSourceMap);
549 output.errors = output.errors.map(
550 /**
551 * @param {Error | string} item an error
552 * @returns {Error} built error with extra info
553 */
554 item => TerserPlugin.buildError(item, name, hasSourceMap ? new (getTraceMapping().TraceMap)(/** @type {RawSourceMap} */
555 inputSourceMap) : undefined, hasSourceMap ? compilation.requestShortener : undefined));
556 }
557 let shebang;
558
559 // Custom functions can return `undefined` or `null` when the
560 // minimizer only produced warnings, errors or extracted comments
561 if (typeof output.code !== "undefined" && output.code !== null) {
562 if (/** @type {ExtractCommentsObject} */
563 this.options.extractComments.banner !== false && output.extractedComments && output.extractedComments.length > 0 && output.code.startsWith("#!")) {
564 const firstNewlinePosition = output.code.indexOf("\n");
565 shebang = output.code.slice(0, Math.max(0, firstNewlinePosition));
566 output.code = output.code.slice(Math.max(0, firstNewlinePosition + 1));
567 }
568 if (output.map) {
569 output.source = new SourceMapSource(output.code, name, output.map, input, /** @type {RawSourceMap} */
570 inputSourceMap, true);
571 } else {
572 output.source = new RawSource(output.code);
573 }
574 }
575 if (output.extractedComments && output.extractedComments.length > 0) {
576 const commentsFilename = /** @type {ExtractCommentsObject} */
577 this.options.extractComments.filename || "[file].LICENSE.txt[query]";
578 let query = "";
579 let filename = name;
580 const querySplit = filename.indexOf("?");
581 if (querySplit >= 0) {
582 query = filename.slice(querySplit);
583 filename = filename.slice(0, querySplit);
584 }
585 const lastSlashIndex = filename.lastIndexOf("/");
586 const basename = lastSlashIndex === -1 ? filename : filename.slice(lastSlashIndex + 1);
587 const data = {
588 filename,
589 basename,
590 query
591 };
592 output.commentsFilename = compilation.getPath(commentsFilename, data);
593
594 // Banner only applies when we have a new source to prepend to
595 if (output.source && /** @type {ExtractCommentsObject} */
596 this.options.extractComments.banner !== false) {
597 let banner = /** @type {ExtractCommentsObject} */
598 this.options.extractComments.banner || `For license information please see ${path.relative(path.dirname(name), output.commentsFilename).replace(/\\/g, "/")}`;
599 if (typeof banner === "function") {
600 banner = banner(output.commentsFilename);
601 }
602 if (banner) {
603 output.source = new ConcatSource(shebang ? `${shebang}\n` : "", `/*! ${banner} */\n`, output.source);
604 }
605 }
606 const extractedCommentsString = output.extractedComments.sort().join("\n\n");
607 output.extractedCommentsSource = new RawSource(`${extractedCommentsString}\n`);
608 }
609 await cacheItem.storePromise({
610 source: output.source,
611 errors: output.errors,
612 warnings: output.warnings,
613 commentsFilename: output.commentsFilename,
614 extractedCommentsSource: output.extractedCommentsSource
615 });
616 }
617 if (output.warnings && output.warnings.length > 0) {
618 for (const warning of output.warnings) {
619 compilation.warnings.push(warning);
620 }
621 }
622 if (output.errors && output.errors.length > 0) {
623 for (const error of output.errors) {
624 compilation.errors.push(error);
625 }
626 }
627
628 // Emit extracted comments file even if the main asset was not
629 // rewritten (some minimizers only produce comments / warnings / errors)
630 if (output.extractedCommentsSource) {
631 allExtractedComments.set(name, {
632 extractedCommentsSource: output.extractedCommentsSource,
633 commentsFilename: (/** @type {string} */output.commentsFilename)
634 });
635 }
636 if (!output.source) {
637 return;
638 }
639
640 /** @type {AssetInfo} */
641 const newInfo = {
642 minimized: true
643 };
644 if (output.extractedCommentsSource) {
645 newInfo.related = {
646 license: (/** @type {string} */output.commentsFilename)
647 };
648 }
649 compilation.updateAsset(name, output.source, newInfo);
650 });
651 }
652 const limit = getWorker && numberOfAssets > 0 ? (/** @type {number} */numberOfWorkers) : scheduledTasks.length;
653 await throttleAll(limit, scheduledTasks);
654 if (initializedWorker) {
655 await initializedWorker.end();
656 }
657
658 /** @typedef {{ source: import("webpack").sources.Source, commentsFilename: string, from: string }} ExtractedCommentsInfoWithFrom */
659 await [...allExtractedComments].sort().reduce(
660 /**
661 * @param {Promise<unknown>} previousPromise previous result
662 * @param {[string, ExtractedCommentsInfo]} extractedComments extracted comments
663 * @returns {Promise<ExtractedCommentsInfoWithFrom>} extract comments with info
664 */
665 async (previousPromise, [from, value]) => {
666 const previous = /** @type {ExtractedCommentsInfoWithFrom | undefined} * */
667 await previousPromise;
668 const {
669 commentsFilename,
670 extractedCommentsSource
671 } = value;
672 if (previous && previous.commentsFilename === commentsFilename) {
673 const {
674 from: previousFrom,
675 source: prevSource
676 } = previous;
677 const mergedName = `${previousFrom}|${from}`;
678 const name = `${commentsFilename}|${mergedName}`;
679 const eTag = [prevSource, extractedCommentsSource].map(item => cache.getLazyHashedEtag(item)).reduce((previousValue, currentValue) => cache.mergeEtags(previousValue, currentValue));
680 let source = await cache.getPromise(name, eTag);
681 if (!source) {
682 source = new ConcatSource([...new Set([... /** @type {string} */prevSource.source().split("\n\n"), ... /** @type {string} */extractedCommentsSource.source().split("\n\n")])].join("\n\n"));
683 await cache.storePromise(name, eTag, source);
684 }
685 compilation.updateAsset(commentsFilename, source);
686 return {
687 source,
688 commentsFilename,
689 from: mergedName
690 };
691 }
692 const existingAsset = compilation.getAsset(commentsFilename);
693 if (existingAsset) {
694 return {
695 source: existingAsset.source,
696 commentsFilename,
697 from: commentsFilename
698 };
699 }
700 compilation.emitAsset(commentsFilename, extractedCommentsSource, {
701 extractedComments: true
702 });
703 return {
704 source: extractedCommentsSource,
705 commentsFilename,
706 from
707 };
708 }, /** @type {Promise<unknown>} */Promise.resolve());
709 }
710
711 /**
712 * @param {Compiler} compiler compiler
713 * @returns {void}
714 */
715 apply(compiler) {
716 const pluginName = this.constructor.name;
717 const availableNumberOfCores = TerserPlugin.getAvailableNumberOfCores(this.options.parallel);
718 compiler.hooks.compilation.tap(pluginName, compilation => {
719 const hooks = compiler.webpack.javascript.JavascriptModulesPlugin.getCompilationHooks(compilation);
720 /**
721 * @param {BasicMinimizerImplementation<EXPECTED_ANY> & MinimizeFunctionHelpers} impl implementation
722 * @returns {string} minimizer version or "0.0.0"
723 */
724 const getVersion = impl => typeof impl.getMinimizerVersion !== "undefined" ? impl.getMinimizerVersion() || "0.0.0" : "0.0.0";
725 const data = getSerializeJavascript()({
726 minimizer: Array.isArray(this.options.minimizer.implementation) ? this.options.minimizer.implementation.map(getVersion) : getVersion(/** @type {BasicMinimizerImplementation<EXPECTED_ANY> & MinimizeFunctionHelpers} */
727 this.options.minimizer.implementation),
728 options: this.options.minimizer.options
729 });
730 hooks.chunkHash.tap(pluginName, (chunk, hash) => {
731 hash.update("TerserPlugin");
732 hash.update(data);
733 });
734 compilation.hooks.processAssets.tapPromise({
735 name: pluginName,
736 stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE,
737 additionalAssets: true
738 }, assets => this.optimize(compiler, compilation, assets, {
739 availableNumberOfCores
740 }));
741 compilation.hooks.statsPrinter.tap(pluginName, stats => {
742 stats.hooks.print.for("asset.info.minimized").tap("minimizer-webpack-plugin", (minimized, {
743 green,
744 formatFlag
745 }) => minimized ? /** @type {(text: string) => string} */green(/** @type {(flag: string) => string} */formatFlag("minimized")) : "");
746 });
747 });
748 }
749}
750TerserPlugin.terserMinify = terserMinify;
751TerserPlugin.uglifyJsMinify = uglifyJsMinify;
752TerserPlugin.swcMinify = swcMinify;
753TerserPlugin.esbuildMinify = esbuildMinify;
754TerserPlugin.jsonMinify = jsonMinify;
755TerserPlugin.htmlMinifierTerser = htmlMinifierTerser;
756TerserPlugin.swcMinifyHtml = swcMinifyHtml;
757TerserPlugin.swcMinifyHtmlFragment = swcMinifyHtmlFragment;
758TerserPlugin.minifyHtmlNode = minifyHtmlNode;
759TerserPlugin.cssnanoMinify = cssnanoMinify;
760TerserPlugin.cssoMinify = cssoMinify;
761TerserPlugin.cleanCssMinify = cleanCssMinify;
762TerserPlugin.esbuildMinifyCss = esbuildMinifyCss;
763TerserPlugin.lightningCssMinify = lightningCssMinify;
764TerserPlugin.swcMinifyCss = swcMinifyCss;
765module.exports = TerserPlugin;
Note: See TracBrowser for help on using the repository browser.