UNPKG

webpack

Version:

Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.

481 lines (421 loc) 10.3 kB
/* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ "use strict"; /** @typedef {import("estree").Node} EsTreeNode */ /** @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]} */ 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} */ this.prefix = undefined; /** @type {BasicEvaluatedExpression | undefined} */ this.postfix = undefined; this.wrappedInnerExpressions = undefined; /** @type {string | undefined} */ this.identifier = undefined; /** @type {VariableInfoInterface} */ this.rootInfo = undefined; /** @type {() => string[]} */ this.getMembers = undefined; /** @type {EsTreeNode} */ 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; } 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; } 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; } 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 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 this.parts) { const partStr = part.asString(); if (partStr === undefined) return undefined; str += partStr; } return str; } return undefined; } 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; } setNumber(number) { this.type = TypeNumber; this.number = number; this.sideEffects = false; return this; } setBigInt(bigint) { this.type = TypeBigInt; this.bigint = bigint; this.sideEffects = false; return this; } setBoolean(bool) { this.type = TypeBoolean; this.bool = bool; this.sideEffects = false; return this; } setRegExp(regExp) { this.type = TypeRegExp; this.regExp = regExp; this.sideEffects = false; return this; } setIdentifier(identifier, rootInfo, getMembers) { this.type = TypeIdentifier; this.identifier = identifier; this.rootInfo = rootInfo; this.getMembers = getMembers; this.sideEffects = true; return this; } setWrapped(prefix, postfix, innerExpressions) { this.type = TypeWrapped; this.prefix = prefix; this.postfix = postfix; this.wrappedInnerExpressions = innerExpressions; this.sideEffects = true; return this; } setOptions(options) { this.type = TypeConditional; this.options = options; this.sideEffects = true; return 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; } setItems(items) { this.type = TypeArray; this.items = items; this.sideEffects = items.some(i => i.couldHaveSideEffects()); return this; } setArray(array) { this.type = TypeConstArray; this.array = array; this.sideEffects = false; return 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; } setNullish(value) { this.nullish = value; if (value) return this.setFalsy(); return this; } setRange(range) { this.range = range; return this; } setSideEffects(sideEffects = true) { this.sideEffects = sideEffects; return 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;