1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.default = void 0;
|
7 |
|
8 | var path = _interopRequireWildcard(require("path"));
|
9 |
|
10 | var os = _interopRequireWildcard(require("os"));
|
11 |
|
12 | var _sourceMap = require("source-map");
|
13 |
|
14 | var _schemaUtils = require("schema-utils");
|
15 |
|
16 | var _serializeJavascript = _interopRequireDefault(require("serialize-javascript"));
|
17 |
|
18 | var terserPackageJson = _interopRequireWildcard(require("terser/package.json"));
|
19 |
|
20 | var _pLimit = _interopRequireDefault(require("p-limit"));
|
21 |
|
22 | var _jestWorker = _interopRequireDefault(require("jest-worker"));
|
23 |
|
24 | var schema = _interopRequireWildcard(require("./options.json"));
|
25 |
|
26 | var _minify = require("./minify");
|
27 |
|
28 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
29 |
|
30 | function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
|
31 |
|
32 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (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; }
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 | class TerserPlugin {
|
114 | |
115 |
|
116 |
|
117 | constructor(options = {}) {
|
118 | (0, _schemaUtils.validate)(
|
119 |
|
120 | schema, options, {
|
121 | name: 'Terser Plugin',
|
122 | baseDataPath: 'options'
|
123 | });
|
124 | const {
|
125 | minify,
|
126 | terserOptions = {},
|
127 | test = /\.[cm]?js(\?.*)?$/i,
|
128 | extractComments = true,
|
129 | parallel = true,
|
130 | include,
|
131 | exclude
|
132 | } = options;
|
133 | this.options = {
|
134 | test,
|
135 | extractComments,
|
136 | parallel,
|
137 | include,
|
138 | exclude,
|
139 | minify,
|
140 | terserOptions
|
141 | };
|
142 | }
|
143 | |
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 | static isSourceMap(input) {
|
151 |
|
152 |
|
153 | return Boolean(input && input.version && input.sources && Array.isArray(input.sources) && typeof input.mappings === 'string');
|
154 | }
|
155 | |
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 | static buildError(error, file, requestShortener, sourceMap) {
|
166 | if (error.line) {
|
167 | const original = sourceMap && sourceMap.originalPositionFor({
|
168 | line: error.line,
|
169 | column: error.col
|
170 | });
|
171 |
|
172 | if (original && original.source && requestShortener) {
|
173 | return new Error(`${file} from Terser\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')}` : ''}`);
|
174 | }
|
175 |
|
176 | return new Error(`${file} from Terser\n${error.message} [${file}:${error.line},${error.col}]${error.stack ? `\n${error.stack.split('\n').slice(1).join('\n')}` : ''}`);
|
177 | }
|
178 |
|
179 | if (error.stack) {
|
180 | return new Error(`${file} from Terser\n${error.stack}`);
|
181 | }
|
182 |
|
183 | return new Error(`${file} from Terser\n${error.message}`);
|
184 | }
|
185 | |
186 |
|
187 |
|
188 |
|
189 |
|
190 |
|
191 |
|
192 | static getAvailableNumberOfCores(parallel) {
|
193 |
|
194 |
|
195 | const cpus = os.cpus() || {
|
196 | length: 1
|
197 | };
|
198 | return parallel === true ? cpus.length - 1 : Math.min(Number(parallel) || 0, cpus.length - 1);
|
199 | }
|
200 | |
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 | async optimize(compiler, compilation, assets, optimizeOptions) {
|
210 | const cache = compilation.getCache('TerserWebpackPlugin');
|
211 | let numberOfAssetsForMinify = 0;
|
212 | const assetsForMinify = await Promise.all(Object.keys(assets).filter(name => {
|
213 | const {
|
214 | info
|
215 | } = compilation.getAsset(name);
|
216 |
|
217 | if (info.minimized) {
|
218 | return false;
|
219 | }
|
220 |
|
221 | if (!compiler.webpack.ModuleFilenameHelpers.matchObject.bind(
|
222 | undefined, this.options)(name)) {
|
223 | return false;
|
224 | }
|
225 |
|
226 | return true;
|
227 | }).map(async name => {
|
228 | const {
|
229 | info,
|
230 | source
|
231 | } = compilation.getAsset(name);
|
232 | const eTag = cache.getLazyHashedEtag(source);
|
233 | const cacheItem = cache.getItemCache(name, eTag);
|
234 | const output = await cacheItem.getPromise();
|
235 |
|
236 | if (!output) {
|
237 | numberOfAssetsForMinify += 1;
|
238 | }
|
239 |
|
240 | return {
|
241 | name,
|
242 | info,
|
243 | inputSource: source,
|
244 | output,
|
245 | cacheItem
|
246 | };
|
247 | }));
|
248 |
|
249 |
|
250 | let getWorker;
|
251 |
|
252 |
|
253 | let initializedWorker;
|
254 |
|
255 |
|
256 | let numberOfWorkers;
|
257 |
|
258 | if (optimizeOptions.availableNumberOfCores > 0) {
|
259 |
|
260 | numberOfWorkers = Math.min(numberOfAssetsForMinify, optimizeOptions.availableNumberOfCores);
|
261 |
|
262 | getWorker = () => {
|
263 | if (initializedWorker) {
|
264 | return initializedWorker;
|
265 | }
|
266 |
|
267 | initializedWorker =
|
268 |
|
269 | new _jestWorker.default(require.resolve('./minify'), {
|
270 | numWorkers: numberOfWorkers,
|
271 | enableWorkerThreads: true
|
272 | });
|
273 |
|
274 | const workerStdout = initializedWorker.getStdout();
|
275 |
|
276 | if (workerStdout) {
|
277 | workerStdout.on('data', chunk => {
|
278 | return process.stdout.write(chunk);
|
279 | });
|
280 | }
|
281 |
|
282 | const workerStderr = initializedWorker.getStderr();
|
283 |
|
284 | if (workerStderr) {
|
285 | workerStderr.on('data', chunk => {
|
286 | return process.stderr.write(chunk);
|
287 | });
|
288 | }
|
289 |
|
290 | return initializedWorker;
|
291 | };
|
292 | }
|
293 |
|
294 | const limit = (0, _pLimit.default)(getWorker && numberOfAssetsForMinify > 0 ?
|
295 |
|
296 | numberOfWorkers : Infinity);
|
297 | const {
|
298 | SourceMapSource,
|
299 | ConcatSource,
|
300 | RawSource
|
301 | } = compiler.webpack.sources;
|
302 | const allExtractedComments = new Map();
|
303 | const scheduledTasks = [];
|
304 |
|
305 | for (const asset of assetsForMinify) {
|
306 | scheduledTasks.push(limit(async () => {
|
307 | const {
|
308 | name,
|
309 | inputSource,
|
310 | info,
|
311 | cacheItem
|
312 | } = asset;
|
313 | let {
|
314 | output
|
315 | } = asset;
|
316 |
|
317 | if (!output) {
|
318 | let input;
|
319 | let inputSourceMap;
|
320 | const {
|
321 | source: sourceFromInputSource,
|
322 | map
|
323 | } = inputSource.sourceAndMap();
|
324 | input = sourceFromInputSource;
|
325 |
|
326 | if (map) {
|
327 | if (TerserPlugin.isSourceMap(map)) {
|
328 | inputSourceMap = map;
|
329 | } else {
|
330 | inputSourceMap = map;
|
331 | compilation.warnings.push(
|
332 |
|
333 | new Error(`${name} contains invalid source map`));
|
334 | }
|
335 | }
|
336 |
|
337 | if (Buffer.isBuffer(input)) {
|
338 | input = input.toString();
|
339 | }
|
340 |
|
341 | const options = {
|
342 | name,
|
343 | input,
|
344 | inputSourceMap,
|
345 | minify: this.options.minify,
|
346 | minifyOptions: { ...this.options.terserOptions
|
347 | },
|
348 | extractComments: this.options.extractComments
|
349 | };
|
350 |
|
351 | if (typeof options.minifyOptions.module === 'undefined') {
|
352 | if (typeof info.javascriptModule !== 'undefined') {
|
353 | options.minifyOptions.module = info.javascriptModule;
|
354 | } else if (/\.mjs(\?.*)?$/i.test(name)) {
|
355 | options.minifyOptions.module = true;
|
356 | } else if (/\.cjs(\?.*)?$/i.test(name)) {
|
357 | options.minifyOptions.module = false;
|
358 | }
|
359 | }
|
360 |
|
361 | try {
|
362 | output = await (getWorker ? getWorker().transform((0, _serializeJavascript.default)(options)) : (0, _minify.minify)(options));
|
363 | } catch (error) {
|
364 | const hasSourceMap = inputSourceMap && TerserPlugin.isSourceMap(inputSourceMap);
|
365 | compilation.errors.push(TerserPlugin.buildError(error, name,
|
366 | hasSourceMap ? compilation.requestShortener : undefined, hasSourceMap ? new _sourceMap.SourceMapConsumer(
|
367 |
|
368 | inputSourceMap) :
|
369 | undefined));
|
370 | return;
|
371 | }
|
372 |
|
373 | let shebang;
|
374 |
|
375 | if (
|
376 |
|
377 | this.options.extractComments.banner !== false && output.extractedComments && output.extractedComments.length > 0 && output.code.startsWith('#!')) {
|
378 | const firstNewlinePosition = output.code.indexOf('\n');
|
379 | shebang = output.code.substring(0, firstNewlinePosition);
|
380 | output.code = output.code.substring(firstNewlinePosition + 1);
|
381 | }
|
382 |
|
383 | if (output.map) {
|
384 | output.source = new SourceMapSource(output.code, name, output.map, input,
|
385 |
|
386 | inputSourceMap, true);
|
387 | } else {
|
388 | output.source = new RawSource(output.code);
|
389 | }
|
390 |
|
391 | if (output.extractedComments && output.extractedComments.length > 0) {
|
392 | const commentsFilename =
|
393 |
|
394 | this.options.extractComments.filename || '[file].LICENSE.txt[query]';
|
395 | let query = '';
|
396 | let filename = name;
|
397 | const querySplit = filename.indexOf('?');
|
398 |
|
399 | if (querySplit >= 0) {
|
400 | query = filename.substr(querySplit);
|
401 | filename = filename.substr(0, querySplit);
|
402 | }
|
403 |
|
404 | const lastSlashIndex = filename.lastIndexOf('/');
|
405 | const basename = lastSlashIndex === -1 ? filename : filename.substr(lastSlashIndex + 1);
|
406 | const data = {
|
407 | filename,
|
408 | basename,
|
409 | query
|
410 | };
|
411 | output.commentsFilename = compilation.getPath(commentsFilename, data);
|
412 | let banner;
|
413 |
|
414 | if (
|
415 |
|
416 | this.options.extractComments.banner !== false) {
|
417 | banner =
|
418 |
|
419 | this.options.extractComments.banner || `For license information please see ${path.relative(path.dirname(name), output.commentsFilename).replace(/\\/g, '/')}`;
|
420 |
|
421 | if (typeof banner === 'function') {
|
422 | banner = banner(output.commentsFilename);
|
423 | }
|
424 |
|
425 | if (banner) {
|
426 | output.source = new ConcatSource(shebang ? `${shebang}\n` : '', `/*! ${banner} */\n`, output.source);
|
427 | }
|
428 | }
|
429 |
|
430 | const extractedCommentsString = output.extractedComments.sort().join('\n\n');
|
431 | output.extractedCommentsSource = new RawSource(`${extractedCommentsString}\n`);
|
432 | }
|
433 |
|
434 | await cacheItem.storePromise({
|
435 | source: output.source,
|
436 | commentsFilename: output.commentsFilename,
|
437 | extractedCommentsSource: output.extractedCommentsSource
|
438 | });
|
439 | }
|
440 |
|
441 |
|
442 |
|
443 | const newInfo = {
|
444 | minimized: true
|
445 | };
|
446 | const {
|
447 | source,
|
448 | extractedCommentsSource
|
449 | } = output;
|
450 |
|
451 | if (extractedCommentsSource) {
|
452 | const {
|
453 | commentsFilename
|
454 | } = output;
|
455 | newInfo.related = {
|
456 | license: commentsFilename
|
457 | };
|
458 | allExtractedComments.set(name, {
|
459 | extractedCommentsSource,
|
460 | commentsFilename
|
461 | });
|
462 | }
|
463 |
|
464 | compilation.updateAsset(name, source, newInfo);
|
465 | }));
|
466 | }
|
467 |
|
468 | await Promise.all(scheduledTasks);
|
469 |
|
470 | if (initializedWorker) {
|
471 | await initializedWorker.end();
|
472 | }
|
473 |
|
474 | await Array.from(allExtractedComments).sort().reduce(
|
475 | |
476 |
|
477 |
|
478 |
|
479 |
|
480 | async (previousPromise, [from, value]) => {
|
481 | const previous = await previousPromise;
|
482 | const {
|
483 | commentsFilename,
|
484 | extractedCommentsSource
|
485 | } = value;
|
486 |
|
487 | if (previous && previous.commentsFilename === commentsFilename) {
|
488 | const {
|
489 | from: previousFrom,
|
490 | source: prevSource
|
491 | } = previous;
|
492 | const mergedName = `${previousFrom}|${from}`;
|
493 | const name = `${commentsFilename}|${mergedName}`;
|
494 | const eTag = [prevSource, extractedCommentsSource].map(item => cache.getLazyHashedEtag(item)).reduce((previousValue, currentValue) => cache.mergeEtags(previousValue, currentValue));
|
495 | let source = await cache.getPromise(name, eTag);
|
496 |
|
497 | if (!source) {
|
498 | source = new ConcatSource(Array.from(new Set([...prevSource.source().split('\n\n'), ...extractedCommentsSource.source().split('\n\n')])).join('\n\n'));
|
499 | await cache.storePromise(name, eTag, source);
|
500 | }
|
501 |
|
502 | compilation.updateAsset(commentsFilename, source);
|
503 | return {
|
504 | commentsFilename,
|
505 | from: mergedName,
|
506 | source
|
507 | };
|
508 | }
|
509 |
|
510 | const existingAsset = compilation.getAsset(commentsFilename);
|
511 |
|
512 | if (existingAsset) {
|
513 | return {
|
514 | commentsFilename,
|
515 | from: commentsFilename,
|
516 | source: existingAsset.source
|
517 | };
|
518 | }
|
519 |
|
520 | compilation.emitAsset(commentsFilename, extractedCommentsSource);
|
521 | return {
|
522 | commentsFilename,
|
523 | from,
|
524 | source: extractedCommentsSource
|
525 | };
|
526 | }, Promise.resolve());
|
527 | }
|
528 | |
529 |
|
530 |
|
531 |
|
532 |
|
533 |
|
534 |
|
535 | static getEcmaVersion(environment) {
|
536 |
|
537 | if (environment.arrowFunction || environment.const || environment.destructuring || environment.forOf || environment.module) {
|
538 | return 2015;
|
539 | }
|
540 |
|
541 |
|
542 | if (environment.bigIntLiteral || environment.dynamicImport) {
|
543 | return 2020;
|
544 | }
|
545 |
|
546 | return 5;
|
547 | }
|
548 | |
549 |
|
550 |
|
551 |
|
552 |
|
553 |
|
554 | apply(compiler) {
|
555 | const {
|
556 | output
|
557 | } = compiler.options;
|
558 |
|
559 | if (typeof this.options.terserOptions.ecma === 'undefined') {
|
560 | this.options.terserOptions.ecma = TerserPlugin.getEcmaVersion(output.environment || {});
|
561 | }
|
562 |
|
563 | const pluginName = this.constructor.name;
|
564 | const availableNumberOfCores = TerserPlugin.getAvailableNumberOfCores(this.options.parallel);
|
565 | compiler.hooks.compilation.tap(pluginName, compilation => {
|
566 | const hooks = compiler.webpack.javascript.JavascriptModulesPlugin.getCompilationHooks(compilation);
|
567 | const data = (0, _serializeJavascript.default)({
|
568 | terser: terserPackageJson.version,
|
569 | terserOptions: this.options.terserOptions
|
570 | });
|
571 | hooks.chunkHash.tap(pluginName, (chunk, hash) => {
|
572 | hash.update('TerserPlugin');
|
573 | hash.update(data);
|
574 | });
|
575 | compilation.hooks.processAssets.tapPromise({
|
576 | name: pluginName,
|
577 | stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE
|
578 | }, assets => this.optimize(compiler, compilation, assets, {
|
579 | availableNumberOfCores
|
580 | }));
|
581 | compilation.hooks.statsPrinter.tap(pluginName, stats => {
|
582 | stats.hooks.print.for('asset.info.minimized').tap('terser-webpack-plugin', (minimized, {
|
583 | green,
|
584 | formatFlag
|
585 | }) =>
|
586 | minimized ? green(formatFlag('minimized')) : undefined);
|
587 | });
|
588 | });
|
589 | }
|
590 |
|
591 | }
|
592 |
|
593 | var _default = TerserPlugin;
|
594 | exports.default = _default; |
\ | No newline at end of file |