DefinePlugin.js 19 KB

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