diff options
Diffstat (limited to 'node_modules/webpack/lib/javascript/BasicEvaluatedExpression.js')
| -rw-r--r-- | node_modules/webpack/lib/javascript/BasicEvaluatedExpression.js | 609 |
1 files changed, 609 insertions, 0 deletions
diff --git a/node_modules/webpack/lib/javascript/BasicEvaluatedExpression.js b/node_modules/webpack/lib/javascript/BasicEvaluatedExpression.js new file mode 100644 index 00000000..701791c8 --- /dev/null +++ b/node_modules/webpack/lib/javascript/BasicEvaluatedExpression.js @@ -0,0 +1,609 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + +"use strict"; + +/** @typedef {import("estree").Node} Node */ +/** @typedef {import("./JavascriptParser").Range} Range */ +/** @typedef {import("./JavascriptParser").VariableInfoInterface} VariableInfoInterface */ + +const TypeUnknown = 0; +const TypeUndefined = 1; +const TypeNull = 2; +const TypeString = 3; +const TypeNumber = 4; +const TypeBoolean = 5; +const TypeRegExp = 6; +const TypeConditional = 7; +const TypeArray = 8; +const TypeConstArray = 9; +const TypeIdentifier = 10; +const TypeWrapped = 11; +const TypeTemplateString = 12; +const TypeBigInt = 13; + +class BasicEvaluatedExpression { + constructor() { + this.type = TypeUnknown; + /** @type {[number, number] | undefined} */ + this.range = undefined; + /** @type {boolean} */ + this.falsy = false; + /** @type {boolean} */ + this.truthy = false; + /** @type {boolean | undefined} */ + this.nullish = undefined; + /** @type {boolean} */ + this.sideEffects = true; + /** @type {boolean | undefined} */ + this.bool = undefined; + /** @type {number | undefined} */ + this.number = undefined; + /** @type {bigint | undefined} */ + this.bigint = undefined; + /** @type {RegExp | undefined} */ + this.regExp = undefined; + /** @type {string | undefined} */ + this.string = undefined; + /** @type {BasicEvaluatedExpression[] | undefined} */ + this.quasis = undefined; + /** @type {BasicEvaluatedExpression[] | undefined} */ + this.parts = undefined; + /** @type {any[] | undefined} */ + this.array = undefined; + /** @type {BasicEvaluatedExpression[] | undefined} */ + this.items = undefined; + /** @type {BasicEvaluatedExpression[] | undefined} */ + this.options = undefined; + /** @type {BasicEvaluatedExpression | undefined | null} */ + this.prefix = undefined; + /** @type {BasicEvaluatedExpression | undefined | null} */ + this.postfix = undefined; + /** @type {BasicEvaluatedExpression[] | undefined} */ + this.wrappedInnerExpressions = undefined; + /** @type {string | VariableInfoInterface | undefined} */ + this.identifier = undefined; + /** @type {string | VariableInfoInterface | undefined} */ + this.rootInfo = undefined; + /** @type {(() => string[]) | undefined} */ + this.getMembers = undefined; + /** @type {(() => boolean[]) | undefined} */ + this.getMembersOptionals = undefined; + /** @type {(() => Range[]) | undefined} */ + this.getMemberRanges = undefined; + /** @type {Node | undefined} */ + this.expression = undefined; + } + + isUnknown() { + return this.type === TypeUnknown; + } + + isNull() { + return this.type === TypeNull; + } + + isUndefined() { + return this.type === TypeUndefined; + } + + isString() { + return this.type === TypeString; + } + + isNumber() { + return this.type === TypeNumber; + } + + isBigInt() { + return this.type === TypeBigInt; + } + + isBoolean() { + return this.type === TypeBoolean; + } + + isRegExp() { + return this.type === TypeRegExp; + } + + isConditional() { + return this.type === TypeConditional; + } + + isArray() { + return this.type === TypeArray; + } + + isConstArray() { + return this.type === TypeConstArray; + } + + isIdentifier() { + return this.type === TypeIdentifier; + } + + isWrapped() { + return this.type === TypeWrapped; + } + + isTemplateString() { + return this.type === TypeTemplateString; + } + + /** + * Is expression a primitive or an object type value? + * @returns {boolean | undefined} true: primitive type, false: object type, undefined: unknown/runtime-defined + */ + isPrimitiveType() { + switch (this.type) { + case TypeUndefined: + case TypeNull: + case TypeString: + case TypeNumber: + case TypeBoolean: + case TypeBigInt: + case TypeWrapped: + case TypeTemplateString: + return true; + case TypeRegExp: + case TypeArray: + case TypeConstArray: + return false; + default: + return undefined; + } + } + + /** + * Is expression a runtime or compile-time value? + * @returns {boolean} true: compile time value, false: runtime value + */ + isCompileTimeValue() { + switch (this.type) { + case TypeUndefined: + case TypeNull: + case TypeString: + case TypeNumber: + case TypeBoolean: + case TypeRegExp: + case TypeConstArray: + case TypeBigInt: + return true; + default: + return false; + } + } + + /** + * Gets the compile-time value of the expression + * @returns {any} the javascript value + */ + asCompileTimeValue() { + switch (this.type) { + case TypeUndefined: + return undefined; + case TypeNull: + return null; + case TypeString: + return this.string; + case TypeNumber: + return this.number; + case TypeBoolean: + return this.bool; + case TypeRegExp: + return this.regExp; + case TypeConstArray: + return this.array; + case TypeBigInt: + return this.bigint; + default: + throw new Error( + "asCompileTimeValue must only be called for compile-time values" + ); + } + } + + isTruthy() { + return this.truthy; + } + + isFalsy() { + return this.falsy; + } + + isNullish() { + return this.nullish; + } + + /** + * Can this expression have side effects? + * @returns {boolean} false: never has side effects + */ + couldHaveSideEffects() { + return this.sideEffects; + } + + /** + * Creates a boolean representation of this evaluated expression. + * @returns {boolean | undefined} true: truthy, false: falsy, undefined: unknown + */ + asBool() { + if (this.truthy) return true; + if (this.falsy || this.nullish) return false; + if (this.isBoolean()) return this.bool; + if (this.isNull()) return false; + if (this.isUndefined()) return false; + if (this.isString()) return this.string !== ""; + if (this.isNumber()) return this.number !== 0; + if (this.isBigInt()) return this.bigint !== BigInt(0); + if (this.isRegExp()) return true; + if (this.isArray()) return true; + if (this.isConstArray()) return true; + if (this.isWrapped()) { + return (this.prefix && this.prefix.asBool()) || + (this.postfix && this.postfix.asBool()) + ? true + : undefined; + } + if (this.isTemplateString()) { + const str = this.asString(); + if (typeof str === "string") return str !== ""; + } + return undefined; + } + + /** + * Creates a nullish coalescing representation of this evaluated expression. + * @returns {boolean | undefined} true: nullish, false: not nullish, undefined: unknown + */ + asNullish() { + const nullish = this.isNullish(); + + if (nullish === true || this.isNull() || this.isUndefined()) return true; + + if (nullish === false) return false; + if (this.isTruthy()) return false; + if (this.isBoolean()) return false; + if (this.isString()) return false; + if (this.isNumber()) return false; + if (this.isBigInt()) return false; + if (this.isRegExp()) return false; + if (this.isArray()) return false; + if (this.isConstArray()) return false; + if (this.isTemplateString()) return false; + if (this.isRegExp()) return false; + + return undefined; + } + + /** + * Creates a string representation of this evaluated expression. + * @returns {string | undefined} the string representation or undefined if not possible + */ + asString() { + if (this.isBoolean()) return `${this.bool}`; + if (this.isNull()) return "null"; + if (this.isUndefined()) return "undefined"; + if (this.isString()) return this.string; + if (this.isNumber()) return `${this.number}`; + if (this.isBigInt()) return `${this.bigint}`; + if (this.isRegExp()) return `${this.regExp}`; + if (this.isArray()) { + let array = []; + for (const item of /** @type {BasicEvaluatedExpression[]} */ ( + this.items + )) { + const itemStr = item.asString(); + if (itemStr === undefined) return undefined; + array.push(itemStr); + } + return `${array}`; + } + if (this.isConstArray()) return `${this.array}`; + if (this.isTemplateString()) { + let str = ""; + for (const part of /** @type {BasicEvaluatedExpression[]} */ ( + this.parts + )) { + const partStr = part.asString(); + if (partStr === undefined) return undefined; + str += partStr; + } + return str; + } + return undefined; + } + + /** + * @param {string} string value + * @returns {BasicEvaluatedExpression} basic evaluated expression + */ + setString(string) { + this.type = TypeString; + this.string = string; + this.sideEffects = false; + return this; + } + + setUndefined() { + this.type = TypeUndefined; + this.sideEffects = false; + return this; + } + + setNull() { + this.type = TypeNull; + this.sideEffects = false; + return this; + } + + /** + * Set's the value of this expression to a number + * @param {number} number number to set + * @returns {this} this + */ + setNumber(number) { + this.type = TypeNumber; + this.number = number; + this.sideEffects = false; + return this; + } + + /** + * Set's the value of this expression to a BigInt + * @param {bigint} bigint bigint to set + * @returns {this} this + */ + setBigInt(bigint) { + this.type = TypeBigInt; + this.bigint = bigint; + this.sideEffects = false; + return this; + } + + /** + * Set's the value of this expression to a boolean + * @param {boolean} bool boolean to set + * @returns {this} this + */ + setBoolean(bool) { + this.type = TypeBoolean; + this.bool = bool; + this.sideEffects = false; + return this; + } + + /** + * Set's the value of this expression to a regular expression + * @param {RegExp} regExp regular expression to set + * @returns {this} this + */ + setRegExp(regExp) { + this.type = TypeRegExp; + this.regExp = regExp; + this.sideEffects = false; + return this; + } + + /** + * Set's the value of this expression to a particular identifier and its members. + * + * @param {string | VariableInfoInterface} identifier identifier to set + * @param {string | VariableInfoInterface} rootInfo root info + * @param {() => string[]} getMembers members + * @param {() => boolean[]=} getMembersOptionals optional members + * @param {() => Range[]=} getMemberRanges ranges of progressively increasing sub-expressions + * @returns {this} this + */ + setIdentifier( + identifier, + rootInfo, + getMembers, + getMembersOptionals, + getMemberRanges + ) { + this.type = TypeIdentifier; + this.identifier = identifier; + this.rootInfo = rootInfo; + this.getMembers = getMembers; + this.getMembersOptionals = getMembersOptionals; + this.getMemberRanges = getMemberRanges; + this.sideEffects = true; + return this; + } + + /** + * Wraps an array of expressions with a prefix and postfix expression. + * + * @param {BasicEvaluatedExpression | null | undefined} prefix Expression to be added before the innerExpressions + * @param {BasicEvaluatedExpression | null | undefined} postfix Expression to be added after the innerExpressions + * @param {BasicEvaluatedExpression[]} innerExpressions Expressions to be wrapped + * @returns {this} this + */ + setWrapped(prefix, postfix, innerExpressions) { + this.type = TypeWrapped; + this.prefix = prefix; + this.postfix = postfix; + this.wrappedInnerExpressions = innerExpressions; + this.sideEffects = true; + return this; + } + + /** + * Stores the options of a conditional expression. + * + * @param {BasicEvaluatedExpression[]} options optional (consequent/alternate) expressions to be set + * @returns {this} this + */ + setOptions(options) { + this.type = TypeConditional; + this.options = options; + this.sideEffects = true; + return this; + } + + /** + * Adds options to a conditional expression. + * + * @param {BasicEvaluatedExpression[]} options optional (consequent/alternate) expressions to be added + * @returns {this} this + */ + addOptions(options) { + if (!this.options) { + this.type = TypeConditional; + this.options = []; + this.sideEffects = true; + } + for (const item of options) { + this.options.push(item); + } + return this; + } + + /** + * Set's the value of this expression to an array of expressions. + * + * @param {BasicEvaluatedExpression[]} items expressions to set + * @returns {this} this + */ + setItems(items) { + this.type = TypeArray; + this.items = items; + this.sideEffects = items.some(i => i.couldHaveSideEffects()); + return this; + } + + /** + * Set's the value of this expression to an array of strings. + * + * @param {string[]} array array to set + * @returns {this} this + */ + setArray(array) { + this.type = TypeConstArray; + this.array = array; + this.sideEffects = false; + return this; + } + + /** + * Set's the value of this expression to a processed/unprocessed template string. Used + * for evaluating TemplateLiteral expressions in the JavaScript Parser. + * + * @param {BasicEvaluatedExpression[]} quasis template string quasis + * @param {BasicEvaluatedExpression[]} parts template string parts + * @param {"cooked" | "raw"} kind template string kind + * @returns {this} this + */ + setTemplateString(quasis, parts, kind) { + this.type = TypeTemplateString; + this.quasis = quasis; + this.parts = parts; + this.templateStringKind = kind; + this.sideEffects = parts.some(p => p.sideEffects); + return this; + } + + setTruthy() { + this.falsy = false; + this.truthy = true; + this.nullish = false; + return this; + } + + setFalsy() { + this.falsy = true; + this.truthy = false; + return this; + } + + /** + * Set's the value of the expression to nullish. + * + * @param {boolean} value true, if the expression is nullish + * @returns {this} this + */ + setNullish(value) { + this.nullish = value; + + if (value) return this.setFalsy(); + + return this; + } + + /** + * Set's the range for the expression. + * + * @param {[number, number]} range range to set + * @returns {this} this + */ + setRange(range) { + this.range = range; + return this; + } + + /** + * Set whether or not the expression has side effects. + * + * @param {boolean} sideEffects true, if the expression has side effects + * @returns {this} this + */ + setSideEffects(sideEffects = true) { + this.sideEffects = sideEffects; + return this; + } + + /** + * Set the expression node for the expression. + * + * @param {Node | undefined} expression expression + * @returns {this} this + */ + setExpression(expression) { + this.expression = expression; + return this; + } +} + +/** + * @param {string} flags regexp flags + * @returns {boolean} is valid flags + */ +BasicEvaluatedExpression.isValidRegExpFlags = flags => { + const len = flags.length; + + if (len === 0) return true; + if (len > 4) return false; + + // cspell:word gimy + let remaining = 0b0000; // bit per RegExp flag: gimy + + for (let i = 0; i < len; i++) + switch (flags.charCodeAt(i)) { + case 103 /* g */: + if (remaining & 0b1000) return false; + remaining |= 0b1000; + break; + case 105 /* i */: + if (remaining & 0b0100) return false; + remaining |= 0b0100; + break; + case 109 /* m */: + if (remaining & 0b0010) return false; + remaining |= 0b0010; + break; + case 121 /* y */: + if (remaining & 0b0001) return false; + remaining |= 0b0001; + break; + default: + return false; + } + + return true; +}; + +module.exports = BasicEvaluatedExpression; |
