source: frontend/node_modules/webpack/lib/javascript/BasicEvaluatedExpression.js

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

Fix frontend appearance

  • Property mode set to 100644
File size: 14.7 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8/** @typedef {import("estree").Node} Node */
9/** @typedef {import("./JavascriptParser").Range} Range */
10/** @typedef {import("./JavascriptParser").VariableInfo} VariableInfo */
11/** @typedef {import("./JavascriptParser").Members} Members */
12/** @typedef {import("./JavascriptParser").MembersOptionals} MembersOptionals */
13/** @typedef {import("./JavascriptParser").MemberRanges} MemberRanges */
14
15const TypeUnknown = 0;
16const TypeUndefined = 1;
17const TypeNull = 2;
18const TypeString = 3;
19const TypeNumber = 4;
20const TypeBoolean = 5;
21const TypeRegExp = 6;
22const TypeConditional = 7;
23const TypeArray = 8;
24const TypeConstArray = 9;
25const TypeIdentifier = 10;
26const TypeWrapped = 11;
27const TypeTemplateString = 12;
28const TypeBigInt = 13;
29
30/** @typedef {() => Members} GetMembers */
31/** @typedef {() => MembersOptionals} GetMembersOptionals */
32/** @typedef {() => MemberRanges} GetMemberRanges */
33
34class BasicEvaluatedExpression {
35 constructor() {
36 this.type = TypeUnknown;
37 /** @type {Range | undefined} */
38 this.range = undefined;
39 /** @type {boolean} */
40 this.falsy = false;
41 /** @type {boolean} */
42 this.truthy = false;
43 /** @type {boolean | undefined} */
44 this.nullish = undefined;
45 /** @type {boolean} */
46 this.sideEffects = true;
47 /** @type {boolean | undefined} */
48 this.bool = undefined;
49 /** @type {number | undefined} */
50 this.number = undefined;
51 /** @type {bigint | undefined} */
52 this.bigint = undefined;
53 /** @type {RegExp | undefined} */
54 this.regExp = undefined;
55 /** @type {string | undefined} */
56 this.string = undefined;
57 /** @type {BasicEvaluatedExpression[] | undefined} */
58 this.quasis = undefined;
59 /** @type {BasicEvaluatedExpression[] | undefined} */
60 this.parts = undefined;
61 /** @type {EXPECTED_ANY[] | undefined} */
62 this.array = undefined;
63 /** @type {BasicEvaluatedExpression[] | undefined} */
64 this.items = undefined;
65 /** @type {BasicEvaluatedExpression[] | undefined} */
66 this.options = undefined;
67 /** @type {BasicEvaluatedExpression | undefined | null} */
68 this.prefix = undefined;
69 /** @type {BasicEvaluatedExpression | undefined | null} */
70 this.postfix = undefined;
71 /** @type {BasicEvaluatedExpression[] | undefined} */
72 this.wrappedInnerExpressions = undefined;
73 /** @type {string | VariableInfo | undefined} */
74 this.identifier = undefined;
75 /** @type {string | VariableInfo | undefined} */
76 this.rootInfo = undefined;
77 /** @type {GetMembers | undefined} */
78 this.getMembers = undefined;
79 /** @type {GetMembersOptionals | undefined} */
80 this.getMembersOptionals = undefined;
81 /** @type {GetMemberRanges | undefined} */
82 this.getMemberRanges = undefined;
83 /** @type {Node | undefined} */
84 this.expression = undefined;
85 }
86
87 isUnknown() {
88 return this.type === TypeUnknown;
89 }
90
91 isNull() {
92 return this.type === TypeNull;
93 }
94
95 isUndefined() {
96 return this.type === TypeUndefined;
97 }
98
99 isString() {
100 return this.type === TypeString;
101 }
102
103 isNumber() {
104 return this.type === TypeNumber;
105 }
106
107 isBigInt() {
108 return this.type === TypeBigInt;
109 }
110
111 isBoolean() {
112 return this.type === TypeBoolean;
113 }
114
115 isRegExp() {
116 return this.type === TypeRegExp;
117 }
118
119 isConditional() {
120 return this.type === TypeConditional;
121 }
122
123 isArray() {
124 return this.type === TypeArray;
125 }
126
127 isConstArray() {
128 return this.type === TypeConstArray;
129 }
130
131 isIdentifier() {
132 return this.type === TypeIdentifier;
133 }
134
135 isWrapped() {
136 return this.type === TypeWrapped;
137 }
138
139 isTemplateString() {
140 return this.type === TypeTemplateString;
141 }
142
143 /**
144 * Is expression a primitive or an object type value?
145 * @returns {boolean | undefined} true: primitive type, false: object type, undefined: unknown/runtime-defined
146 */
147 isPrimitiveType() {
148 switch (this.type) {
149 case TypeUndefined:
150 case TypeNull:
151 case TypeString:
152 case TypeNumber:
153 case TypeBoolean:
154 case TypeBigInt:
155 case TypeWrapped:
156 case TypeTemplateString:
157 return true;
158 case TypeRegExp:
159 case TypeArray:
160 case TypeConstArray:
161 return false;
162 default:
163 return undefined;
164 }
165 }
166
167 /**
168 * Is expression a runtime or compile-time value?
169 * @returns {boolean} true: compile time value, false: runtime value
170 */
171 isCompileTimeValue() {
172 switch (this.type) {
173 case TypeUndefined:
174 case TypeNull:
175 case TypeString:
176 case TypeNumber:
177 case TypeBoolean:
178 case TypeRegExp:
179 case TypeConstArray:
180 case TypeBigInt:
181 return true;
182 default:
183 return false;
184 }
185 }
186
187 /**
188 * As compile time value.
189 * @returns {undefined | null | string | number | boolean | RegExp | EXPECTED_ANY[] | bigint} the javascript value
190 */
191 asCompileTimeValue() {
192 switch (this.type) {
193 case TypeUndefined:
194 return;
195 case TypeNull:
196 return null;
197 case TypeString:
198 return this.string;
199 case TypeNumber:
200 return this.number;
201 case TypeBoolean:
202 return this.bool;
203 case TypeRegExp:
204 return this.regExp;
205 case TypeConstArray:
206 return this.array;
207 case TypeBigInt:
208 return this.bigint;
209 default:
210 throw new Error(
211 "asCompileTimeValue must only be called for compile-time values"
212 );
213 }
214 }
215
216 isTruthy() {
217 return this.truthy;
218 }
219
220 isFalsy() {
221 return this.falsy;
222 }
223
224 isNullish() {
225 return this.nullish;
226 }
227
228 /**
229 * Can this expression have side effects?
230 * @returns {boolean} false: never has side effects
231 */
232 couldHaveSideEffects() {
233 return this.sideEffects;
234 }
235
236 /**
237 * Creates a boolean representation of this evaluated expression.
238 * @returns {boolean | undefined} true: truthy, false: falsy, undefined: unknown
239 */
240 asBool() {
241 if (this.truthy) return true;
242 if (this.falsy || this.nullish) return false;
243 if (this.isBoolean()) return this.bool;
244 if (this.isNull()) return false;
245 if (this.isUndefined()) return false;
246 if (this.isString()) return this.string !== "";
247 if (this.isNumber()) return this.number !== 0;
248 if (this.isBigInt()) return this.bigint !== BigInt(0);
249 if (this.isRegExp()) return true;
250 if (this.isArray()) return true;
251 if (this.isConstArray()) return true;
252 if (this.isWrapped()) {
253 return (this.prefix && this.prefix.asBool()) ||
254 (this.postfix && this.postfix.asBool())
255 ? true
256 : undefined;
257 }
258 if (this.isTemplateString()) {
259 const str = this.asString();
260 if (typeof str === "string") return str !== "";
261 }
262 }
263
264 /**
265 * Creates a nullish coalescing representation of this evaluated expression.
266 * @returns {boolean | undefined} true: nullish, false: not nullish, undefined: unknown
267 */
268 asNullish() {
269 const nullish = this.isNullish();
270
271 if (nullish === true || this.isNull() || this.isUndefined()) return true;
272
273 if (nullish === false) return false;
274 if (this.isTruthy()) return false;
275 if (this.isBoolean()) return false;
276 if (this.isString()) return false;
277 if (this.isNumber()) return false;
278 if (this.isBigInt()) return false;
279 if (this.isRegExp()) return false;
280 if (this.isArray()) return false;
281 if (this.isConstArray()) return false;
282 if (this.isTemplateString()) return false;
283 if (this.isRegExp()) return false;
284 }
285
286 /**
287 * Creates a string representation of this evaluated expression.
288 * @returns {string | undefined} the string representation or undefined if not possible
289 */
290 asString() {
291 if (this.isBoolean()) return `${this.bool}`;
292 if (this.isNull()) return "null";
293 if (this.isUndefined()) return "undefined";
294 if (this.isString()) return this.string;
295 if (this.isNumber()) return `${this.number}`;
296 if (this.isBigInt()) return `${this.bigint}`;
297 if (this.isRegExp()) return `${this.regExp}`;
298 if (this.isArray()) {
299 /** @type {string[]} */
300 const array = [];
301 for (const item of /** @type {BasicEvaluatedExpression[]} */ (
302 this.items
303 )) {
304 const itemStr = item.asString();
305 if (itemStr === undefined) return;
306 array.push(itemStr);
307 }
308 return `${array}`;
309 }
310 if (this.isConstArray()) return `${this.array}`;
311 if (this.isTemplateString()) {
312 let str = "";
313 for (const part of /** @type {BasicEvaluatedExpression[]} */ (
314 this.parts
315 )) {
316 const partStr = part.asString();
317 if (partStr === undefined) return;
318 str += partStr;
319 }
320 return str;
321 }
322 }
323
324 /**
325 * Updates string using the provided string.
326 * @param {string} string value
327 * @returns {BasicEvaluatedExpression} basic evaluated expression
328 */
329 setString(string) {
330 this.type = TypeString;
331 this.string = string;
332 this.sideEffects = false;
333 return this;
334 }
335
336 setUndefined() {
337 this.type = TypeUndefined;
338 this.sideEffects = false;
339 return this;
340 }
341
342 setNull() {
343 this.type = TypeNull;
344 this.sideEffects = false;
345 return this;
346 }
347
348 /**
349 * Set's the value of this expression to a number
350 * @param {number} number number to set
351 * @returns {this} this
352 */
353 setNumber(number) {
354 this.type = TypeNumber;
355 this.number = number;
356 this.sideEffects = false;
357 return this;
358 }
359
360 /**
361 * Set's the value of this expression to a BigInt
362 * @param {bigint} bigint bigint to set
363 * @returns {this} this
364 */
365 setBigInt(bigint) {
366 this.type = TypeBigInt;
367 this.bigint = bigint;
368 this.sideEffects = false;
369 return this;
370 }
371
372 /**
373 * Set's the value of this expression to a boolean
374 * @param {boolean} bool boolean to set
375 * @returns {this} this
376 */
377 setBoolean(bool) {
378 this.type = TypeBoolean;
379 this.bool = bool;
380 this.sideEffects = false;
381 return this;
382 }
383
384 /**
385 * Set's the value of this expression to a regular expression
386 * @param {RegExp} regExp regular expression to set
387 * @returns {this} this
388 */
389 setRegExp(regExp) {
390 this.type = TypeRegExp;
391 this.regExp = regExp;
392 this.sideEffects = false;
393 return this;
394 }
395
396 /**
397 * Set's the value of this expression to a particular identifier and its members.
398 * @param {string | VariableInfo} identifier identifier to set
399 * @param {string | VariableInfo} rootInfo root info
400 * @param {GetMembers} getMembers members
401 * @param {GetMembersOptionals=} getMembersOptionals optional members
402 * @param {GetMemberRanges=} getMemberRanges ranges of progressively increasing sub-expressions
403 * @returns {this} this
404 */
405 setIdentifier(
406 identifier,
407 rootInfo,
408 getMembers,
409 getMembersOptionals,
410 getMemberRanges
411 ) {
412 this.type = TypeIdentifier;
413 this.identifier = identifier;
414 this.rootInfo = rootInfo;
415 this.getMembers = getMembers;
416 this.getMembersOptionals = getMembersOptionals;
417 this.getMemberRanges = getMemberRanges;
418 this.sideEffects = true;
419 return this;
420 }
421
422 /**
423 * Wraps an array of expressions with a prefix and postfix expression.
424 * @param {BasicEvaluatedExpression | null | undefined} prefix Expression to be added before the innerExpressions
425 * @param {BasicEvaluatedExpression | null | undefined} postfix Expression to be added after the innerExpressions
426 * @param {BasicEvaluatedExpression[] | undefined} innerExpressions Expressions to be wrapped
427 * @returns {this} this
428 */
429 setWrapped(prefix, postfix, innerExpressions) {
430 this.type = TypeWrapped;
431 this.prefix = prefix;
432 this.postfix = postfix;
433 this.wrappedInnerExpressions = innerExpressions;
434 this.sideEffects = true;
435 return this;
436 }
437
438 /**
439 * Stores the options of a conditional expression.
440 * @param {BasicEvaluatedExpression[]} options optional (consequent/alternate) expressions to be set
441 * @returns {this} this
442 */
443 setOptions(options) {
444 this.type = TypeConditional;
445 this.options = options;
446 this.sideEffects = true;
447 return this;
448 }
449
450 /**
451 * Adds the provided basic evaluated expression to the basic evaluated expression.
452 * @param {BasicEvaluatedExpression[]} options optional (consequent/alternate) expressions to be added
453 * @returns {this} this
454 */
455 addOptions(options) {
456 if (!this.options) {
457 this.type = TypeConditional;
458 this.options = [];
459 this.sideEffects = true;
460 }
461 for (const item of options) {
462 this.options.push(item);
463 }
464 return this;
465 }
466
467 /**
468 * Set's the value of this expression to an array of expressions.
469 * @param {BasicEvaluatedExpression[]} items expressions to set
470 * @returns {this} this
471 */
472 setItems(items) {
473 this.type = TypeArray;
474 this.items = items;
475 this.sideEffects = items.some((i) => i.couldHaveSideEffects());
476 return this;
477 }
478
479 /**
480 * Set's the value of this expression to an array of strings.
481 * @param {string[]} array array to set
482 * @returns {this} this
483 */
484 setArray(array) {
485 this.type = TypeConstArray;
486 this.array = array;
487 this.sideEffects = false;
488 return this;
489 }
490
491 /**
492 * Set's the value of this expression to a processed/unprocessed template string. Used
493 * for evaluating TemplateLiteral expressions in the JavaScript Parser.
494 * @param {BasicEvaluatedExpression[]} quasis template string quasis
495 * @param {BasicEvaluatedExpression[]} parts template string parts
496 * @param {"cooked" | "raw"} kind template string kind
497 * @returns {this} this
498 */
499 setTemplateString(quasis, parts, kind) {
500 this.type = TypeTemplateString;
501 this.quasis = quasis;
502 this.parts = parts;
503 this.templateStringKind = kind;
504 this.sideEffects = parts.some((p) => p.sideEffects);
505 return this;
506 }
507
508 setTruthy() {
509 this.falsy = false;
510 this.truthy = true;
511 this.nullish = false;
512 return this;
513 }
514
515 setFalsy() {
516 this.falsy = true;
517 this.truthy = false;
518 return this;
519 }
520
521 /**
522 * Set's the value of the expression to nullish.
523 * @param {boolean} value true, if the expression is nullish
524 * @returns {this} this
525 */
526 setNullish(value) {
527 this.nullish = value;
528
529 if (value) return this.setFalsy();
530
531 return this;
532 }
533
534 /**
535 * Set's the range for the expression.
536 * @param {Range} range range to set
537 * @returns {this} this
538 */
539 setRange(range) {
540 this.range = range;
541 return this;
542 }
543
544 /**
545 * Set whether or not the expression has side effects.
546 * @param {boolean} sideEffects true, if the expression has side effects
547 * @returns {this} this
548 */
549 setSideEffects(sideEffects = true) {
550 this.sideEffects = sideEffects;
551 return this;
552 }
553
554 /**
555 * Set the expression node for the expression.
556 * @param {Node | undefined} expression expression
557 * @returns {this} this
558 */
559 setExpression(expression) {
560 this.expression = expression;
561 return this;
562 }
563}
564
565/**
566 * Returns is valid flags.
567 * @param {string} flags regexp flags
568 * @returns {boolean} is valid flags
569 */
570BasicEvaluatedExpression.isValidRegExpFlags = (flags) => {
571 const len = flags.length;
572
573 if (len === 0) return true;
574 if (len > 4) return false;
575
576 // cspell:word gimy
577 let remaining = 0b0000; // bit per RegExp flag: gimy
578
579 for (let i = 0; i < len; i++) {
580 switch (flags.charCodeAt(i)) {
581 case 103 /* g */:
582 if (remaining & 0b1000) return false;
583 remaining |= 0b1000;
584 break;
585 case 105 /* i */:
586 if (remaining & 0b0100) return false;
587 remaining |= 0b0100;
588 break;
589 case 109 /* m */:
590 if (remaining & 0b0010) return false;
591 remaining |= 0b0010;
592 break;
593 case 121 /* y */:
594 if (remaining & 0b0001) return false;
595 remaining |= 0b0001;
596 break;
597 default:
598 return false;
599 }
600 }
601
602 return true;
603};
604
605module.exports = BasicEvaluatedExpression;
Note: See TracBrowser for help on using the repository browser.