source: frontend/node_modules/terser-webpack-plugin/dist/minify.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: 11.1 KB
Line 
1"use strict";
2
3/** @typedef {import("./index.js").MinimizedResult} MinimizedResult */
4/** @typedef {import("./index.js").CustomOptions} CustomOptions */
5/** @typedef {import("./index.js").RawSourceMap} RawSourceMap */
6/**
7 * @template T
8 * @typedef {import("./index.js").MinimizerOptions<T>} MinimizerOptions
9 */
10
11const VLQ_BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
12
13/**
14 * Encode a single integer as Base64 VLQ as used by the source-map spec.
15 * @param {number} value integer to encode
16 * @returns {string} encoded VLQ characters
17 */
18/* eslint-disable prefer-destructuring, no-eq-null, eqeqeq */
19/**
20 * @param {number} value integer to encode
21 * @returns {string} encoded VLQ characters
22 */
23function encodeVlq(value) {
24 let vlq = value < 0 ? -value << 1 | 1 : value << 1;
25 let out = "";
26 do {
27 let digit = vlq & 0b11111;
28 vlq >>>= 5;
29 if (vlq > 0) {
30 digit |= 0b100000;
31 }
32 out += VLQ_BASE64[digit];
33 } while (vlq > 0);
34 return out;
35}
36
37/**
38 * Encode decoded source-map mappings (per-line arrays of segments) back into
39 * the spec's `mappings` string.
40 * @param {number[][][]} decoded mappings as nested arrays of segments
41 * @returns {string} encoded `mappings` field
42 */
43function encodeMappings(decoded) {
44 let result = "";
45 let prevSourceIdx = 0;
46 let prevOriginalLine = 0;
47 let prevOriginalColumn = 0;
48 let prevNameIdx = 0;
49 for (let line = 0; line < decoded.length; line++) {
50 if (line > 0) {
51 result += ";";
52 }
53 let prevGeneratedColumn = 0;
54 const segments = decoded[line];
55 for (let i = 0; i < segments.length; i++) {
56 if (i > 0) {
57 result += ",";
58 }
59 const seg = segments[i];
60 result += encodeVlq(seg[0] - prevGeneratedColumn);
61 prevGeneratedColumn = seg[0];
62 if (seg.length >= 4) {
63 result += encodeVlq(seg[1] - prevSourceIdx);
64 prevSourceIdx = seg[1];
65 result += encodeVlq(seg[2] - prevOriginalLine);
66 prevOriginalLine = seg[2];
67 result += encodeVlq(seg[3] - prevOriginalColumn);
68 prevOriginalColumn = seg[3];
69 if (seg.length >= 5) {
70 result += encodeVlq(seg[4] - prevNameIdx);
71 prevNameIdx = seg[4];
72 }
73 }
74 }
75 }
76 return result;
77}
78
79/**
80 * Compose a freshly-produced source map with the input source map fed to
81 * the minimizer. `currentMap` represents `name → step-output` and
82 * `prevMap` represents `original → name`; the result represents
83 * `original → step-output`.
84 *
85 * TODO: replace with a webpack-sources helper once one is exposed —
86 * `SourceMapSource` already composes one level via `innerSourceMap`,
87 * see https://github.com/webpack/webpack-sources for the proposal to
88 * expose it as a public `composeSourceMaps` (or n-step `SourceMapSource`).
89 * @param {RawSourceMap | undefined} currentMap map produced by the minimizer
90 * @param {RawSourceMap | undefined} prevMap input source map fed to the minimizer
91 * @param {string} name name of the asset that the current map points to
92 * @returns {RawSourceMap | undefined} composed map
93 */
94function composeSourceMaps(currentMap, prevMap, name) {
95 if (!currentMap || !prevMap) {
96 return currentMap;
97 }
98
99 // Custom minimizers may return the map as a JSON string (e.g. terser's
100 // default output). `TraceMap` accepts both shapes, but we still hand
101 // back the original `currentMap` (string preserved) when the previous
102 // map can't be combined.
103 const {
104 TraceMap,
105 decodedMappings,
106 originalPositionFor,
107 sourceContentFor
108 } = require("@jridgewell/trace-mapping");
109 const current = new TraceMap(/** @type {import("@jridgewell/trace-mapping").SourceMapInput} */
110 /** @type {unknown} */currentMap);
111 const previous = new TraceMap(/** @type {import("@jridgewell/trace-mapping").SourceMapInput} */
112 /** @type {unknown} */prevMap);
113
114 /** @type {string[]} */
115 const sources = [];
116 /** @type {(string | null)[]} */
117 const sourcesContent = [];
118 /** @type {string[]} */
119 const names = [];
120 /** @type {Map<string, number>} */
121 const sourceIdx = new Map();
122 /** @type {Map<string, number>} */
123 const nameIdx = new Map();
124
125 /**
126 * @param {string | null | undefined} source source identifier
127 * @param {string | undefined} content source content (when available)
128 * @returns {number} index assigned in the composed map
129 */
130 const getSourceIdx = (source, content) => {
131 const key = source || "";
132 let idx = sourceIdx.get(key);
133 if (typeof idx === "undefined") {
134 idx = sources.length;
135 sources.push(key);
136 sourcesContent.push(typeof content === "string" ? content : null);
137 sourceIdx.set(key, idx);
138 } else if (typeof content === "string" && sourcesContent[idx] === null) {
139 sourcesContent[idx] = content;
140 }
141 return idx;
142 };
143
144 /**
145 * @param {string | null | undefined} value name
146 * @returns {number} index assigned in the composed map
147 */
148 const getNameIdx = value => {
149 if (typeof value !== "string") {
150 return -1;
151 }
152 let idx = nameIdx.get(value);
153 if (typeof idx === "undefined") {
154 idx = names.length;
155 names.push(value);
156 nameIdx.set(value, idx);
157 }
158 return idx;
159 };
160 const decoded = decodedMappings(current);
161 const currentSources = current.sources.map(
162 /**
163 * @param {string | null} source source from current map
164 * @returns {string} normalized source string
165 */
166 source => source || "");
167 const currentNames = current.names;
168
169 /** @type {number[][][]} */
170 const composed = [];
171 for (let line = 0; line < decoded.length; line++) {
172 /** @type {number[][]} */
173 const newSegments = [];
174 for (const rawSeg of decoded[line]) {
175 const seg = /** @type {number[]} */rawSeg;
176
177 // Single-element segment is just a generated column with no source info
178 if (seg.length < 4) {
179 newSegments.push([seg[0]]);
180 continue;
181 }
182 const sourceName = currentSources[seg[1]];
183 const origLine = /** @type {number} */seg[2];
184 const origCol = /** @type {number} */seg[3];
185 const segName = seg.length >= 5 ? currentNames[seg[4]] : (/** @type {string | null} */null);
186
187 // When the segment points back at our intermediate `name`, look up
188 // the original position in the previous map and emit a mapping that
189 // points all the way back. Otherwise keep the segment as-is.
190 if (sourceName === name) {
191 const orig = originalPositionFor(previous, {
192 line: origLine + 1,
193 column: origCol
194 });
195 if (typeof orig.source !== "string" || orig.line == null || orig.column == null) {
196 continue;
197 }
198 const content = sourceContentFor(previous, orig.source) || undefined;
199 const newSrcIdx = getSourceIdx(orig.source, content);
200 const finalName = typeof orig.name === "string" && orig.name ? orig.name : segName;
201 if (typeof finalName === "string") {
202 newSegments.push([seg[0], newSrcIdx, orig.line - 1, orig.column, getNameIdx(finalName)]);
203 } else {
204 newSegments.push([seg[0], newSrcIdx, orig.line - 1, orig.column]);
205 }
206 } else {
207 const content = sourceContentFor(current, sourceName) || undefined;
208 const newSrcIdx = getSourceIdx(sourceName, content);
209 if (typeof segName === "string") {
210 newSegments.push([seg[0], newSrcIdx, origLine, origCol, getNameIdx(segName)]);
211 } else {
212 newSegments.push([seg[0], newSrcIdx, origLine, origCol]);
213 }
214 }
215 }
216 composed.push(newSegments);
217 }
218 const result = /** @type {RawSourceMap} */
219
220 /** @type {unknown} */{
221 version: 3,
222 sources,
223 names,
224 mappings: encodeMappings(composed)
225 };
226 if (currentMap.file) {
227 result.file = currentMap.file;
228 }
229 if (sourcesContent.some(value => typeof value === "string")) {
230 result.sourcesContent = /** @type {string[]} */
231 /** @type {unknown} */sourcesContent;
232 }
233 return result;
234}
235/* eslint-enable prefer-destructuring, no-eq-null, eqeqeq */
236
237/**
238 * @template T
239 * @param {import("./index.js").InternalOptions<T>} options options
240 * @returns {Promise<MinimizedResult>} minified result
241 */
242async function minify(options) {
243 const {
244 name,
245 input,
246 inputSourceMap,
247 extractComments,
248 module,
249 ecma
250 } = options;
251 const {
252 implementation,
253 options: minimizerOptions
254 } = options.minimizer;
255 const implementations = Array.isArray(implementation) ? implementation : [implementation];
256
257 /** @type {string | undefined} */
258 let lastCode;
259 /** @type {RawSourceMap | undefined} */
260 let lastMap;
261 /** @type {(Error | string)[]} */
262 const warnings = [];
263 /** @type {(Error | string)[]} */
264 const errors = [];
265 /** @type {string[]} */
266 const extractedComments = [];
267 for (let i = 0; i < implementations.length; i++) {
268 const currentImplementation = /** @type {import("./index.js").BasicMinimizerImplementation<T> & import("./index.js").MinimizeFunctionHelpers} */
269 implementations[i];
270 const baseOptions = /** @type {import("./index.js").MinimizerOptions<T> & { module?: boolean, ecma?: number | string }} */
271
272 Array.isArray(minimizerOptions) ? minimizerOptions[i] || {} : minimizerOptions || {};
273 const currentInput = typeof lastCode === "string" ? lastCode : input;
274 const currentMap = typeof lastCode === "string" ? lastMap : inputSourceMap;
275
276 // Overlay `module` and `ecma` without mutating the caller's options so
277 // a single options object can be reused safely across assets.
278 const currentOptions = /** @type {import("./index.js").MinimizerOptions<T>} */
279 {
280 ...baseOptions,
281 module: baseOptions.module || module,
282 ecma: baseOptions.ecma || ecma
283 };
284 const result = await currentImplementation({
285 [name]: currentInput
286 }, currentMap, currentOptions, extractComments);
287 if (result.warnings && result.warnings.length > 0) {
288 warnings.push(...result.warnings);
289 }
290 if (result.errors && result.errors.length > 0) {
291 errors.push(...result.errors);
292 }
293 if (result.extractedComments && result.extractedComments.length > 0) {
294 extractedComments.push(...result.extractedComments);
295 }
296 if (typeof result.code === "string") {
297 lastCode = result.code;
298 // The minimizer's output map is `name → step-output`. Chain it with
299 // the previous accumulated map so that across an array of minimizers
300 // the final map points back to the original sources.
301 lastMap = composeSourceMaps(result.map, currentMap, name);
302 }
303 }
304 return {
305 code: lastCode,
306 map: lastMap,
307 warnings,
308 errors,
309 extractedComments
310 };
311}
312
313/**
314 * @param {string} options options
315 * @returns {Promise<MinimizedResult>} minified result
316 */
317async function transform(options) {
318 // 'use strict' => this === undefined (Clean Scope)
319 // Safer for possible security issues, albeit not critical at all here
320
321 const evaluatedOptions =
322 /**
323 * @template T
324 * @type {import("./index.js").InternalOptions<T>}
325 */
326
327 // eslint-disable-next-line no-new-func
328 new Function("exports", "require", "module", "__filename", "__dirname", `'use strict'\nreturn ${options}`) // eslint-disable-next-line n/exports-style
329 (exports, require, module, __filename, __dirname);
330 return minify(evaluatedOptions);
331}
332module.exports = {
333 minify,
334 transform
335};
Note: See TracBrowser for help on using the repository browser.