1 | "use strict";
|
---|
2 | /**
|
---|
3 | * @license
|
---|
4 | * Copyright Google LLC All Rights Reserved.
|
---|
5 | *
|
---|
6 | * Use of this source code is governed by an MIT-style license that can be
|
---|
7 | * found in the LICENSE file at https://angular.io/license
|
---|
8 | */
|
---|
9 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
---|
10 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
---|
11 | };
|
---|
12 | Object.defineProperty(exports, "__esModule", { value: true });
|
---|
13 | const remapping_1 = __importDefault(require("@ampproject/remapping"));
|
---|
14 | const terser_1 = require("terser");
|
---|
15 | const esbuild_executor_1 = require("./esbuild-executor");
|
---|
16 | /**
|
---|
17 | * The cached esbuild executor.
|
---|
18 | * This will automatically use the native or WASM version based on platform and availability
|
---|
19 | * with the native version given priority due to its superior performance.
|
---|
20 | */
|
---|
21 | let esbuild;
|
---|
22 | /**
|
---|
23 | * Handles optimization requests sent from the main thread via the `JavaScriptOptimizerPlugin`.
|
---|
24 | */
|
---|
25 | async function default_1({ asset, options }) {
|
---|
26 | // esbuild is used as a first pass
|
---|
27 | const esbuildResult = await optimizeWithEsbuild(asset.code, asset.name, options);
|
---|
28 | // terser is used as a second pass
|
---|
29 | const terserResult = await optimizeWithTerser(asset.name, esbuildResult.code, options.sourcemap, options.target, options.advanced);
|
---|
30 | // Merge intermediate sourcemaps with input sourcemap if enabled
|
---|
31 | let fullSourcemap;
|
---|
32 | if (options.sourcemap) {
|
---|
33 | const partialSourcemaps = [];
|
---|
34 | if (esbuildResult.map) {
|
---|
35 | partialSourcemaps.unshift(JSON.parse(esbuildResult.map));
|
---|
36 | }
|
---|
37 | if (terserResult.map) {
|
---|
38 | partialSourcemaps.unshift(terserResult.map);
|
---|
39 | }
|
---|
40 | if (asset.map) {
|
---|
41 | partialSourcemaps.push(asset.map);
|
---|
42 | }
|
---|
43 | fullSourcemap = remapping_1.default(partialSourcemaps, () => null);
|
---|
44 | }
|
---|
45 | return { name: asset.name, code: terserResult.code, map: fullSourcemap };
|
---|
46 | }
|
---|
47 | exports.default = default_1;
|
---|
48 | /**
|
---|
49 | * Optimizes a JavaScript asset using esbuild.
|
---|
50 | *
|
---|
51 | * @param content The JavaScript asset source content to optimize.
|
---|
52 | * @param name The name of the JavaScript asset. Used to generate source maps.
|
---|
53 | * @param options The optimization request options to apply to the content.
|
---|
54 | * @returns A promise that resolves with the optimized code, source map, and any warnings.
|
---|
55 | */
|
---|
56 | async function optimizeWithEsbuild(content, name, options) {
|
---|
57 | var _a;
|
---|
58 | if (!esbuild) {
|
---|
59 | esbuild = new esbuild_executor_1.EsbuildExecutor(options.alwaysUseWasm);
|
---|
60 | }
|
---|
61 | let result;
|
---|
62 | try {
|
---|
63 | result = await esbuild.transform(content, {
|
---|
64 | minifyIdentifiers: !options.keepNames,
|
---|
65 | minifySyntax: true,
|
---|
66 | // NOTE: Disabling whitespace ensures unused pure annotations are kept
|
---|
67 | minifyWhitespace: false,
|
---|
68 | pure: ['forwardRef'],
|
---|
69 | legalComments: options.removeLicenses ? 'none' : 'inline',
|
---|
70 | sourcefile: name,
|
---|
71 | sourcemap: options.sourcemap && 'external',
|
---|
72 | define: options.define,
|
---|
73 | keepNames: options.keepNames,
|
---|
74 | target: `es${options.target}`,
|
---|
75 | });
|
---|
76 | }
|
---|
77 | catch (error) {
|
---|
78 | const failure = error;
|
---|
79 | // If esbuild fails with only ES5 support errors, fallback to just terser.
|
---|
80 | // This will only happen if ES5 is the output target and a global script contains ES2015+ syntax.
|
---|
81 | // In that case, the global script is technically already invalid for the target environment but
|
---|
82 | // this is and has been considered a configuration issue. Global scripts must be compatible with
|
---|
83 | // the target environment.
|
---|
84 | if ((_a = failure.errors) === null || _a === void 0 ? void 0 : _a.every((error) => error.text.includes('to the configured target environment ("es5") is not supported yet'))) {
|
---|
85 | result = {
|
---|
86 | code: content,
|
---|
87 | map: '',
|
---|
88 | warnings: [],
|
---|
89 | };
|
---|
90 | }
|
---|
91 | else {
|
---|
92 | throw error;
|
---|
93 | }
|
---|
94 | }
|
---|
95 | return result;
|
---|
96 | }
|
---|
97 | /**
|
---|
98 | * Optimizes a JavaScript asset using terser.
|
---|
99 | *
|
---|
100 | * @param name The name of the JavaScript asset. Used to generate source maps.
|
---|
101 | * @param code The JavaScript asset source content to optimize.
|
---|
102 | * @param sourcemaps If true, generate an output source map for the optimized code.
|
---|
103 | * @param target Specifies the target ECMAScript version for the output code.
|
---|
104 | * @param advanced Controls advanced optimizations.
|
---|
105 | * @returns A promise that resolves with the optimized code and source map.
|
---|
106 | */
|
---|
107 | async function optimizeWithTerser(name, code, sourcemaps, target, advanced) {
|
---|
108 | const result = await terser_1.minify({ [name]: code }, {
|
---|
109 | compress: {
|
---|
110 | passes: advanced ? 2 : 1,
|
---|
111 | pure_getters: advanced,
|
---|
112 | },
|
---|
113 | ecma: target,
|
---|
114 | // esbuild in the first pass is used to minify identifiers instead of mangle here
|
---|
115 | mangle: false,
|
---|
116 | format: {
|
---|
117 | // ASCII output is enabled here as well to prevent terser from converting back to UTF-8
|
---|
118 | ascii_only: true,
|
---|
119 | wrap_func_args: false,
|
---|
120 | },
|
---|
121 | sourceMap: sourcemaps &&
|
---|
122 | {
|
---|
123 | asObject: true,
|
---|
124 | // typings don't include asObject option
|
---|
125 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
---|
126 | },
|
---|
127 | });
|
---|
128 | if (!result.code) {
|
---|
129 | throw new Error('Terser failed for unknown reason.');
|
---|
130 | }
|
---|
131 | return { code: result.code, map: result.map };
|
---|
132 | }
|
---|