Buckets:
| /* | |
| MIT License http://www.opensource.org/licenses/mit-license.php | |
| Author Tobias Koppers @sokra | |
| */ | |
| ; | |
| const { SyncWaterfallHook } = require("tapable"); | |
| const { | |
| JAVASCRIPT_MODULE_TYPE_AUTO, | |
| JAVASCRIPT_MODULE_TYPE_DYNAMIC, | |
| JAVASCRIPT_MODULE_TYPE_ESM | |
| } = require("./ModuleTypeConstants"); | |
| const RuntimeGlobals = require("./RuntimeGlobals"); | |
| const WebpackError = require("./WebpackError"); | |
| const ConstDependency = require("./dependencies/ConstDependency"); | |
| const BasicEvaluatedExpression = require("./javascript/BasicEvaluatedExpression"); | |
| const { VariableInfo } = require("./javascript/JavascriptParser"); | |
| const { | |
| evaluateToString, | |
| toConstantDependency | |
| } = require("./javascript/JavascriptParserHelpers"); | |
| const createHash = require("./util/createHash"); | |
| /** @typedef {import("estree").Expression} Expression */ | |
| /** @typedef {import("./Compiler")} Compiler */ | |
| /** @typedef {import("./Module").BuildInfo} BuildInfo */ | |
| /** @typedef {import("./Module").ValueCacheVersion} ValueCacheVersion */ | |
| /** @typedef {import("./Module").ValueCacheVersions} ValueCacheVersions */ | |
| /** @typedef {import("./NormalModule")} NormalModule */ | |
| /** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */ | |
| /** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */ | |
| /** @typedef {import("./javascript/JavascriptParser").DestructuringAssignmentProperties} DestructuringAssignmentProperties */ | |
| /** @typedef {import("./javascript/JavascriptParser").Range} Range */ | |
| /** @typedef {import("./logging/Logger").Logger} Logger */ | |
| /** @typedef {import("./Compilation")} Compilation */ | |
| /** @typedef {null | undefined | RegExp | EXPECTED_FUNCTION | string | number | boolean | bigint | undefined} CodeValuePrimitive */ | |
| /** @typedef {RecursiveArrayOrRecord<CodeValuePrimitive | RuntimeValue>} CodeValue */ | |
| /** | |
| * @typedef {object} RuntimeValueOptions | |
| * @property {string[]=} fileDependencies | |
| * @property {string[]=} contextDependencies | |
| * @property {string[]=} missingDependencies | |
| * @property {string[]=} buildDependencies | |
| * @property {string | (() => string)=} version | |
| */ | |
| /** @typedef {(value: { module: NormalModule, key: string, readonly version: ValueCacheVersion }) => CodeValuePrimitive} GeneratorFn */ | |
| class RuntimeValue { | |
| /** | |
| * @param {GeneratorFn} fn generator function | |
| * @param {true | string[] | RuntimeValueOptions=} options options | |
| */ | |
| constructor(fn, options) { | |
| /** @type {GeneratorFn} */ | |
| this.fn = fn; | |
| if (Array.isArray(options)) { | |
| options = { | |
| fileDependencies: options | |
| }; | |
| } | |
| /** @type {true | RuntimeValueOptions} */ | |
| this.options = options || {}; | |
| } | |
| get fileDependencies() { | |
| return this.options === true ? true : this.options.fileDependencies; | |
| } | |
| /** | |
| * @param {JavascriptParser} parser the parser | |
| * @param {ValueCacheVersions} valueCacheVersions valueCacheVersions | |
| * @param {string} key the defined key | |
| * @returns {CodeValuePrimitive} code | |
| */ | |
| exec(parser, valueCacheVersions, key) { | |
| const buildInfo = /** @type {BuildInfo} */ (parser.state.module.buildInfo); | |
| if (this.options === true) { | |
| buildInfo.cacheable = false; | |
| } else { | |
| if (this.options.fileDependencies) { | |
| for (const dep of this.options.fileDependencies) { | |
| /** @type {NonNullable<BuildInfo["fileDependencies"]>} */ | |
| (buildInfo.fileDependencies).add(dep); | |
| } | |
| } | |
| if (this.options.contextDependencies) { | |
| for (const dep of this.options.contextDependencies) { | |
| /** @type {NonNullable<BuildInfo["contextDependencies"]>} */ | |
| (buildInfo.contextDependencies).add(dep); | |
| } | |
| } | |
| if (this.options.missingDependencies) { | |
| for (const dep of this.options.missingDependencies) { | |
| /** @type {NonNullable<BuildInfo["missingDependencies"]>} */ | |
| (buildInfo.missingDependencies).add(dep); | |
| } | |
| } | |
| if (this.options.buildDependencies) { | |
| for (const dep of this.options.buildDependencies) { | |
| /** @type {NonNullable<BuildInfo["buildDependencies"]>} */ | |
| (buildInfo.buildDependencies).add(dep); | |
| } | |
| } | |
| } | |
| return this.fn({ | |
| module: parser.state.module, | |
| key, | |
| get version() { | |
| return /** @type {ValueCacheVersion} */ ( | |
| valueCacheVersions.get(VALUE_DEP_PREFIX + key) | |
| ); | |
| } | |
| }); | |
| } | |
| getCacheVersion() { | |
| return this.options === true | |
| ? undefined | |
| : (typeof this.options.version === "function" | |
| ? this.options.version() | |
| : this.options.version) || "unset"; | |
| } | |
| } | |
| /** | |
| * @param {DestructuringAssignmentProperties | undefined} properties properties | |
| * @returns {Set<string> | undefined} used keys | |
| */ | |
| function getObjKeys(properties) { | |
| if (!properties) return; | |
| return new Set([...properties].map((p) => p.id)); | |
| } | |
| /** @typedef {Set<string> | null} ObjKeys */ | |
| /** @typedef {boolean | undefined | null} AsiSafe */ | |
| /** | |
| * @param {EXPECTED_ANY[] | { [k: string]: EXPECTED_ANY }} obj obj | |
| * @param {JavascriptParser} parser Parser | |
| * @param {ValueCacheVersions} valueCacheVersions valueCacheVersions | |
| * @param {string} key the defined key | |
| * @param {RuntimeTemplate} runtimeTemplate the runtime template | |
| * @param {Logger} logger the logger object | |
| * @param {AsiSafe=} asiSafe asi safe (undefined: unknown, null: unneeded) | |
| * @param {ObjKeys=} objKeys used keys | |
| * @returns {string} code converted to string that evaluates | |
| */ | |
| const stringifyObj = ( | |
| obj, | |
| parser, | |
| valueCacheVersions, | |
| key, | |
| runtimeTemplate, | |
| logger, | |
| asiSafe, | |
| objKeys | |
| ) => { | |
| /** @type {string} */ | |
| let code; | |
| const arr = Array.isArray(obj); | |
| if (arr) { | |
| code = `[${obj | |
| .map((code) => | |
| toCode( | |
| code, | |
| parser, | |
| valueCacheVersions, | |
| key, | |
| runtimeTemplate, | |
| logger, | |
| null | |
| ) | |
| ) | |
| .join(",")}]`; | |
| } else { | |
| let keys = Object.keys(obj); | |
| if (objKeys) { | |
| keys = objKeys.size === 0 ? [] : keys.filter((k) => objKeys.has(k)); | |
| } | |
| code = `{${keys | |
| .map((key) => { | |
| const code = obj[key]; | |
| return `${key === "__proto__" ? '["__proto__"]' : JSON.stringify(key)}:${toCode( | |
| code, | |
| parser, | |
| valueCacheVersions, | |
| key, | |
| runtimeTemplate, | |
| logger, | |
| null | |
| )}`; | |
| }) | |
| .join(",")}}`; | |
| } | |
| switch (asiSafe) { | |
| case null: | |
| return code; | |
| case true: | |
| return arr ? code : `(${code})`; | |
| case false: | |
| return arr ? `;${code}` : `;(${code})`; | |
| default: | |
| return `/*#__PURE__*/Object(${code})`; | |
| } | |
| }; | |
| /** | |
| * Convert code to a string that evaluates | |
| * @param {CodeValue} code Code to evaluate | |
| * @param {JavascriptParser} parser Parser | |
| * @param {ValueCacheVersions} valueCacheVersions valueCacheVersions | |
| * @param {string} key the defined key | |
| * @param {RuntimeTemplate} runtimeTemplate the runtime template | |
| * @param {Logger} logger the logger object | |
| * @param {boolean | undefined | null=} asiSafe asi safe (undefined: unknown, null: unneeded) | |
| * @param {ObjKeys=} objKeys used keys | |
| * @returns {string} code converted to string that evaluates | |
| */ | |
| const toCode = ( | |
| code, | |
| parser, | |
| valueCacheVersions, | |
| key, | |
| runtimeTemplate, | |
| logger, | |
| asiSafe, | |
| objKeys | |
| ) => { | |
| const transformToCode = () => { | |
| if (code === null) { | |
| return "null"; | |
| } | |
| if (code === undefined) { | |
| return "undefined"; | |
| } | |
| if (Object.is(code, -0)) { | |
| return "-0"; | |
| } | |
| if (code instanceof RuntimeValue) { | |
| return toCode( | |
| code.exec(parser, valueCacheVersions, key), | |
| parser, | |
| valueCacheVersions, | |
| key, | |
| runtimeTemplate, | |
| logger, | |
| asiSafe | |
| ); | |
| } | |
| if (code instanceof RegExp && code.toString) { | |
| return code.toString(); | |
| } | |
| if (typeof code === "function" && code.toString) { | |
| return `(${code.toString()})`; | |
| } | |
| if (typeof code === "object") { | |
| return stringifyObj( | |
| code, | |
| parser, | |
| valueCacheVersions, | |
| key, | |
| runtimeTemplate, | |
| logger, | |
| asiSafe, | |
| objKeys | |
| ); | |
| } | |
| if (typeof code === "bigint") { | |
| return runtimeTemplate.supportsBigIntLiteral() | |
| ? `${code}n` | |
| : `BigInt("${code}")`; | |
| } | |
| return `${code}`; | |
| }; | |
| const strCode = transformToCode(); | |
| logger.debug(`Replaced "${key}" with "${strCode}"`); | |
| return strCode; | |
| }; | |
| /** | |
| * @param {CodeValue} code code | |
| * @returns {string | undefined} result | |
| */ | |
| const toCacheVersion = (code) => { | |
| if (code === null) { | |
| return "null"; | |
| } | |
| if (code === undefined) { | |
| return "undefined"; | |
| } | |
| if (Object.is(code, -0)) { | |
| return "-0"; | |
| } | |
| if (code instanceof RuntimeValue) { | |
| return code.getCacheVersion(); | |
| } | |
| if (code instanceof RegExp && code.toString) { | |
| return code.toString(); | |
| } | |
| if (typeof code === "function" && code.toString) { | |
| return `(${code.toString()})`; | |
| } | |
| if (typeof code === "object") { | |
| const items = Object.keys(code).map((key) => ({ | |
| key, | |
| value: toCacheVersion( | |
| /** @type {Record<string, EXPECTED_ANY>} */ | |
| (code)[key] | |
| ) | |
| })); | |
| if (items.some(({ value }) => value === undefined)) return; | |
| return `{${items.map(({ key, value }) => `${key}: ${value}`).join(", ")}}`; | |
| } | |
| if (typeof code === "bigint") { | |
| return `${code}n`; | |
| } | |
| return `${code}`; | |
| }; | |
| const PLUGIN_NAME = "DefinePlugin"; | |
| const VALUE_DEP_PREFIX = `webpack/${PLUGIN_NAME} `; | |
| const VALUE_DEP_MAIN = `webpack/${PLUGIN_NAME}_hash`; | |
| const TYPEOF_OPERATOR_REGEXP = /^typeof\s+/; | |
| const WEBPACK_REQUIRE_FUNCTION_REGEXP = new RegExp( | |
| `${RuntimeGlobals.require}\\s*(!?\\.)` | |
| ); | |
| const WEBPACK_REQUIRE_IDENTIFIER_REGEXP = new RegExp(RuntimeGlobals.require); | |
| /** | |
| * @typedef {object} DefinePluginHooks | |
| * @property {SyncWaterfallHook<[Record<string, CodeValue>]>} definitions | |
| */ | |
| /** @typedef {Record<string, CodeValue>} Definitions */ | |
| /** @type {WeakMap<Compilation, DefinePluginHooks>} */ | |
| const compilationHooksMap = new WeakMap(); | |
| class DefinePlugin { | |
| /** | |
| * @param {Compilation} compilation the compilation | |
| * @returns {DefinePluginHooks} the attached hooks | |
| */ | |
| static getCompilationHooks(compilation) { | |
| let hooks = compilationHooksMap.get(compilation); | |
| if (hooks === undefined) { | |
| hooks = { | |
| definitions: new SyncWaterfallHook(["definitions"]) | |
| }; | |
| compilationHooksMap.set(compilation, hooks); | |
| } | |
| return hooks; | |
| } | |
| /** | |
| * Create a new define plugin | |
| * @param {Definitions} definitions A map of global object definitions | |
| */ | |
| constructor(definitions) { | |
| /** @type {Definitions} */ | |
| this.definitions = definitions; | |
| } | |
| /** | |
| * @param {GeneratorFn} fn generator function | |
| * @param {true | string[] | RuntimeValueOptions=} options options | |
| * @returns {RuntimeValue} runtime value | |
| */ | |
| static runtimeValue(fn, options) { | |
| return new RuntimeValue(fn, options); | |
| } | |
| /** | |
| * Apply the plugin | |
| * @param {Compiler} compiler the compiler instance | |
| * @returns {void} | |
| */ | |
| apply(compiler) { | |
| compiler.hooks.compilation.tap( | |
| PLUGIN_NAME, | |
| (compilation, { normalModuleFactory }) => { | |
| const definitions = this.definitions; | |
| const hooks = DefinePlugin.getCompilationHooks(compilation); | |
| hooks.definitions.tap(PLUGIN_NAME, (previousDefinitions) => ({ | |
| ...previousDefinitions, | |
| ...definitions | |
| })); | |
| /** | |
| * @type {Map<string, Set<string>>} | |
| */ | |
| const finalByNestedKey = new Map(); | |
| /** | |
| * @type {Map<string, Set<string>>} | |
| */ | |
| const nestedByFinalKey = new Map(); | |
| const logger = compilation.getLogger("webpack.DefinePlugin"); | |
| compilation.dependencyTemplates.set( | |
| ConstDependency, | |
| new ConstDependency.Template() | |
| ); | |
| const { runtimeTemplate } = compilation; | |
| const mainHash = createHash(compilation.outputOptions.hashFunction); | |
| mainHash.update( | |
| /** @type {string} */ | |
| (compilation.valueCacheVersions.get(VALUE_DEP_MAIN)) || "" | |
| ); | |
| /** | |
| * Handler | |
| * @param {JavascriptParser} parser Parser | |
| * @returns {void} | |
| */ | |
| const handler = (parser) => { | |
| /** @type {Set<string>} */ | |
| const hooked = new Set(); | |
| const mainValue = | |
| /** @type {ValueCacheVersion} */ | |
| (compilation.valueCacheVersions.get(VALUE_DEP_MAIN)); | |
| parser.hooks.program.tap(PLUGIN_NAME, () => { | |
| const buildInfo = /** @type {BuildInfo} */ ( | |
| parser.state.module.buildInfo | |
| ); | |
| if (!buildInfo.valueDependencies) { | |
| buildInfo.valueDependencies = new Map(); | |
| } | |
| buildInfo.valueDependencies.set(VALUE_DEP_MAIN, mainValue); | |
| }); | |
| /** | |
| * @param {string} key key | |
| */ | |
| const addValueDependency = (key) => { | |
| const buildInfo = | |
| /** @type {BuildInfo} */ | |
| (parser.state.module.buildInfo); | |
| /** @type {NonNullable<BuildInfo["valueDependencies"]>} */ | |
| (buildInfo.valueDependencies).set( | |
| VALUE_DEP_PREFIX + key, | |
| /** @type {ValueCacheVersion} */ | |
| (compilation.valueCacheVersions.get(VALUE_DEP_PREFIX + key)) | |
| ); | |
| }; | |
| /** | |
| * @template T | |
| * @param {string} key key | |
| * @param {(expression: Expression) => T} fn fn | |
| * @returns {(expression: Expression) => T} result | |
| */ | |
| const withValueDependency = | |
| (key, fn) => | |
| (...args) => { | |
| addValueDependency(key); | |
| return fn(...args); | |
| }; | |
| /** | |
| * Walk definitions | |
| * @param {Definitions} definitions Definitions map | |
| * @param {string} prefix Prefix string | |
| * @returns {void} | |
| */ | |
| const walkDefinitions = (definitions, prefix) => { | |
| for (const key of Object.keys(definitions)) { | |
| const code = definitions[key]; | |
| if ( | |
| code && | |
| typeof code === "object" && | |
| !(code instanceof RuntimeValue) && | |
| !(code instanceof RegExp) | |
| ) { | |
| walkDefinitions( | |
| /** @type {Definitions} */ (code), | |
| `${prefix + key}.` | |
| ); | |
| applyObjectDefine(prefix + key, code); | |
| continue; | |
| } | |
| applyDefineKey(prefix, key); | |
| applyDefine(prefix + key, code); | |
| } | |
| }; | |
| /** | |
| * Apply define key | |
| * @param {string} prefix Prefix | |
| * @param {string} key Key | |
| * @returns {void} | |
| */ | |
| const applyDefineKey = (prefix, key) => { | |
| const splittedKey = key.split("."); | |
| const firstKey = splittedKey[0]; | |
| for (const [i, _] of splittedKey.slice(1).entries()) { | |
| const fullKey = prefix + splittedKey.slice(0, i + 1).join("."); | |
| parser.hooks.canRename.for(fullKey).tap(PLUGIN_NAME, () => { | |
| addValueDependency(key); | |
| if ( | |
| parser.scope.definitions.get(firstKey) instanceof VariableInfo | |
| ) { | |
| return false; | |
| } | |
| return true; | |
| }); | |
| } | |
| if (prefix === "") { | |
| const final = splittedKey[splittedKey.length - 1]; | |
| const nestedSet = nestedByFinalKey.get(final); | |
| if (!nestedSet || nestedSet.size <= 0) return; | |
| for (const nested of /** @type {Set<string>} */ (nestedSet)) { | |
| if (nested && !hooked.has(nested)) { | |
| // only detect the same nested key once | |
| hooked.add(nested); | |
| parser.hooks.collectDestructuringAssignmentProperties.tap( | |
| PLUGIN_NAME, | |
| (expr) => { | |
| const nameInfo = parser.getNameForExpression(expr); | |
| if (nameInfo && nameInfo.name === nested) return true; | |
| } | |
| ); | |
| parser.hooks.expression.for(nested).tap( | |
| { | |
| name: PLUGIN_NAME, | |
| // why 100? Ensures it runs after object define | |
| stage: 100 | |
| }, | |
| (expr) => { | |
| const destructed = | |
| parser.destructuringAssignmentPropertiesFor(expr); | |
| if (destructed === undefined) { | |
| return; | |
| } | |
| /** @type {Definitions} */ | |
| const obj = Object.create(null); | |
| const finalSet = finalByNestedKey.get(nested); | |
| for (const { id } of destructed) { | |
| const fullKey = `${nested}.${id}`; | |
| if ( | |
| !finalSet || | |
| !finalSet.has(id) || | |
| !definitions[fullKey] | |
| ) { | |
| return; | |
| } | |
| obj[id] = definitions[fullKey]; | |
| } | |
| let strCode = stringifyObj( | |
| obj, | |
| parser, | |
| compilation.valueCacheVersions, | |
| key, | |
| runtimeTemplate, | |
| logger, | |
| !parser.isAsiPosition( | |
| /** @type {Range} */ (expr.range)[0] | |
| ), | |
| getObjKeys(destructed) | |
| ); | |
| if (parser.scope.inShorthand) { | |
| strCode = `${parser.scope.inShorthand}:${strCode}`; | |
| } | |
| return toConstantDependency(parser, strCode)(expr); | |
| } | |
| ); | |
| } | |
| } | |
| } | |
| }; | |
| /** | |
| * Apply Code | |
| * @param {string} key Key | |
| * @param {CodeValue} code Code | |
| * @returns {void} | |
| */ | |
| const applyDefine = (key, code) => { | |
| const originalKey = key; | |
| const isTypeof = TYPEOF_OPERATOR_REGEXP.test(key); | |
| if (isTypeof) key = key.replace(TYPEOF_OPERATOR_REGEXP, ""); | |
| let recurse = false; | |
| let recurseTypeof = false; | |
| if (!isTypeof) { | |
| parser.hooks.canRename.for(key).tap(PLUGIN_NAME, () => { | |
| addValueDependency(originalKey); | |
| return true; | |
| }); | |
| parser.hooks.evaluateIdentifier | |
| .for(key) | |
| .tap(PLUGIN_NAME, (expr) => { | |
| /** | |
| * this is needed in case there is a recursion in the DefinePlugin | |
| * to prevent an endless recursion | |
| * e.g.: new DefinePlugin({ | |
| * "a": "b", | |
| * "b": "a" | |
| * }); | |
| */ | |
| if (recurse) return; | |
| addValueDependency(originalKey); | |
| recurse = true; | |
| const res = parser.evaluate( | |
| toCode( | |
| code, | |
| parser, | |
| compilation.valueCacheVersions, | |
| key, | |
| runtimeTemplate, | |
| logger, | |
| null | |
| ) | |
| ); | |
| recurse = false; | |
| res.setRange(/** @type {Range} */ (expr.range)); | |
| return res; | |
| }); | |
| parser.hooks.expression.for(key).tap(PLUGIN_NAME, (expr) => { | |
| addValueDependency(originalKey); | |
| let strCode = toCode( | |
| code, | |
| parser, | |
| compilation.valueCacheVersions, | |
| originalKey, | |
| runtimeTemplate, | |
| logger, | |
| !parser.isAsiPosition(/** @type {Range} */ (expr.range)[0]), | |
| null | |
| ); | |
| if (parser.scope.inShorthand) { | |
| strCode = `${parser.scope.inShorthand}:${strCode}`; | |
| } | |
| if (WEBPACK_REQUIRE_FUNCTION_REGEXP.test(strCode)) { | |
| return toConstantDependency(parser, strCode, [ | |
| RuntimeGlobals.require | |
| ])(expr); | |
| } else if (WEBPACK_REQUIRE_IDENTIFIER_REGEXP.test(strCode)) { | |
| return toConstantDependency(parser, strCode, [ | |
| RuntimeGlobals.requireScope | |
| ])(expr); | |
| } | |
| return toConstantDependency(parser, strCode)(expr); | |
| }); | |
| } | |
| parser.hooks.evaluateTypeof.for(key).tap(PLUGIN_NAME, (expr) => { | |
| /** | |
| * this is needed in case there is a recursion in the DefinePlugin | |
| * to prevent an endless recursion | |
| * e.g.: new DefinePlugin({ | |
| * "typeof a": "typeof b", | |
| * "typeof b": "typeof a" | |
| * }); | |
| */ | |
| if (recurseTypeof) return; | |
| recurseTypeof = true; | |
| addValueDependency(originalKey); | |
| const codeCode = toCode( | |
| code, | |
| parser, | |
| compilation.valueCacheVersions, | |
| originalKey, | |
| runtimeTemplate, | |
| logger, | |
| null | |
| ); | |
| const typeofCode = isTypeof ? codeCode : `typeof (${codeCode})`; | |
| const res = parser.evaluate(typeofCode); | |
| recurseTypeof = false; | |
| res.setRange(/** @type {Range} */ (expr.range)); | |
| return res; | |
| }); | |
| parser.hooks.typeof.for(key).tap(PLUGIN_NAME, (expr) => { | |
| addValueDependency(originalKey); | |
| const codeCode = toCode( | |
| code, | |
| parser, | |
| compilation.valueCacheVersions, | |
| originalKey, | |
| runtimeTemplate, | |
| logger, | |
| null | |
| ); | |
| const typeofCode = isTypeof ? codeCode : `typeof (${codeCode})`; | |
| const res = parser.evaluate(typeofCode); | |
| if (!res.isString()) return; | |
| return toConstantDependency( | |
| parser, | |
| JSON.stringify(res.string) | |
| ).bind(parser)(expr); | |
| }); | |
| }; | |
| /** | |
| * Apply Object | |
| * @param {string} key Key | |
| * @param {object} obj Object | |
| * @returns {void} | |
| */ | |
| const applyObjectDefine = (key, obj) => { | |
| parser.hooks.canRename.for(key).tap(PLUGIN_NAME, () => { | |
| addValueDependency(key); | |
| return true; | |
| }); | |
| parser.hooks.evaluateIdentifier | |
| .for(key) | |
| .tap(PLUGIN_NAME, (expr) => { | |
| addValueDependency(key); | |
| return new BasicEvaluatedExpression() | |
| .setTruthy() | |
| .setSideEffects(false) | |
| .setRange(/** @type {Range} */ (expr.range)); | |
| }); | |
| parser.hooks.evaluateTypeof | |
| .for(key) | |
| .tap( | |
| PLUGIN_NAME, | |
| withValueDependency(key, evaluateToString("object")) | |
| ); | |
| parser.hooks.collectDestructuringAssignmentProperties.tap( | |
| PLUGIN_NAME, | |
| (expr) => { | |
| const nameInfo = parser.getNameForExpression(expr); | |
| if (nameInfo && nameInfo.name === key) return true; | |
| } | |
| ); | |
| parser.hooks.expression.for(key).tap(PLUGIN_NAME, (expr) => { | |
| addValueDependency(key); | |
| let strCode = stringifyObj( | |
| obj, | |
| parser, | |
| compilation.valueCacheVersions, | |
| key, | |
| runtimeTemplate, | |
| logger, | |
| !parser.isAsiPosition(/** @type {Range} */ (expr.range)[0]), | |
| getObjKeys(parser.destructuringAssignmentPropertiesFor(expr)) | |
| ); | |
| if (parser.scope.inShorthand) { | |
| strCode = `${parser.scope.inShorthand}:${strCode}`; | |
| } | |
| if (WEBPACK_REQUIRE_FUNCTION_REGEXP.test(strCode)) { | |
| return toConstantDependency(parser, strCode, [ | |
| RuntimeGlobals.require | |
| ])(expr); | |
| } else if (WEBPACK_REQUIRE_IDENTIFIER_REGEXP.test(strCode)) { | |
| return toConstantDependency(parser, strCode, [ | |
| RuntimeGlobals.requireScope | |
| ])(expr); | |
| } | |
| return toConstantDependency(parser, strCode)(expr); | |
| }); | |
| parser.hooks.typeof | |
| .for(key) | |
| .tap( | |
| PLUGIN_NAME, | |
| withValueDependency( | |
| key, | |
| toConstantDependency(parser, JSON.stringify("object")) | |
| ) | |
| ); | |
| }; | |
| walkDefinitions(definitions, ""); | |
| }; | |
| normalModuleFactory.hooks.parser | |
| .for(JAVASCRIPT_MODULE_TYPE_AUTO) | |
| .tap(PLUGIN_NAME, handler); | |
| normalModuleFactory.hooks.parser | |
| .for(JAVASCRIPT_MODULE_TYPE_DYNAMIC) | |
| .tap(PLUGIN_NAME, handler); | |
| normalModuleFactory.hooks.parser | |
| .for(JAVASCRIPT_MODULE_TYPE_ESM) | |
| .tap(PLUGIN_NAME, handler); | |
| /** | |
| * Walk definitions | |
| * @param {Definitions} definitions Definitions map | |
| * @param {string} prefix Prefix string | |
| * @returns {void} | |
| */ | |
| const walkDefinitionsForValues = (definitions, prefix) => { | |
| for (const key of Object.keys(definitions)) { | |
| const code = definitions[key]; | |
| const version = /** @type {string} */ (toCacheVersion(code)); | |
| const name = VALUE_DEP_PREFIX + prefix + key; | |
| mainHash.update(`|${prefix}${key}`); | |
| const oldVersion = compilation.valueCacheVersions.get(name); | |
| if (oldVersion === undefined) { | |
| compilation.valueCacheVersions.set(name, version); | |
| } else if (oldVersion !== version) { | |
| const warning = new WebpackError( | |
| `${PLUGIN_NAME}\nConflicting values for '${prefix + key}'` | |
| ); | |
| warning.details = `'${oldVersion}' !== '${version}'`; | |
| warning.hideStack = true; | |
| compilation.warnings.push(warning); | |
| } | |
| if ( | |
| code && | |
| typeof code === "object" && | |
| !(code instanceof RuntimeValue) && | |
| !(code instanceof RegExp) | |
| ) { | |
| walkDefinitionsForValues( | |
| /** @type {Definitions} */ (code), | |
| `${prefix + key}.` | |
| ); | |
| } | |
| } | |
| }; | |
| /** | |
| * @param {Definitions} definitions Definitions map | |
| * @returns {void} | |
| */ | |
| const walkDefinitionsForKeys = (definitions) => { | |
| /** | |
| * @param {Map<string, Set<string>>} map Map | |
| * @param {string} key key | |
| * @param {string} value v | |
| * @returns {void} | |
| */ | |
| const addToMap = (map, key, value) => { | |
| if (map.has(key)) { | |
| /** @type {Set<string>} */ | |
| (map.get(key)).add(value); | |
| } else { | |
| map.set(key, new Set([value])); | |
| } | |
| }; | |
| for (const key of Object.keys(definitions)) { | |
| const code = definitions[key]; | |
| if ( | |
| !code || | |
| typeof code === "object" || | |
| TYPEOF_OPERATOR_REGEXP.test(key) | |
| ) { | |
| continue; | |
| } | |
| const idx = key.lastIndexOf("."); | |
| if (idx <= 0 || idx >= key.length - 1) { | |
| continue; | |
| } | |
| const nested = key.slice(0, idx); | |
| const final = key.slice(idx + 1); | |
| addToMap(finalByNestedKey, nested, final); | |
| addToMap(nestedByFinalKey, final, nested); | |
| } | |
| }; | |
| walkDefinitionsForKeys(definitions); | |
| walkDefinitionsForValues(definitions, ""); | |
| compilation.valueCacheVersions.set( | |
| VALUE_DEP_MAIN, | |
| mainHash.digest("hex").slice(0, 8) | |
| ); | |
| } | |
| ); | |
| } | |
| } | |
| module.exports = DefinePlugin; | |
Xet Storage Details
- Size:
- 25 kB
- Xet hash:
- 7258eb8f2e42ee626bcac3797ed0048a13eda54ed44edf6fe3e0d7b5600a9333
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.