source: frontend/node_modules/webpack/lib/DefinePlugin.js

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

Fix frontend appearance

  • Property mode set to 100644
File size: 25.1 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
8const { SyncWaterfallHook } = require("tapable");
9const {
10 JAVASCRIPT_MODULE_TYPE_AUTO,
11 JAVASCRIPT_MODULE_TYPE_DYNAMIC,
12 JAVASCRIPT_MODULE_TYPE_ESM
13} = require("./ModuleTypeConstants");
14const RuntimeGlobals = require("./RuntimeGlobals");
15const ConstDependency = require("./dependencies/ConstDependency");
16const WebpackError = require("./errors/WebpackError");
17const BasicEvaluatedExpression = require("./javascript/BasicEvaluatedExpression");
18const { VariableInfo } = require("./javascript/JavascriptParser");
19const {
20 evaluateToString,
21 toConstantDependency
22} = require("./javascript/JavascriptParserHelpers");
23const createHash = require("./util/createHash");
24
25/** @typedef {import("estree").Expression} Expression */
26/** @typedef {import("./Compiler")} Compiler */
27/** @typedef {import("./Module").BuildInfo} BuildInfo */
28/** @typedef {import("./Module").ValueCacheVersion} ValueCacheVersion */
29/** @typedef {import("./Module").ValueCacheVersions} ValueCacheVersions */
30/** @typedef {import("./NormalModule")} NormalModule */
31/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
32/** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */
33/** @typedef {import("./javascript/JavascriptParser").DestructuringAssignmentProperties} DestructuringAssignmentProperties */
34/** @typedef {import("./javascript/JavascriptParser").Range} Range */
35/** @typedef {import("./logging/Logger").Logger} Logger */
36/** @typedef {import("./Compilation")} Compilation */
37
38/** @typedef {null | undefined | RegExp | EXPECTED_FUNCTION | string | number | boolean | bigint | undefined} CodeValuePrimitive */
39/** @typedef {RecursiveArrayOrRecord<CodeValuePrimitive | RuntimeValue>} CodeValue */
40
41/**
42 * Defines the runtime value options type used by this module.
43 * @typedef {object} RuntimeValueOptions
44 * @property {string[]=} fileDependencies
45 * @property {string[]=} contextDependencies
46 * @property {string[]=} missingDependencies
47 * @property {string[]=} buildDependencies
48 * @property {string | (() => string)=} version
49 */
50
51/** @typedef {(value: { module: NormalModule, key: string, readonly version: ValueCacheVersion }) => CodeValuePrimitive} GeneratorFn */
52
53class RuntimeValue {
54 /**
55 * Creates an instance of RuntimeValue.
56 * @param {GeneratorFn} fn generator function
57 * @param {true | string[] | RuntimeValueOptions=} options options
58 */
59 constructor(fn, options) {
60 /** @type {GeneratorFn} */
61 this.fn = fn;
62 if (Array.isArray(options)) {
63 options = {
64 fileDependencies: options
65 };
66 }
67 /** @type {true | RuntimeValueOptions} */
68 this.options = options || {};
69 }
70
71 get fileDependencies() {
72 return this.options === true ? true : this.options.fileDependencies;
73 }
74
75 /**
76 * Returns code.
77 * @param {JavascriptParser} parser the parser
78 * @param {ValueCacheVersions} valueCacheVersions valueCacheVersions
79 * @param {string} key the defined key
80 * @returns {CodeValuePrimitive} code
81 */
82 exec(parser, valueCacheVersions, key) {
83 const buildInfo = /** @type {BuildInfo} */ (parser.state.module.buildInfo);
84 if (this.options === true) {
85 buildInfo.cacheable = false;
86 } else {
87 if (this.options.fileDependencies) {
88 for (const dep of this.options.fileDependencies) {
89 /** @type {NonNullable<BuildInfo["fileDependencies"]>} */
90 (buildInfo.fileDependencies).add(dep);
91 }
92 }
93 if (this.options.contextDependencies) {
94 for (const dep of this.options.contextDependencies) {
95 /** @type {NonNullable<BuildInfo["contextDependencies"]>} */
96 (buildInfo.contextDependencies).add(dep);
97 }
98 }
99 if (this.options.missingDependencies) {
100 for (const dep of this.options.missingDependencies) {
101 /** @type {NonNullable<BuildInfo["missingDependencies"]>} */
102 (buildInfo.missingDependencies).add(dep);
103 }
104 }
105 if (this.options.buildDependencies) {
106 for (const dep of this.options.buildDependencies) {
107 /** @type {NonNullable<BuildInfo["buildDependencies"]>} */
108 (buildInfo.buildDependencies).add(dep);
109 }
110 }
111 }
112
113 return this.fn({
114 module: parser.state.module,
115 key,
116 get version() {
117 return /** @type {ValueCacheVersion} */ (
118 valueCacheVersions.get(VALUE_DEP_PREFIX + key)
119 );
120 }
121 });
122 }
123
124 getCacheVersion() {
125 return this.options === true
126 ? undefined
127 : (typeof this.options.version === "function"
128 ? this.options.version()
129 : this.options.version) || "unset";
130 }
131}
132
133/**
134 * Returns used keys.
135 * @param {DestructuringAssignmentProperties | undefined} properties properties
136 * @returns {Set<string> | undefined} used keys
137 */
138function getObjKeys(properties) {
139 if (!properties) return;
140 return new Set([...properties].map((p) => p.id));
141}
142
143/** @typedef {Set<string> | null} ObjKeys */
144/** @typedef {boolean | undefined | null} AsiSafe */
145
146/**
147 * Returns code converted to string that evaluates.
148 * @param {EXPECTED_ANY[] | { [k: string]: EXPECTED_ANY }} obj obj
149 * @param {JavascriptParser} parser Parser
150 * @param {ValueCacheVersions} valueCacheVersions valueCacheVersions
151 * @param {string} key the defined key
152 * @param {RuntimeTemplate} runtimeTemplate the runtime template
153 * @param {Logger} logger the logger object
154 * @param {AsiSafe=} asiSafe asi safe (undefined: unknown, null: unneeded)
155 * @param {ObjKeys=} objKeys used keys
156 * @returns {string} code converted to string that evaluates
157 */
158const stringifyObj = (
159 obj,
160 parser,
161 valueCacheVersions,
162 key,
163 runtimeTemplate,
164 logger,
165 asiSafe,
166 objKeys
167) => {
168 /** @type {string} */
169 let code;
170 const arr = Array.isArray(obj);
171 if (arr) {
172 code = `[${obj
173 .map((code) =>
174 toCode(
175 code,
176 parser,
177 valueCacheVersions,
178 key,
179 runtimeTemplate,
180 logger,
181 null
182 )
183 )
184 .join(",")}]`;
185 } else {
186 let keys = Object.keys(obj);
187 if (objKeys) {
188 keys = objKeys.size === 0 ? [] : keys.filter((k) => objKeys.has(k));
189 }
190 code = `{${keys
191 .map((key) => {
192 const code = obj[key];
193 return `${key === "__proto__" ? '["__proto__"]' : JSON.stringify(key)}:${toCode(
194 code,
195 parser,
196 valueCacheVersions,
197 key,
198 runtimeTemplate,
199 logger,
200 null
201 )}`;
202 })
203 .join(",")}}`;
204 }
205
206 switch (asiSafe) {
207 case null:
208 return code;
209 case true:
210 return arr ? code : `(${code})`;
211 case false:
212 return arr ? `;${code}` : `;(${code})`;
213 default:
214 return `/*#__PURE__*/Object(${code})`;
215 }
216};
217
218/**
219 * Convert code to a string that evaluates
220 * @param {CodeValue} code Code to evaluate
221 * @param {JavascriptParser} parser Parser
222 * @param {ValueCacheVersions} valueCacheVersions valueCacheVersions
223 * @param {string} key the defined key
224 * @param {RuntimeTemplate} runtimeTemplate the runtime template
225 * @param {Logger} logger the logger object
226 * @param {boolean | undefined | null=} asiSafe asi safe (undefined: unknown, null: unneeded)
227 * @param {ObjKeys=} objKeys used keys
228 * @returns {string} code converted to string that evaluates
229 */
230const toCode = (
231 code,
232 parser,
233 valueCacheVersions,
234 key,
235 runtimeTemplate,
236 logger,
237 asiSafe,
238 objKeys
239) => {
240 const transformToCode = () => {
241 if (code === null) {
242 return "null";
243 }
244 if (code === undefined) {
245 return "undefined";
246 }
247 if (Object.is(code, -0)) {
248 return "-0";
249 }
250 if (code instanceof RuntimeValue) {
251 return toCode(
252 code.exec(parser, valueCacheVersions, key),
253 parser,
254 valueCacheVersions,
255 key,
256 runtimeTemplate,
257 logger,
258 asiSafe
259 );
260 }
261 if (code instanceof RegExp && code.toString) {
262 return code.toString();
263 }
264 if (typeof code === "function" && code.toString) {
265 return `(${code.toString()})`;
266 }
267 if (typeof code === "object") {
268 return stringifyObj(
269 code,
270 parser,
271 valueCacheVersions,
272 key,
273 runtimeTemplate,
274 logger,
275 asiSafe,
276 objKeys
277 );
278 }
279 if (typeof code === "bigint") {
280 return runtimeTemplate.supportsBigIntLiteral()
281 ? `${code}n`
282 : `BigInt("${code}")`;
283 }
284 return `${code}`;
285 };
286
287 const strCode = transformToCode();
288
289 logger.debug(`Replaced "${key}" with "${strCode}"`);
290
291 return strCode;
292};
293
294/**
295 * Returns result.
296 * @param {CodeValue} code code
297 * @returns {string | undefined} result
298 */
299const toCacheVersion = (code) => {
300 if (code === null) {
301 return "null";
302 }
303 if (code === undefined) {
304 return "undefined";
305 }
306 if (Object.is(code, -0)) {
307 return "-0";
308 }
309 if (code instanceof RuntimeValue) {
310 return code.getCacheVersion();
311 }
312 if (code instanceof RegExp && code.toString) {
313 return code.toString();
314 }
315 if (typeof code === "function" && code.toString) {
316 return `(${code.toString()})`;
317 }
318 if (typeof code === "object") {
319 const items = Object.keys(code).map((key) => ({
320 key,
321 value: toCacheVersion(
322 /** @type {Record<string, CodeValue>} */
323 (code)[key]
324 )
325 }));
326 if (items.some(({ value }) => value === undefined)) return;
327 return `{${items.map(({ key, value }) => `${key}: ${value}`).join(", ")}}`;
328 }
329 if (typeof code === "bigint") {
330 return `${code}n`;
331 }
332 return `${code}`;
333};
334
335const PLUGIN_NAME = "DefinePlugin";
336const VALUE_DEP_PREFIX = `webpack/${PLUGIN_NAME} `;
337const VALUE_DEP_MAIN = `webpack/${PLUGIN_NAME}_hash`;
338const TYPEOF_OPERATOR_REGEXP = /^typeof\s+/;
339const WEBPACK_REQUIRE_FUNCTION_REGEXP = new RegExp(
340 `${RuntimeGlobals.require}\\s*(!?\\.)`
341);
342const WEBPACK_REQUIRE_IDENTIFIER_REGEXP = new RegExp(RuntimeGlobals.require);
343
344/**
345 * Defines the define plugin hooks type used by this module.
346 * @typedef {object} DefinePluginHooks
347 * @property {SyncWaterfallHook<[Record<string, CodeValue>]>} definitions
348 */
349
350/** @typedef {Record<string, CodeValue>} Definitions */
351
352/** @type {WeakMap<Compilation, DefinePluginHooks>} */
353const compilationHooksMap = new WeakMap();
354
355class DefinePlugin {
356 /**
357 * Returns the attached hooks.
358 * @param {Compilation} compilation the compilation
359 * @returns {DefinePluginHooks} the attached hooks
360 */
361 static getCompilationHooks(compilation) {
362 let hooks = compilationHooksMap.get(compilation);
363 if (hooks === undefined) {
364 hooks = {
365 definitions: new SyncWaterfallHook(["definitions"])
366 };
367 compilationHooksMap.set(compilation, hooks);
368 }
369 return hooks;
370 }
371
372 /**
373 * Create a new define plugin
374 * @param {Definitions} definitions A map of global object definitions
375 */
376 constructor(definitions) {
377 /** @type {Definitions} */
378 this.definitions = definitions;
379 }
380
381 /**
382 * Returns runtime value.
383 * @param {GeneratorFn} fn generator function
384 * @param {true | string[] | RuntimeValueOptions=} options options
385 * @returns {RuntimeValue} runtime value
386 */
387 static runtimeValue(fn, options) {
388 return new RuntimeValue(fn, options);
389 }
390
391 /**
392 * Applies the plugin by registering its hooks on the compiler.
393 * @param {Compiler} compiler the compiler instance
394 * @returns {void}
395 */
396 apply(compiler) {
397 compiler.hooks.compilation.tap(
398 PLUGIN_NAME,
399 (compilation, { normalModuleFactory }) => {
400 const definitions = this.definitions;
401 const hooks = DefinePlugin.getCompilationHooks(compilation);
402
403 hooks.definitions.tap(PLUGIN_NAME, (previousDefinitions) => ({
404 ...previousDefinitions,
405 ...definitions
406 }));
407
408 /**
409 * @type {Map<string, Set<string>>}
410 */
411 const finalByNestedKey = new Map();
412 /**
413 * @type {Map<string, Set<string>>}
414 */
415 const nestedByFinalKey = new Map();
416
417 const logger = compilation.getLogger("webpack.DefinePlugin");
418 compilation.dependencyTemplates.set(
419 ConstDependency,
420 new ConstDependency.Template()
421 );
422 const { runtimeTemplate } = compilation;
423
424 const mainHash = createHash(compilation.outputOptions.hashFunction);
425 mainHash.update(
426 /** @type {string} */
427 (compilation.valueCacheVersions.get(VALUE_DEP_MAIN)) || ""
428 );
429
430 /**
431 * Handles the hook callback for this code path.
432 * @param {JavascriptParser} parser Parser
433 * @returns {void}
434 */
435 const handler = (parser) => {
436 /** @type {Set<string>} */
437 const hooked = new Set();
438 const mainValue =
439 /** @type {ValueCacheVersion} */
440 (compilation.valueCacheVersions.get(VALUE_DEP_MAIN));
441 parser.hooks.program.tap(PLUGIN_NAME, () => {
442 const buildInfo = /** @type {BuildInfo} */ (
443 parser.state.module.buildInfo
444 );
445 if (!buildInfo.valueDependencies) {
446 buildInfo.valueDependencies = new Map();
447 }
448 buildInfo.valueDependencies.set(VALUE_DEP_MAIN, mainValue);
449 });
450
451 /**
452 * Adds value dependency.
453 * @param {string} key key
454 */
455 const addValueDependency = (key) => {
456 const buildInfo =
457 /** @type {BuildInfo} */
458 (parser.state.module.buildInfo);
459 /** @type {NonNullable<BuildInfo["valueDependencies"]>} */
460 (buildInfo.valueDependencies).set(
461 VALUE_DEP_PREFIX + key,
462 /** @type {ValueCacheVersion} */
463 (compilation.valueCacheVersions.get(VALUE_DEP_PREFIX + key))
464 );
465 };
466
467 /**
468 * With value dependency.
469 * @template T
470 * @param {string} key key
471 * @param {(expression: Expression) => T} fn fn
472 * @returns {(expression: Expression) => T} result
473 */
474 const withValueDependency =
475 (key, fn) =>
476 (...args) => {
477 addValueDependency(key);
478 return fn(...args);
479 };
480
481 /**
482 * Processes the provided definition.
483 * @param {Definitions} definitions Definitions map
484 * @param {string} prefix Prefix string
485 * @returns {void}
486 */
487 const walkDefinitions = (definitions, prefix) => {
488 for (const key of Object.keys(definitions)) {
489 const code = definitions[key];
490 if (
491 code &&
492 typeof code === "object" &&
493 !(code instanceof RuntimeValue) &&
494 !(code instanceof RegExp)
495 ) {
496 walkDefinitions(
497 /** @type {Definitions} */ (code),
498 `${prefix + key}.`
499 );
500 applyObjectDefine(prefix + key, code);
501 continue;
502 }
503 applyDefineKey(prefix, key);
504 applyDefine(prefix + key, code);
505 }
506 };
507
508 /**
509 * Processes the provided prefix.
510 * @param {string} prefix Prefix
511 * @param {string} key Key
512 * @returns {void}
513 */
514 const applyDefineKey = (prefix, key) => {
515 const splittedKey = key.split(".");
516 const firstKey = splittedKey[0];
517 for (const [i, _] of splittedKey.slice(1).entries()) {
518 const fullKey = prefix + splittedKey.slice(0, i + 1).join(".");
519 parser.hooks.canRename.for(fullKey).tap(PLUGIN_NAME, () => {
520 addValueDependency(key);
521 if (
522 parser.scope.definitions.get(firstKey) instanceof VariableInfo
523 ) {
524 return false;
525 }
526 return true;
527 });
528 }
529 if (prefix === "") {
530 const final = splittedKey[splittedKey.length - 1];
531 const nestedSet = nestedByFinalKey.get(final);
532 if (!nestedSet || nestedSet.size <= 0) return;
533 for (const nested of /** @type {Set<string>} */ (nestedSet)) {
534 if (nested && !hooked.has(nested)) {
535 // only detect the same nested key once
536 hooked.add(nested);
537 parser.hooks.collectDestructuringAssignmentProperties.tap(
538 PLUGIN_NAME,
539 (expr) => {
540 const nameInfo = parser.getNameForExpression(expr);
541 if (nameInfo && nameInfo.name === nested) return true;
542 }
543 );
544 parser.hooks.expression.for(nested).tap(
545 {
546 name: PLUGIN_NAME,
547 // why 100? Ensures it runs after object define
548 stage: 100
549 },
550 (expr) => {
551 const destructed =
552 parser.destructuringAssignmentPropertiesFor(expr);
553 if (destructed === undefined) {
554 return;
555 }
556 /** @type {Definitions} */
557 const obj = Object.create(null);
558 const finalSet = finalByNestedKey.get(nested);
559 for (const { id } of destructed) {
560 const fullKey = `${nested}.${id}`;
561 if (
562 !finalSet ||
563 !finalSet.has(id) ||
564 !definitions[fullKey]
565 ) {
566 return;
567 }
568 obj[id] = definitions[fullKey];
569 }
570 let strCode = stringifyObj(
571 obj,
572 parser,
573 compilation.valueCacheVersions,
574 key,
575 runtimeTemplate,
576 logger,
577 !parser.isAsiPosition(
578 /** @type {Range} */ (expr.range)[0]
579 ),
580 getObjKeys(destructed)
581 );
582 if (parser.scope.inShorthand) {
583 strCode = `${parser.scope.inShorthand}:${strCode}`;
584 }
585 return toConstantDependency(parser, strCode)(expr);
586 }
587 );
588 }
589 }
590 }
591 };
592
593 /**
594 * Processes the provided key.
595 * @param {string} key Key
596 * @param {CodeValue} code Code
597 * @returns {void}
598 */
599 const applyDefine = (key, code) => {
600 const originalKey = key;
601 const isTypeof = TYPEOF_OPERATOR_REGEXP.test(key);
602 if (isTypeof) key = key.replace(TYPEOF_OPERATOR_REGEXP, "");
603 let recurse = false;
604 let recurseTypeof = false;
605 if (!isTypeof) {
606 parser.hooks.canRename.for(key).tap(PLUGIN_NAME, () => {
607 addValueDependency(originalKey);
608 return true;
609 });
610 parser.hooks.evaluateIdentifier
611 .for(key)
612 .tap(PLUGIN_NAME, (expr) => {
613 /**
614 * this is needed in case there is a recursion in the DefinePlugin
615 * to prevent an endless recursion
616 * e.g.: new DefinePlugin({
617 * "a": "b",
618 * "b": "a"
619 * });
620 */
621 if (recurse) return;
622 addValueDependency(originalKey);
623 recurse = true;
624 const res = parser.evaluate(
625 toCode(
626 code,
627 parser,
628 compilation.valueCacheVersions,
629 key,
630 runtimeTemplate,
631 logger,
632 null
633 )
634 );
635 recurse = false;
636 res.setRange(/** @type {Range} */ (expr.range));
637 return res;
638 });
639 parser.hooks.expression.for(key).tap(PLUGIN_NAME, (expr) => {
640 addValueDependency(originalKey);
641 let strCode = toCode(
642 code,
643 parser,
644 compilation.valueCacheVersions,
645 originalKey,
646 runtimeTemplate,
647 logger,
648 !parser.isAsiPosition(/** @type {Range} */ (expr.range)[0]),
649 null
650 );
651
652 if (parser.scope.inShorthand) {
653 strCode = `${parser.scope.inShorthand}:${strCode}`;
654 }
655
656 if (WEBPACK_REQUIRE_FUNCTION_REGEXP.test(strCode)) {
657 return toConstantDependency(parser, strCode, [
658 RuntimeGlobals.require
659 ])(expr);
660 } else if (WEBPACK_REQUIRE_IDENTIFIER_REGEXP.test(strCode)) {
661 return toConstantDependency(parser, strCode, [
662 RuntimeGlobals.requireScope
663 ])(expr);
664 }
665 return toConstantDependency(parser, strCode)(expr);
666 });
667 }
668 parser.hooks.evaluateTypeof.for(key).tap(PLUGIN_NAME, (expr) => {
669 /**
670 * this is needed in case there is a recursion in the DefinePlugin
671 * to prevent an endless recursion
672 * e.g.: new DefinePlugin({
673 * "typeof a": "typeof b",
674 * "typeof b": "typeof a"
675 * });
676 */
677 if (recurseTypeof) return;
678 recurseTypeof = true;
679 addValueDependency(originalKey);
680 const codeCode = toCode(
681 code,
682 parser,
683 compilation.valueCacheVersions,
684 originalKey,
685 runtimeTemplate,
686 logger,
687 null
688 );
689 const typeofCode = isTypeof ? codeCode : `typeof (${codeCode})`;
690 const res = parser.evaluate(typeofCode);
691 recurseTypeof = false;
692 res.setRange(/** @type {Range} */ (expr.range));
693 return res;
694 });
695 parser.hooks.typeof.for(key).tap(PLUGIN_NAME, (expr) => {
696 addValueDependency(originalKey);
697 const codeCode = toCode(
698 code,
699 parser,
700 compilation.valueCacheVersions,
701 originalKey,
702 runtimeTemplate,
703 logger,
704 null
705 );
706 const typeofCode = isTypeof ? codeCode : `typeof (${codeCode})`;
707 const res = parser.evaluate(typeofCode);
708 if (!res.isString()) return;
709 return toConstantDependency(
710 parser,
711 JSON.stringify(res.string)
712 ).bind(parser)(expr);
713 });
714 };
715
716 /**
717 * Processes the provided key.
718 * @param {string} key Key
719 * @param {object} obj Object
720 * @returns {void}
721 */
722 const applyObjectDefine = (key, obj) => {
723 parser.hooks.canRename.for(key).tap(PLUGIN_NAME, () => {
724 addValueDependency(key);
725 return true;
726 });
727 parser.hooks.evaluateIdentifier
728 .for(key)
729 .tap(PLUGIN_NAME, (expr) => {
730 addValueDependency(key);
731 return new BasicEvaluatedExpression()
732 .setTruthy()
733 .setSideEffects(false)
734 .setRange(/** @type {Range} */ (expr.range));
735 });
736 parser.hooks.evaluateTypeof
737 .for(key)
738 .tap(
739 PLUGIN_NAME,
740 withValueDependency(key, evaluateToString("object"))
741 );
742 parser.hooks.collectDestructuringAssignmentProperties.tap(
743 PLUGIN_NAME,
744 (expr) => {
745 const nameInfo = parser.getNameForExpression(expr);
746 if (nameInfo && nameInfo.name === key) return true;
747 }
748 );
749 parser.hooks.expression.for(key).tap(PLUGIN_NAME, (expr) => {
750 addValueDependency(key);
751 let strCode = stringifyObj(
752 obj,
753 parser,
754 compilation.valueCacheVersions,
755 key,
756 runtimeTemplate,
757 logger,
758 !parser.isAsiPosition(/** @type {Range} */ (expr.range)[0]),
759 getObjKeys(parser.destructuringAssignmentPropertiesFor(expr))
760 );
761
762 if (parser.scope.inShorthand) {
763 strCode = `${parser.scope.inShorthand}:${strCode}`;
764 }
765
766 if (WEBPACK_REQUIRE_FUNCTION_REGEXP.test(strCode)) {
767 return toConstantDependency(parser, strCode, [
768 RuntimeGlobals.require
769 ])(expr);
770 } else if (WEBPACK_REQUIRE_IDENTIFIER_REGEXP.test(strCode)) {
771 return toConstantDependency(parser, strCode, [
772 RuntimeGlobals.requireScope
773 ])(expr);
774 }
775 return toConstantDependency(parser, strCode)(expr);
776 });
777 parser.hooks.typeof
778 .for(key)
779 .tap(
780 PLUGIN_NAME,
781 withValueDependency(
782 key,
783 toConstantDependency(parser, JSON.stringify("object"))
784 )
785 );
786 };
787
788 walkDefinitions(definitions, "");
789 };
790
791 normalModuleFactory.hooks.parser
792 .for(JAVASCRIPT_MODULE_TYPE_AUTO)
793 .tap(PLUGIN_NAME, handler);
794 normalModuleFactory.hooks.parser
795 .for(JAVASCRIPT_MODULE_TYPE_DYNAMIC)
796 .tap(PLUGIN_NAME, handler);
797 normalModuleFactory.hooks.parser
798 .for(JAVASCRIPT_MODULE_TYPE_ESM)
799 .tap(PLUGIN_NAME, handler);
800
801 /**
802 * Processes the provided definition.
803 * @param {Definitions} definitions Definitions map
804 * @param {string} prefix Prefix string
805 * @returns {void}
806 */
807 const walkDefinitionsForValues = (definitions, prefix) => {
808 for (const key of Object.keys(definitions)) {
809 const code = definitions[key];
810 const version = /** @type {string} */ (toCacheVersion(code));
811 const name = VALUE_DEP_PREFIX + prefix + key;
812 mainHash.update(`|${prefix}${key}`);
813 const oldVersion = compilation.valueCacheVersions.get(name);
814 if (oldVersion === undefined) {
815 compilation.valueCacheVersions.set(name, version);
816 } else if (oldVersion !== version) {
817 const warning = new WebpackError(
818 `${PLUGIN_NAME}\nConflicting values for '${prefix + key}'`
819 );
820 warning.details = `'${oldVersion}' !== '${version}'`;
821 warning.hideStack = true;
822 compilation.warnings.push(warning);
823 }
824 if (
825 code &&
826 typeof code === "object" &&
827 !(code instanceof RuntimeValue) &&
828 !(code instanceof RegExp)
829 ) {
830 walkDefinitionsForValues(
831 /** @type {Definitions} */ (code),
832 `${prefix + key}.`
833 );
834 }
835 }
836 };
837
838 /**
839 * Walk definitions for keys.
840 * @param {Definitions} definitions Definitions map
841 * @returns {void}
842 */
843 const walkDefinitionsForKeys = (definitions) => {
844 /**
845 * Adds the provided map to the define plugin.
846 * @param {Map<string, Set<string>>} map Map
847 * @param {string} key key
848 * @param {string} value v
849 * @returns {void}
850 */
851 const addToMap = (map, key, value) => {
852 if (map.has(key)) {
853 /** @type {Set<string>} */
854 (map.get(key)).add(value);
855 } else {
856 map.set(key, new Set([value]));
857 }
858 };
859 for (const key of Object.keys(definitions)) {
860 const code = definitions[key];
861 if (
862 !code ||
863 typeof code === "object" ||
864 TYPEOF_OPERATOR_REGEXP.test(key)
865 ) {
866 continue;
867 }
868 const idx = key.lastIndexOf(".");
869 if (idx <= 0 || idx >= key.length - 1) {
870 continue;
871 }
872 const nested = key.slice(0, idx);
873 const final = key.slice(idx + 1);
874 addToMap(finalByNestedKey, nested, final);
875 addToMap(nestedByFinalKey, final, nested);
876 }
877 };
878
879 walkDefinitionsForKeys(definitions);
880 walkDefinitionsForValues(definitions, "");
881
882 compilation.valueCacheVersions.set(
883 VALUE_DEP_MAIN,
884 mainHash.digest("hex").slice(0, 8)
885 );
886 }
887 );
888 }
889}
890
891module.exports = DefinePlugin;
Note: See TracBrowser for help on using the repository browser.