source: frontend/node_modules/terser/lib/compress/common.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.7 KB
RevLine 
[9af201e]1/***********************************************************************
2
3 A JavaScript tokenizer / parser / beautifier / compressor.
4 https://github.com/mishoo/UglifyJS2
5
6 -------------------------------- (C) ---------------------------------
7
8 Author: Mihai Bazon
9 <mihai.bazon@gmail.com>
10 http://mihai.bazon.net/blog
11
12 Distributed under the BSD license:
13
14 Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
15
16 Redistribution and use in source and binary forms, with or without
17 modification, are permitted provided that the following conditions
18 are met:
19
20 * Redistributions of source code must retain the above
21 copyright notice, this list of conditions and the following
22 disclaimer.
23
24 * Redistributions in binary form must reproduce the above
25 copyright notice, this list of conditions and the following
26 disclaimer in the documentation and/or other materials
27 provided with the distribution.
28
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
30 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
33 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
34 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
38 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
39 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 SUCH DAMAGE.
41
42 ***********************************************************************/
43
44import {
45 AST_Array,
46 AST_Arrow,
47 AST_BigInt,
48 AST_BlockStatement,
49 AST_Call,
50 AST_Chain,
51 AST_Class,
52 AST_Const,
53 AST_Constant,
54 AST_DefClass,
55 AST_Defun,
56 AST_EmptyStatement,
57 AST_Export,
58 AST_False,
59 AST_Function,
60 AST_Import,
61 AST_Infinity,
62 AST_LabeledStatement,
63 AST_Lambda,
64 AST_Let,
65 AST_LoopControl,
66 AST_NaN,
67 AST_Node,
68 AST_Null,
69 AST_Number,
70 AST_Object,
71 AST_ObjectKeyVal,
72 AST_PropAccess,
73 AST_RegExp,
74 AST_Scope,
75 AST_Sequence,
76 AST_SimpleStatement,
77 AST_Statement,
78 AST_String,
79 AST_SymbolRef,
80 AST_True,
81 AST_UnaryPrefix,
82 AST_Undefined,
83 AST_Using,
84
85 TreeWalker,
86 walk,
87 walk_abort,
88 walk_parent,
89} from "../ast.js";
90import { make_node, make_void_0, regexp_source_fix, string_template, makePredicate } from "../utils/index.js";
91import { first_in_statement } from "../utils/first_in_statement.js";
92import { has_flag, TOP } from "./compressor-flags.js";
93
94export function merge_sequence(array, node) {
95 if (node instanceof AST_Sequence) {
96 array.push(...node.expressions);
97 } else {
98 array.push(node);
99 }
100 return array;
101}
102
103export function make_sequence(orig, expressions) {
104 if (expressions.length == 1) return expressions[0];
105 if (expressions.length == 0) throw new Error("trying to create a sequence with length zero!");
106 return make_node(AST_Sequence, orig, {
107 expressions: expressions.reduce(merge_sequence, [])
108 });
109}
110
111export function make_empty_function(self) {
112 return make_node(AST_Function, self, {
113 uses_arguments: false,
114 argnames: [],
115 body: [],
116 is_generator: false,
117 async: false,
118 variables: new Map(),
119 uses_with: false,
120 uses_eval: false,
121 parent_scope: null,
122 enclosed: [],
123 cname: 0,
124 block_scope: undefined,
125 });
126}
127
128export function make_node_from_constant(val, orig) {
129 switch (typeof val) {
130 case "string":
131 return make_node(AST_String, orig, {
132 value: val
133 });
134 case "number":
135 if (isNaN(val)) return make_node(AST_NaN, orig);
136 if (isFinite(val)) {
137 return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, {
138 operator: "-",
139 expression: make_node(AST_Number, orig, { value: -val })
140 }) : make_node(AST_Number, orig, { value: val });
141 }
142 return val < 0 ? make_node(AST_UnaryPrefix, orig, {
143 operator: "-",
144 expression: make_node(AST_Infinity, orig)
145 }) : make_node(AST_Infinity, orig);
146 case "bigint":
147 return make_node(AST_BigInt, orig, { value: val.toString() });
148 case "boolean":
149 return make_node(val ? AST_True : AST_False, orig);
150 case "undefined":
151 return make_void_0(orig);
152 default:
153 if (val === null) {
154 return make_node(AST_Null, orig, { value: null });
155 }
156 if (val instanceof RegExp) {
157 return make_node(AST_RegExp, orig, {
158 value: {
159 source: regexp_source_fix(val.source),
160 flags: val.flags
161 }
162 });
163 }
164 throw new Error(string_template("Can't handle constant of type: {type}", {
165 type: typeof val
166 }));
167 }
168}
169
170export function best_of_expression(ast1, ast2) {
171 return ast1.size() > ast2.size() ? ast2 : ast1;
172}
173
174export function best_of_statement(ast1, ast2) {
175 return best_of_expression(
176 make_node(AST_SimpleStatement, ast1, {
177 body: ast1
178 }),
179 make_node(AST_SimpleStatement, ast2, {
180 body: ast2
181 })
182 ).body;
183}
184
185/** Find which node is smaller, and return that */
186export function best_of(compressor, ast1, ast2) {
187 if (first_in_statement(compressor)) {
188 return best_of_statement(ast1, ast2);
189 } else {
190 return best_of_expression(ast1, ast2);
191 }
192}
193
194/** Simplify an object property's key, if possible */
195export function get_simple_key(key) {
196 if (key instanceof AST_Constant) {
197 return key.getValue();
198 }
199 if (key instanceof AST_UnaryPrefix
200 && key.operator == "void"
201 && key.expression instanceof AST_Constant) {
202 return undefined;
203 }
204 return key;
205}
206
207export function read_property(obj, key) {
208 key = get_simple_key(key);
209 if (key instanceof AST_Node) return;
210
211 var value;
212 if (obj instanceof AST_Array) {
213 var elements = obj.elements;
214 if (key == "length") return make_node_from_constant(elements.length, obj);
215 if (typeof key == "number" && key in elements) value = elements[key];
216 } else if (obj instanceof AST_Object) {
217 key = "" + key;
218 var props = obj.properties;
219 for (var i = props.length; --i >= 0;) {
220 var prop = props[i];
221 if (!(prop instanceof AST_ObjectKeyVal)) return;
222 if (!value && props[i].key === key) value = props[i].value;
223 }
224 }
225
226 return value instanceof AST_SymbolRef && value.fixed_value() || value;
227}
228
229export function has_break_or_continue(loop, parent) {
230 var found = false;
231 var tw = new TreeWalker(function(node) {
232 if (found || node instanceof AST_Scope) return true;
233 if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === loop) {
234 return found = true;
235 }
236 });
237 if (parent instanceof AST_LabeledStatement) tw.push(parent);
238 tw.push(loop);
239 loop.body.walk(tw);
240 return found;
241}
242
243// we shouldn't compress (1,func)(something) to
244// func(something) because that changes the meaning of
245// the func (becomes lexical instead of global).
246export function maintain_this_binding(parent, orig, val) {
247 if (requires_sequence_to_maintain_binding(parent, orig, val)) {
248 const zero = make_node(AST_Number, orig, { value: 0 });
249 return make_sequence(orig, [ zero, val ]);
250 } else {
251 return val;
252 }
253}
254
255/** Detect (1, x.noThis)(), (0, eval)(), which need sequences */
256export function requires_sequence_to_maintain_binding(parent, orig, val) {
257 return (
258 parent instanceof AST_UnaryPrefix && parent.operator == "delete"
259 || parent instanceof AST_Call && parent.expression === orig
260 && (
261 val instanceof AST_Chain
262 || val instanceof AST_PropAccess
263 || val instanceof AST_SymbolRef && val.name == "eval"
264 )
265 );
266}
267
268export function is_func_expr(node) {
269 return node instanceof AST_Arrow || node instanceof AST_Function;
270}
271
272/**
273 * Used to determine whether the node can benefit from negation.
274 * Not the case with arrow functions (you need an extra set of parens). */
275export function is_iife_call(node) {
276 if (node.TYPE != "Call") return false;
277 return node.expression instanceof AST_Function || is_iife_call(node.expression);
278}
279
280export function is_empty(thing) {
281 if (thing === null) return true;
282 if (thing instanceof AST_EmptyStatement) return true;
283 if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
284 return false;
285}
286
287export const identifier_atom = makePredicate("Infinity NaN undefined");
288export function is_identifier_atom(node) {
289 return node instanceof AST_Infinity
290 || node instanceof AST_NaN
291 || node instanceof AST_Undefined;
292}
293
294/** Check if this is a SymbolRef node which has one def of a certain AST type */
295export function is_ref_of(ref, type) {
296 if (!(ref instanceof AST_SymbolRef)) return false;
297 var orig = ref.definition().orig;
298 for (var i = orig.length; --i >= 0;) {
299 if (orig[i] instanceof type) return true;
300 }
301}
302
303/**Can we turn { block contents... } into just the block contents ?
304 * Not if one of these is inside.
305 **/
306export function can_be_evicted_from_block(node) {
307 return !(
308 node instanceof AST_DefClass ||
309 node instanceof AST_Defun ||
310 node instanceof AST_Let ||
311 node instanceof AST_Const ||
312 node instanceof AST_Using ||
313 node instanceof AST_Export ||
314 node instanceof AST_Import
315 );
316}
317
318export function as_statement_array(thing) {
319 if (thing === null) return [];
320 if (thing instanceof AST_BlockStatement) return thing.body;
321 if (thing instanceof AST_EmptyStatement) return [];
322 if (thing instanceof AST_Statement) return [ thing ];
323 throw new Error("Can't convert thing to statement array");
324}
325
326export function is_reachable(scope_node, defs) {
327 const find_ref = node => {
328 if (node instanceof AST_SymbolRef && defs.includes(node.definition())) {
329 return walk_abort;
330 }
331 };
332
333 return walk_parent(scope_node, (node, info) => {
334 if (node instanceof AST_Scope && node !== scope_node) {
335 var parent = info.parent();
336
337 if (
338 parent instanceof AST_Call
339 && parent.expression === node
340 // Async/Generators aren't guaranteed to sync evaluate all of
341 // their body steps, so it's possible they close over the variable.
342 && !(node.async || node.is_generator)
343 ) {
344 return;
345 }
346
347 if (walk(node, find_ref)) return walk_abort;
348
349 return true;
350 }
351 });
352}
353
354/** Check if a ref refers to the name of a function/class it's defined within */
355export function is_recursive_ref(tw, def) {
356 var node;
357 for (var i = 0; node = tw.parent(i); i++) {
358 if (node instanceof AST_Lambda || node instanceof AST_Class) {
359 var name = node.name;
360 if (name && name.definition() === def) {
361 return true;
362 }
363 }
364 }
365 return false;
366}
367
368// TODO this only works with AST_Defun, shouldn't it work for other ways of defining functions?
369export function retain_top_func(fn, compressor) {
370 return compressor.top_retain
371 && fn instanceof AST_Defun
372 && has_flag(fn, TOP)
373 && fn.name
374 && compressor.top_retain(fn.name.definition());
375}
Note: See TracBrowser for help on using the repository browser.