[79a0317] | 1 | /*
|
---|
| 2 | MIT License http://www.opensource.org/licenses/mit-license.php
|
---|
| 3 | Author Tobias Koppers @sokra
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | "use strict";
|
---|
| 7 |
|
---|
| 8 | const {
|
---|
| 9 | JAVASCRIPT_MODULE_TYPE_AUTO,
|
---|
| 10 | JAVASCRIPT_MODULE_TYPE_ESM,
|
---|
| 11 | JAVASCRIPT_MODULE_TYPE_DYNAMIC
|
---|
| 12 | } = require("./ModuleTypeConstants");
|
---|
| 13 | const RuntimeGlobals = require("./RuntimeGlobals");
|
---|
| 14 | const WebpackError = require("./WebpackError");
|
---|
| 15 | const ConstDependency = require("./dependencies/ConstDependency");
|
---|
| 16 | const BasicEvaluatedExpression = require("./javascript/BasicEvaluatedExpression");
|
---|
| 17 | const { VariableInfo } = require("./javascript/JavascriptParser");
|
---|
| 18 | const {
|
---|
| 19 | evaluateToString,
|
---|
| 20 | toConstantDependency
|
---|
| 21 | } = require("./javascript/JavascriptParserHelpers");
|
---|
| 22 | const createHash = require("./util/createHash");
|
---|
| 23 |
|
---|
| 24 | /** @typedef {import("estree").Expression} Expression */
|
---|
| 25 | /** @typedef {import("./Compiler")} Compiler */
|
---|
| 26 | /** @typedef {import("./Module").BuildInfo} BuildInfo */
|
---|
| 27 | /** @typedef {import("./Module").ValueCacheVersions} ValueCacheVersions */
|
---|
| 28 | /** @typedef {import("./NormalModule")} NormalModule */
|
---|
| 29 | /** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
|
---|
| 30 | /** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */
|
---|
| 31 | /** @typedef {import("./javascript/JavascriptParser").DestructuringAssignmentProperty} DestructuringAssignmentProperty */
|
---|
| 32 | /** @typedef {import("./javascript/JavascriptParser").Range} Range */
|
---|
| 33 | /** @typedef {import("./logging/Logger").Logger} Logger */
|
---|
| 34 | /** @typedef {import("./util/createHash").Algorithm} Algorithm */
|
---|
| 35 |
|
---|
| 36 | /** @typedef {null|undefined|RegExp|Function|string|number|boolean|bigint|undefined} CodeValuePrimitive */
|
---|
| 37 | /** @typedef {RecursiveArrayOrRecord<CodeValuePrimitive|RuntimeValue>} CodeValue */
|
---|
| 38 |
|
---|
| 39 | /**
|
---|
| 40 | * @typedef {object} RuntimeValueOptions
|
---|
| 41 | * @property {string[]=} fileDependencies
|
---|
| 42 | * @property {string[]=} contextDependencies
|
---|
| 43 | * @property {string[]=} missingDependencies
|
---|
| 44 | * @property {string[]=} buildDependencies
|
---|
| 45 | * @property {string|function(): string=} version
|
---|
| 46 | */
|
---|
| 47 |
|
---|
| 48 | /** @typedef {string | Set<string>} ValueCacheVersion */
|
---|
| 49 | /** @typedef {function({ module: NormalModule, key: string, readonly version: ValueCacheVersion }): CodeValuePrimitive} GeneratorFn */
|
---|
| 50 |
|
---|
| 51 | class RuntimeValue {
|
---|
| 52 | /**
|
---|
| 53 | * @param {GeneratorFn} fn generator function
|
---|
| 54 | * @param {true | string[] | RuntimeValueOptions=} options options
|
---|
| 55 | */
|
---|
| 56 | constructor(fn, options) {
|
---|
| 57 | this.fn = fn;
|
---|
| 58 | if (Array.isArray(options)) {
|
---|
| 59 | options = {
|
---|
| 60 | fileDependencies: options
|
---|
| 61 | };
|
---|
| 62 | }
|
---|
| 63 | this.options = options || {};
|
---|
| 64 | }
|
---|
| 65 |
|
---|
| 66 | get fileDependencies() {
|
---|
| 67 | return this.options === true ? true : this.options.fileDependencies;
|
---|
| 68 | }
|
---|
| 69 |
|
---|
| 70 | /**
|
---|
| 71 | * @param {JavascriptParser} parser the parser
|
---|
| 72 | * @param {ValueCacheVersions} valueCacheVersions valueCacheVersions
|
---|
| 73 | * @param {string} key the defined key
|
---|
| 74 | * @returns {CodeValuePrimitive} code
|
---|
| 75 | */
|
---|
| 76 | exec(parser, valueCacheVersions, key) {
|
---|
| 77 | const buildInfo = /** @type {BuildInfo} */ (parser.state.module.buildInfo);
|
---|
| 78 | if (this.options === true) {
|
---|
| 79 | buildInfo.cacheable = false;
|
---|
| 80 | } else {
|
---|
| 81 | if (this.options.fileDependencies) {
|
---|
| 82 | for (const dep of this.options.fileDependencies) {
|
---|
| 83 | /** @type {NonNullable<BuildInfo["fileDependencies"]>} */
|
---|
| 84 | (buildInfo.fileDependencies).add(dep);
|
---|
| 85 | }
|
---|
| 86 | }
|
---|
| 87 | if (this.options.contextDependencies) {
|
---|
| 88 | for (const dep of this.options.contextDependencies) {
|
---|
| 89 | /** @type {NonNullable<BuildInfo["contextDependencies"]>} */
|
---|
| 90 | (buildInfo.contextDependencies).add(dep);
|
---|
| 91 | }
|
---|
| 92 | }
|
---|
| 93 | if (this.options.missingDependencies) {
|
---|
| 94 | for (const dep of this.options.missingDependencies) {
|
---|
| 95 | /** @type {NonNullable<BuildInfo["missingDependencies"]>} */
|
---|
| 96 | (buildInfo.missingDependencies).add(dep);
|
---|
| 97 | }
|
---|
| 98 | }
|
---|
| 99 | if (this.options.buildDependencies) {
|
---|
| 100 | for (const dep of this.options.buildDependencies) {
|
---|
| 101 | /** @type {NonNullable<BuildInfo["buildDependencies"]>} */
|
---|
| 102 | (buildInfo.buildDependencies).add(dep);
|
---|
| 103 | }
|
---|
| 104 | }
|
---|
| 105 | }
|
---|
| 106 |
|
---|
| 107 | return this.fn({
|
---|
| 108 | module: parser.state.module,
|
---|
| 109 | key,
|
---|
| 110 | get version() {
|
---|
| 111 | return /** @type {ValueCacheVersion} */ (
|
---|
| 112 | valueCacheVersions.get(VALUE_DEP_PREFIX + key)
|
---|
| 113 | );
|
---|
| 114 | }
|
---|
| 115 | });
|
---|
| 116 | }
|
---|
| 117 |
|
---|
| 118 | getCacheVersion() {
|
---|
| 119 | return this.options === true
|
---|
| 120 | ? undefined
|
---|
| 121 | : (typeof this.options.version === "function"
|
---|
| 122 | ? this.options.version()
|
---|
| 123 | : this.options.version) || "unset";
|
---|
| 124 | }
|
---|
| 125 | }
|
---|
| 126 |
|
---|
| 127 | /**
|
---|
| 128 | * @param {Set<DestructuringAssignmentProperty> | undefined} properties properties
|
---|
| 129 | * @returns {Set<string> | undefined} used keys
|
---|
| 130 | */
|
---|
| 131 | function getObjKeys(properties) {
|
---|
| 132 | if (!properties) return;
|
---|
| 133 | return new Set([...properties].map(p => p.id));
|
---|
| 134 | }
|
---|
| 135 |
|
---|
| 136 | /** @typedef {Set<string> | null} ObjKeys */
|
---|
| 137 | /** @typedef {boolean | undefined | null} AsiSafe */
|
---|
| 138 |
|
---|
| 139 | /**
|
---|
| 140 | * @param {any[]|{[k: string]: any}} obj obj
|
---|
| 141 | * @param {JavascriptParser} parser Parser
|
---|
| 142 | * @param {ValueCacheVersions} valueCacheVersions valueCacheVersions
|
---|
| 143 | * @param {string} key the defined key
|
---|
| 144 | * @param {RuntimeTemplate} runtimeTemplate the runtime template
|
---|
| 145 | * @param {Logger} logger the logger object
|
---|
| 146 | * @param {AsiSafe=} asiSafe asi safe (undefined: unknown, null: unneeded)
|
---|
| 147 | * @param {ObjKeys=} objKeys used keys
|
---|
| 148 | * @returns {string} code converted to string that evaluates
|
---|
| 149 | */
|
---|
| 150 | const stringifyObj = (
|
---|
| 151 | obj,
|
---|
| 152 | parser,
|
---|
| 153 | valueCacheVersions,
|
---|
| 154 | key,
|
---|
| 155 | runtimeTemplate,
|
---|
| 156 | logger,
|
---|
| 157 | asiSafe,
|
---|
| 158 | objKeys
|
---|
| 159 | ) => {
|
---|
| 160 | let code;
|
---|
| 161 | const arr = Array.isArray(obj);
|
---|
| 162 | if (arr) {
|
---|
| 163 | code = `[${
|
---|
| 164 | /** @type {any[]} */ (obj)
|
---|
| 165 | .map(code =>
|
---|
| 166 | toCode(
|
---|
| 167 | code,
|
---|
| 168 | parser,
|
---|
| 169 | valueCacheVersions,
|
---|
| 170 | key,
|
---|
| 171 | runtimeTemplate,
|
---|
| 172 | logger,
|
---|
| 173 | null
|
---|
| 174 | )
|
---|
| 175 | )
|
---|
| 176 | .join(",")
|
---|
| 177 | }]`;
|
---|
| 178 | } else {
|
---|
| 179 | let keys = Object.keys(obj);
|
---|
| 180 | if (objKeys) {
|
---|
| 181 | keys = objKeys.size === 0 ? [] : keys.filter(k => objKeys.has(k));
|
---|
| 182 | }
|
---|
| 183 | code = `{${keys
|
---|
| 184 | .map(key => {
|
---|
| 185 | const code = /** @type {{[k: string]: any}} */ (obj)[key];
|
---|
| 186 | return `${JSON.stringify(key)}:${toCode(
|
---|
| 187 | code,
|
---|
| 188 | parser,
|
---|
| 189 | valueCacheVersions,
|
---|
| 190 | key,
|
---|
| 191 | runtimeTemplate,
|
---|
| 192 | logger,
|
---|
| 193 | null
|
---|
| 194 | )}`;
|
---|
| 195 | })
|
---|
| 196 | .join(",")}}`;
|
---|
| 197 | }
|
---|
| 198 |
|
---|
| 199 | switch (asiSafe) {
|
---|
| 200 | case null:
|
---|
| 201 | return code;
|
---|
| 202 | case true:
|
---|
| 203 | return arr ? code : `(${code})`;
|
---|
| 204 | case false:
|
---|
| 205 | return arr ? `;${code}` : `;(${code})`;
|
---|
| 206 | default:
|
---|
| 207 | return `/*#__PURE__*/Object(${code})`;
|
---|
| 208 | }
|
---|
| 209 | };
|
---|
| 210 |
|
---|
| 211 | /**
|
---|
| 212 | * Convert code to a string that evaluates
|
---|
| 213 | * @param {CodeValue} code Code to evaluate
|
---|
| 214 | * @param {JavascriptParser} parser Parser
|
---|
| 215 | * @param {ValueCacheVersions} valueCacheVersions valueCacheVersions
|
---|
| 216 | * @param {string} key the defined key
|
---|
| 217 | * @param {RuntimeTemplate} runtimeTemplate the runtime template
|
---|
| 218 | * @param {Logger} logger the logger object
|
---|
| 219 | * @param {boolean | undefined | null=} asiSafe asi safe (undefined: unknown, null: unneeded)
|
---|
| 220 | * @param {ObjKeys=} objKeys used keys
|
---|
| 221 | * @returns {string} code converted to string that evaluates
|
---|
| 222 | */
|
---|
| 223 | const toCode = (
|
---|
| 224 | code,
|
---|
| 225 | parser,
|
---|
| 226 | valueCacheVersions,
|
---|
| 227 | key,
|
---|
| 228 | runtimeTemplate,
|
---|
| 229 | logger,
|
---|
| 230 | asiSafe,
|
---|
| 231 | objKeys
|
---|
| 232 | ) => {
|
---|
| 233 | const transformToCode = () => {
|
---|
| 234 | if (code === null) {
|
---|
| 235 | return "null";
|
---|
| 236 | }
|
---|
| 237 | if (code === undefined) {
|
---|
| 238 | return "undefined";
|
---|
| 239 | }
|
---|
| 240 | if (Object.is(code, -0)) {
|
---|
| 241 | return "-0";
|
---|
| 242 | }
|
---|
| 243 | if (code instanceof RuntimeValue) {
|
---|
| 244 | return toCode(
|
---|
| 245 | code.exec(parser, valueCacheVersions, key),
|
---|
| 246 | parser,
|
---|
| 247 | valueCacheVersions,
|
---|
| 248 | key,
|
---|
| 249 | runtimeTemplate,
|
---|
| 250 | logger,
|
---|
| 251 | asiSafe
|
---|
| 252 | );
|
---|
| 253 | }
|
---|
| 254 | if (code instanceof RegExp && code.toString) {
|
---|
| 255 | return code.toString();
|
---|
| 256 | }
|
---|
| 257 | if (typeof code === "function" && code.toString) {
|
---|
| 258 | return `(${code.toString()})`;
|
---|
| 259 | }
|
---|
| 260 | if (typeof code === "object") {
|
---|
| 261 | return stringifyObj(
|
---|
| 262 | code,
|
---|
| 263 | parser,
|
---|
| 264 | valueCacheVersions,
|
---|
| 265 | key,
|
---|
| 266 | runtimeTemplate,
|
---|
| 267 | logger,
|
---|
| 268 | asiSafe,
|
---|
| 269 | objKeys
|
---|
| 270 | );
|
---|
| 271 | }
|
---|
| 272 | if (typeof code === "bigint") {
|
---|
| 273 | return runtimeTemplate.supportsBigIntLiteral()
|
---|
| 274 | ? `${code}n`
|
---|
| 275 | : `BigInt("${code}")`;
|
---|
| 276 | }
|
---|
| 277 | return `${code}`;
|
---|
| 278 | };
|
---|
| 279 |
|
---|
| 280 | const strCode = transformToCode();
|
---|
| 281 |
|
---|
| 282 | logger.debug(`Replaced "${key}" with "${strCode}"`);
|
---|
| 283 |
|
---|
| 284 | return strCode;
|
---|
| 285 | };
|
---|
| 286 |
|
---|
| 287 | /**
|
---|
| 288 | * @param {CodeValue} code code
|
---|
| 289 | * @returns {string | undefined} result
|
---|
| 290 | */
|
---|
| 291 | const toCacheVersion = code => {
|
---|
| 292 | if (code === null) {
|
---|
| 293 | return "null";
|
---|
| 294 | }
|
---|
| 295 | if (code === undefined) {
|
---|
| 296 | return "undefined";
|
---|
| 297 | }
|
---|
| 298 | if (Object.is(code, -0)) {
|
---|
| 299 | return "-0";
|
---|
| 300 | }
|
---|
| 301 | if (code instanceof RuntimeValue) {
|
---|
| 302 | return code.getCacheVersion();
|
---|
| 303 | }
|
---|
| 304 | if (code instanceof RegExp && code.toString) {
|
---|
| 305 | return code.toString();
|
---|
| 306 | }
|
---|
| 307 | if (typeof code === "function" && code.toString) {
|
---|
| 308 | return `(${code.toString()})`;
|
---|
| 309 | }
|
---|
| 310 | if (typeof code === "object") {
|
---|
| 311 | const items = Object.keys(code).map(key => ({
|
---|
| 312 | key,
|
---|
| 313 | value: toCacheVersion(/** @type {Record<string, any>} */ (code)[key])
|
---|
| 314 | }));
|
---|
| 315 | if (items.some(({ value }) => value === undefined)) return;
|
---|
| 316 | return `{${items.map(({ key, value }) => `${key}: ${value}`).join(", ")}}`;
|
---|
| 317 | }
|
---|
| 318 | if (typeof code === "bigint") {
|
---|
| 319 | return `${code}n`;
|
---|
| 320 | }
|
---|
| 321 | return `${code}`;
|
---|
| 322 | };
|
---|
| 323 |
|
---|
| 324 | const PLUGIN_NAME = "DefinePlugin";
|
---|
| 325 | const VALUE_DEP_PREFIX = `webpack/${PLUGIN_NAME} `;
|
---|
| 326 | const VALUE_DEP_MAIN = `webpack/${PLUGIN_NAME}_hash`;
|
---|
| 327 | const TYPEOF_OPERATOR_REGEXP = /^typeof\s+/;
|
---|
| 328 | const WEBPACK_REQUIRE_FUNCTION_REGEXP = new RegExp(
|
---|
| 329 | `${RuntimeGlobals.require}\\s*(!?\\.)`
|
---|
| 330 | );
|
---|
| 331 | const WEBPACK_REQUIRE_IDENTIFIER_REGEXP = new RegExp(RuntimeGlobals.require);
|
---|
| 332 |
|
---|
| 333 | class DefinePlugin {
|
---|
| 334 | /**
|
---|
| 335 | * Create a new define plugin
|
---|
| 336 | * @param {Record<string, CodeValue>} definitions A map of global object definitions
|
---|
| 337 | */
|
---|
| 338 | constructor(definitions) {
|
---|
| 339 | this.definitions = definitions;
|
---|
| 340 | }
|
---|
| 341 |
|
---|
| 342 | /**
|
---|
| 343 | * @param {GeneratorFn} fn generator function
|
---|
| 344 | * @param {true | string[] | RuntimeValueOptions=} options options
|
---|
| 345 | * @returns {RuntimeValue} runtime value
|
---|
| 346 | */
|
---|
| 347 | static runtimeValue(fn, options) {
|
---|
| 348 | return new RuntimeValue(fn, options);
|
---|
| 349 | }
|
---|
| 350 |
|
---|
| 351 | /**
|
---|
| 352 | * Apply the plugin
|
---|
| 353 | * @param {Compiler} compiler the compiler instance
|
---|
| 354 | * @returns {void}
|
---|
| 355 | */
|
---|
| 356 | apply(compiler) {
|
---|
| 357 | const definitions = this.definitions;
|
---|
| 358 | compiler.hooks.compilation.tap(
|
---|
| 359 | PLUGIN_NAME,
|
---|
| 360 | (compilation, { normalModuleFactory }) => {
|
---|
| 361 | const logger = compilation.getLogger("webpack.DefinePlugin");
|
---|
| 362 | compilation.dependencyTemplates.set(
|
---|
| 363 | ConstDependency,
|
---|
| 364 | new ConstDependency.Template()
|
---|
| 365 | );
|
---|
| 366 | const { runtimeTemplate } = compilation;
|
---|
| 367 |
|
---|
| 368 | const mainHash = createHash(
|
---|
| 369 | /** @type {Algorithm} */
|
---|
| 370 | (compilation.outputOptions.hashFunction)
|
---|
| 371 | );
|
---|
| 372 | mainHash.update(
|
---|
| 373 | /** @type {string} */
|
---|
| 374 | (compilation.valueCacheVersions.get(VALUE_DEP_MAIN)) || ""
|
---|
| 375 | );
|
---|
| 376 |
|
---|
| 377 | /**
|
---|
| 378 | * Handler
|
---|
| 379 | * @param {JavascriptParser} parser Parser
|
---|
| 380 | * @returns {void}
|
---|
| 381 | */
|
---|
| 382 | const handler = parser => {
|
---|
| 383 | const mainValue =
|
---|
| 384 | /** @type {ValueCacheVersion} */
|
---|
| 385 | (compilation.valueCacheVersions.get(VALUE_DEP_MAIN));
|
---|
| 386 | parser.hooks.program.tap(PLUGIN_NAME, () => {
|
---|
| 387 | const buildInfo = /** @type {BuildInfo} */ (
|
---|
| 388 | parser.state.module.buildInfo
|
---|
| 389 | );
|
---|
| 390 | if (!buildInfo.valueDependencies)
|
---|
| 391 | buildInfo.valueDependencies = new Map();
|
---|
| 392 | buildInfo.valueDependencies.set(VALUE_DEP_MAIN, mainValue);
|
---|
| 393 | });
|
---|
| 394 |
|
---|
| 395 | /**
|
---|
| 396 | * @param {string} key key
|
---|
| 397 | */
|
---|
| 398 | const addValueDependency = key => {
|
---|
| 399 | const buildInfo =
|
---|
| 400 | /** @type {BuildInfo} */
|
---|
| 401 | (parser.state.module.buildInfo);
|
---|
| 402 | /** @type {NonNullable<BuildInfo["valueDependencies"]>} */
|
---|
| 403 | (buildInfo.valueDependencies).set(
|
---|
| 404 | VALUE_DEP_PREFIX + key,
|
---|
| 405 | /** @type {ValueCacheVersion} */
|
---|
| 406 | (compilation.valueCacheVersions.get(VALUE_DEP_PREFIX + key))
|
---|
| 407 | );
|
---|
| 408 | };
|
---|
| 409 |
|
---|
| 410 | /**
|
---|
| 411 | * @template {Function} T
|
---|
| 412 | * @param {string} key key
|
---|
| 413 | * @param {T} fn fn
|
---|
| 414 | * @returns {function(TODO): TODO} result
|
---|
| 415 | */
|
---|
| 416 | const withValueDependency =
|
---|
| 417 | (key, fn) =>
|
---|
| 418 | (...args) => {
|
---|
| 419 | addValueDependency(key);
|
---|
| 420 | return fn(...args);
|
---|
| 421 | };
|
---|
| 422 |
|
---|
| 423 | /**
|
---|
| 424 | * Walk definitions
|
---|
| 425 | * @param {Record<string, CodeValue>} definitions Definitions map
|
---|
| 426 | * @param {string} prefix Prefix string
|
---|
| 427 | * @returns {void}
|
---|
| 428 | */
|
---|
| 429 | const walkDefinitions = (definitions, prefix) => {
|
---|
| 430 | for (const key of Object.keys(definitions)) {
|
---|
| 431 | const code = definitions[key];
|
---|
| 432 | if (
|
---|
| 433 | code &&
|
---|
| 434 | typeof code === "object" &&
|
---|
| 435 | !(code instanceof RuntimeValue) &&
|
---|
| 436 | !(code instanceof RegExp)
|
---|
| 437 | ) {
|
---|
| 438 | walkDefinitions(
|
---|
| 439 | /** @type {Record<string, CodeValue>} */ (code),
|
---|
| 440 | `${prefix + key}.`
|
---|
| 441 | );
|
---|
| 442 | applyObjectDefine(prefix + key, code);
|
---|
| 443 | continue;
|
---|
| 444 | }
|
---|
| 445 | applyDefineKey(prefix, key);
|
---|
| 446 | applyDefine(prefix + key, code);
|
---|
| 447 | }
|
---|
| 448 | };
|
---|
| 449 |
|
---|
| 450 | /**
|
---|
| 451 | * Apply define key
|
---|
| 452 | * @param {string} prefix Prefix
|
---|
| 453 | * @param {string} key Key
|
---|
| 454 | * @returns {void}
|
---|
| 455 | */
|
---|
| 456 | const applyDefineKey = (prefix, key) => {
|
---|
| 457 | const splittedKey = key.split(".");
|
---|
| 458 | const firstKey = splittedKey[0];
|
---|
| 459 | for (const [i, _] of splittedKey.slice(1).entries()) {
|
---|
| 460 | const fullKey = prefix + splittedKey.slice(0, i + 1).join(".");
|
---|
| 461 | parser.hooks.canRename.for(fullKey).tap(PLUGIN_NAME, () => {
|
---|
| 462 | addValueDependency(key);
|
---|
| 463 | if (
|
---|
| 464 | parser.scope.definitions.get(firstKey) instanceof VariableInfo
|
---|
| 465 | ) {
|
---|
| 466 | return false;
|
---|
| 467 | }
|
---|
| 468 | return true;
|
---|
| 469 | });
|
---|
| 470 | }
|
---|
| 471 | };
|
---|
| 472 |
|
---|
| 473 | /**
|
---|
| 474 | * Apply Code
|
---|
| 475 | * @param {string} key Key
|
---|
| 476 | * @param {CodeValue} code Code
|
---|
| 477 | * @returns {void}
|
---|
| 478 | */
|
---|
| 479 | const applyDefine = (key, code) => {
|
---|
| 480 | const originalKey = key;
|
---|
| 481 | const isTypeof = TYPEOF_OPERATOR_REGEXP.test(key);
|
---|
| 482 | if (isTypeof) key = key.replace(TYPEOF_OPERATOR_REGEXP, "");
|
---|
| 483 | let recurse = false;
|
---|
| 484 | let recurseTypeof = false;
|
---|
| 485 | if (!isTypeof) {
|
---|
| 486 | parser.hooks.canRename.for(key).tap(PLUGIN_NAME, () => {
|
---|
| 487 | addValueDependency(originalKey);
|
---|
| 488 | return true;
|
---|
| 489 | });
|
---|
| 490 | parser.hooks.evaluateIdentifier
|
---|
| 491 | .for(key)
|
---|
| 492 | .tap(PLUGIN_NAME, expr => {
|
---|
| 493 | /**
|
---|
| 494 | * this is needed in case there is a recursion in the DefinePlugin
|
---|
| 495 | * to prevent an endless recursion
|
---|
| 496 | * e.g.: new DefinePlugin({
|
---|
| 497 | * "a": "b",
|
---|
| 498 | * "b": "a"
|
---|
| 499 | * });
|
---|
| 500 | */
|
---|
| 501 | if (recurse) return;
|
---|
| 502 | addValueDependency(originalKey);
|
---|
| 503 | recurse = true;
|
---|
| 504 | const res = parser.evaluate(
|
---|
| 505 | toCode(
|
---|
| 506 | code,
|
---|
| 507 | parser,
|
---|
| 508 | compilation.valueCacheVersions,
|
---|
| 509 | key,
|
---|
| 510 | runtimeTemplate,
|
---|
| 511 | logger,
|
---|
| 512 | null
|
---|
| 513 | )
|
---|
| 514 | );
|
---|
| 515 | recurse = false;
|
---|
| 516 | res.setRange(/** @type {Range} */ (expr.range));
|
---|
| 517 | return res;
|
---|
| 518 | });
|
---|
| 519 | parser.hooks.expression.for(key).tap(PLUGIN_NAME, expr => {
|
---|
| 520 | addValueDependency(originalKey);
|
---|
| 521 | let strCode = toCode(
|
---|
| 522 | code,
|
---|
| 523 | parser,
|
---|
| 524 | compilation.valueCacheVersions,
|
---|
| 525 | originalKey,
|
---|
| 526 | runtimeTemplate,
|
---|
| 527 | logger,
|
---|
| 528 | !parser.isAsiPosition(/** @type {Range} */ (expr.range)[0]),
|
---|
| 529 | null
|
---|
| 530 | );
|
---|
| 531 |
|
---|
| 532 | if (parser.scope.inShorthand) {
|
---|
| 533 | strCode = `${parser.scope.inShorthand}:${strCode}`;
|
---|
| 534 | }
|
---|
| 535 |
|
---|
| 536 | if (WEBPACK_REQUIRE_FUNCTION_REGEXP.test(strCode)) {
|
---|
| 537 | return toConstantDependency(parser, strCode, [
|
---|
| 538 | RuntimeGlobals.require
|
---|
| 539 | ])(expr);
|
---|
| 540 | } else if (WEBPACK_REQUIRE_IDENTIFIER_REGEXP.test(strCode)) {
|
---|
| 541 | return toConstantDependency(parser, strCode, [
|
---|
| 542 | RuntimeGlobals.requireScope
|
---|
| 543 | ])(expr);
|
---|
| 544 | }
|
---|
| 545 | return toConstantDependency(parser, strCode)(expr);
|
---|
| 546 | });
|
---|
| 547 | }
|
---|
| 548 | parser.hooks.evaluateTypeof.for(key).tap(PLUGIN_NAME, expr => {
|
---|
| 549 | /**
|
---|
| 550 | * this is needed in case there is a recursion in the DefinePlugin
|
---|
| 551 | * to prevent an endless recursion
|
---|
| 552 | * e.g.: new DefinePlugin({
|
---|
| 553 | * "typeof a": "typeof b",
|
---|
| 554 | * "typeof b": "typeof a"
|
---|
| 555 | * });
|
---|
| 556 | */
|
---|
| 557 | if (recurseTypeof) return;
|
---|
| 558 | recurseTypeof = true;
|
---|
| 559 | addValueDependency(originalKey);
|
---|
| 560 | const codeCode = toCode(
|
---|
| 561 | code,
|
---|
| 562 | parser,
|
---|
| 563 | compilation.valueCacheVersions,
|
---|
| 564 | originalKey,
|
---|
| 565 | runtimeTemplate,
|
---|
| 566 | logger,
|
---|
| 567 | null
|
---|
| 568 | );
|
---|
| 569 | const typeofCode = isTypeof ? codeCode : `typeof (${codeCode})`;
|
---|
| 570 | const res = parser.evaluate(typeofCode);
|
---|
| 571 | recurseTypeof = false;
|
---|
| 572 | res.setRange(/** @type {Range} */ (expr.range));
|
---|
| 573 | return res;
|
---|
| 574 | });
|
---|
| 575 | parser.hooks.typeof.for(key).tap(PLUGIN_NAME, expr => {
|
---|
| 576 | addValueDependency(originalKey);
|
---|
| 577 | const codeCode = toCode(
|
---|
| 578 | code,
|
---|
| 579 | parser,
|
---|
| 580 | compilation.valueCacheVersions,
|
---|
| 581 | originalKey,
|
---|
| 582 | runtimeTemplate,
|
---|
| 583 | logger,
|
---|
| 584 | null
|
---|
| 585 | );
|
---|
| 586 | const typeofCode = isTypeof ? codeCode : `typeof (${codeCode})`;
|
---|
| 587 | const res = parser.evaluate(typeofCode);
|
---|
| 588 | if (!res.isString()) return;
|
---|
| 589 | return toConstantDependency(
|
---|
| 590 | parser,
|
---|
| 591 | JSON.stringify(res.string)
|
---|
| 592 | ).bind(parser)(expr);
|
---|
| 593 | });
|
---|
| 594 | };
|
---|
| 595 |
|
---|
| 596 | /**
|
---|
| 597 | * Apply Object
|
---|
| 598 | * @param {string} key Key
|
---|
| 599 | * @param {object} obj Object
|
---|
| 600 | * @returns {void}
|
---|
| 601 | */
|
---|
| 602 | const applyObjectDefine = (key, obj) => {
|
---|
| 603 | parser.hooks.canRename.for(key).tap(PLUGIN_NAME, () => {
|
---|
| 604 | addValueDependency(key);
|
---|
| 605 | return true;
|
---|
| 606 | });
|
---|
| 607 | parser.hooks.evaluateIdentifier.for(key).tap(PLUGIN_NAME, expr => {
|
---|
| 608 | addValueDependency(key);
|
---|
| 609 | return new BasicEvaluatedExpression()
|
---|
| 610 | .setTruthy()
|
---|
| 611 | .setSideEffects(false)
|
---|
| 612 | .setRange(/** @type {Range} */ (expr.range));
|
---|
| 613 | });
|
---|
| 614 | parser.hooks.evaluateTypeof
|
---|
| 615 | .for(key)
|
---|
| 616 | .tap(
|
---|
| 617 | PLUGIN_NAME,
|
---|
| 618 | withValueDependency(key, evaluateToString("object"))
|
---|
| 619 | );
|
---|
| 620 | parser.hooks.expression.for(key).tap(PLUGIN_NAME, expr => {
|
---|
| 621 | addValueDependency(key);
|
---|
| 622 | let strCode = stringifyObj(
|
---|
| 623 | obj,
|
---|
| 624 | parser,
|
---|
| 625 | compilation.valueCacheVersions,
|
---|
| 626 | key,
|
---|
| 627 | runtimeTemplate,
|
---|
| 628 | logger,
|
---|
| 629 | !parser.isAsiPosition(/** @type {Range} */ (expr.range)[0]),
|
---|
| 630 | getObjKeys(parser.destructuringAssignmentPropertiesFor(expr))
|
---|
| 631 | );
|
---|
| 632 |
|
---|
| 633 | if (parser.scope.inShorthand) {
|
---|
| 634 | strCode = `${parser.scope.inShorthand}:${strCode}`;
|
---|
| 635 | }
|
---|
| 636 |
|
---|
| 637 | if (WEBPACK_REQUIRE_FUNCTION_REGEXP.test(strCode)) {
|
---|
| 638 | return toConstantDependency(parser, strCode, [
|
---|
| 639 | RuntimeGlobals.require
|
---|
| 640 | ])(expr);
|
---|
| 641 | } else if (WEBPACK_REQUIRE_IDENTIFIER_REGEXP.test(strCode)) {
|
---|
| 642 | return toConstantDependency(parser, strCode, [
|
---|
| 643 | RuntimeGlobals.requireScope
|
---|
| 644 | ])(expr);
|
---|
| 645 | }
|
---|
| 646 | return toConstantDependency(parser, strCode)(expr);
|
---|
| 647 | });
|
---|
| 648 | parser.hooks.typeof
|
---|
| 649 | .for(key)
|
---|
| 650 | .tap(
|
---|
| 651 | PLUGIN_NAME,
|
---|
| 652 | withValueDependency(
|
---|
| 653 | key,
|
---|
| 654 | toConstantDependency(parser, JSON.stringify("object"))
|
---|
| 655 | )
|
---|
| 656 | );
|
---|
| 657 | };
|
---|
| 658 |
|
---|
| 659 | walkDefinitions(definitions, "");
|
---|
| 660 | };
|
---|
| 661 |
|
---|
| 662 | normalModuleFactory.hooks.parser
|
---|
| 663 | .for(JAVASCRIPT_MODULE_TYPE_AUTO)
|
---|
| 664 | .tap(PLUGIN_NAME, handler);
|
---|
| 665 | normalModuleFactory.hooks.parser
|
---|
| 666 | .for(JAVASCRIPT_MODULE_TYPE_DYNAMIC)
|
---|
| 667 | .tap(PLUGIN_NAME, handler);
|
---|
| 668 | normalModuleFactory.hooks.parser
|
---|
| 669 | .for(JAVASCRIPT_MODULE_TYPE_ESM)
|
---|
| 670 | .tap(PLUGIN_NAME, handler);
|
---|
| 671 |
|
---|
| 672 | /**
|
---|
| 673 | * Walk definitions
|
---|
| 674 | * @param {Record<string, CodeValue>} definitions Definitions map
|
---|
| 675 | * @param {string} prefix Prefix string
|
---|
| 676 | * @returns {void}
|
---|
| 677 | */
|
---|
| 678 | const walkDefinitionsForValues = (definitions, prefix) => {
|
---|
| 679 | for (const key of Object.keys(definitions)) {
|
---|
| 680 | const code = definitions[key];
|
---|
| 681 | const version = /** @type {string} */ (toCacheVersion(code));
|
---|
| 682 | const name = VALUE_DEP_PREFIX + prefix + key;
|
---|
| 683 | mainHash.update(`|${prefix}${key}`);
|
---|
| 684 | const oldVersion = compilation.valueCacheVersions.get(name);
|
---|
| 685 | if (oldVersion === undefined) {
|
---|
| 686 | compilation.valueCacheVersions.set(name, version);
|
---|
| 687 | } else if (oldVersion !== version) {
|
---|
| 688 | const warning = new WebpackError(
|
---|
| 689 | `${PLUGIN_NAME}\nConflicting values for '${prefix + key}'`
|
---|
| 690 | );
|
---|
| 691 | warning.details = `'${oldVersion}' !== '${version}'`;
|
---|
| 692 | warning.hideStack = true;
|
---|
| 693 | compilation.warnings.push(warning);
|
---|
| 694 | }
|
---|
| 695 | if (
|
---|
| 696 | code &&
|
---|
| 697 | typeof code === "object" &&
|
---|
| 698 | !(code instanceof RuntimeValue) &&
|
---|
| 699 | !(code instanceof RegExp)
|
---|
| 700 | ) {
|
---|
| 701 | walkDefinitionsForValues(
|
---|
| 702 | /** @type {Record<string, CodeValue>} */ (code),
|
---|
| 703 | `${prefix + key}.`
|
---|
| 704 | );
|
---|
| 705 | }
|
---|
| 706 | }
|
---|
| 707 | };
|
---|
| 708 |
|
---|
| 709 | walkDefinitionsForValues(definitions, "");
|
---|
| 710 |
|
---|
| 711 | compilation.valueCacheVersions.set(
|
---|
| 712 | VALUE_DEP_MAIN,
|
---|
| 713 | /** @type {string} */ (mainHash.digest("hex").slice(0, 8))
|
---|
| 714 | );
|
---|
| 715 | }
|
---|
| 716 | );
|
---|
| 717 | }
|
---|
| 718 | }
|
---|
| 719 | module.exports = DefinePlugin;
|
---|