ShareRuntimeModule.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const RuntimeGlobals = require("../RuntimeGlobals");
  7. const RuntimeModule = require("../RuntimeModule");
  8. const Template = require("../Template");
  9. const {
  10. compareModulesByIdentifier,
  11. compareStrings
  12. } = require("../util/comparators");
  13. /** @typedef {import("../Chunk")} Chunk */
  14. /** @typedef {import("../ChunkGraph")} ChunkGraph */
  15. /** @typedef {import("../Compilation")} Compilation */
  16. /** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */
  17. class ShareRuntimeModule extends RuntimeModule {
  18. constructor() {
  19. super("sharing");
  20. }
  21. /**
  22. * Generates runtime code for this runtime module.
  23. * @returns {string | null} runtime code
  24. */
  25. generate() {
  26. const compilation = /** @type {Compilation} */ (this.compilation);
  27. const {
  28. runtimeTemplate,
  29. outputOptions: { uniqueName, ignoreBrowserWarnings }
  30. } = compilation;
  31. const codeGenerationResults =
  32. /** @type {CodeGenerationResults} */
  33. (compilation.codeGenerationResults);
  34. const chunkGraph = /** @type {ChunkGraph} */ (this.chunkGraph);
  35. /** @type {Map<string, Map<number, Set<string>>>} */
  36. const initCodePerScope = new Map();
  37. for (const chunk of /** @type {Chunk} */ (
  38. this.chunk
  39. ).getAllReferencedChunks()) {
  40. const modules = chunkGraph.getOrderedChunkModulesIterableBySourceType(
  41. chunk,
  42. "share-init",
  43. compareModulesByIdentifier
  44. );
  45. if (!modules) continue;
  46. for (const m of modules) {
  47. const data = codeGenerationResults.getData(
  48. m,
  49. chunk.runtime,
  50. "share-init"
  51. );
  52. if (!data) continue;
  53. for (const item of data) {
  54. const { shareScope, initStage, init } = item;
  55. let stages = initCodePerScope.get(shareScope);
  56. if (stages === undefined) {
  57. initCodePerScope.set(shareScope, (stages = new Map()));
  58. }
  59. let list = stages.get(initStage || 0);
  60. if (list === undefined) {
  61. stages.set(initStage || 0, (list = new Set()));
  62. }
  63. list.add(init);
  64. }
  65. }
  66. }
  67. return Template.asString([
  68. `${RuntimeGlobals.shareScopeMap} = {};`,
  69. "var initPromises = {};",
  70. "var initTokens = {};",
  71. `${RuntimeGlobals.initializeSharing} = ${runtimeTemplate.basicFunction(
  72. "name, initScope",
  73. [
  74. "if(!initScope) initScope = [];",
  75. "// handling circular init calls",
  76. "var initToken = initTokens[name];",
  77. "if(!initToken) initToken = initTokens[name] = {};",
  78. "if(initScope.indexOf(initToken) >= 0) return;",
  79. "initScope.push(initToken);",
  80. "// only runs once",
  81. "if(initPromises[name]) return initPromises[name];",
  82. "// creates a new share scope if needed",
  83. `if(!${RuntimeGlobals.hasOwnProperty}(${RuntimeGlobals.shareScopeMap}, name)) ${RuntimeGlobals.shareScopeMap}[name] = {};`,
  84. "// runs all init snippets from all modules reachable",
  85. `var scope = ${RuntimeGlobals.shareScopeMap}[name];`,
  86. `var warn = ${
  87. ignoreBrowserWarnings
  88. ? runtimeTemplate.basicFunction("", "")
  89. : runtimeTemplate.basicFunction("msg", [
  90. 'if (typeof console !== "undefined" && console.warn) console.warn(msg);'
  91. ])
  92. };`,
  93. `var uniqueName = ${JSON.stringify(uniqueName || undefined)};`,
  94. `var register = ${runtimeTemplate.basicFunction(
  95. "name, version, factory, eager",
  96. [
  97. "var versions = scope[name] = scope[name] || {};",
  98. "var activeVersion = versions[version];",
  99. "if(!activeVersion || (!activeVersion.loaded && (!eager != !activeVersion.eager ? eager : uniqueName > activeVersion.from))) versions[version] = { get: factory, from: uniqueName, eager: !!eager };"
  100. ]
  101. )};`,
  102. `var initExternal = ${runtimeTemplate.basicFunction("id", [
  103. `var handleError = ${runtimeTemplate.expressionFunction(
  104. 'warn("Initialization of sharing external failed: " + err)',
  105. "err"
  106. )};`,
  107. "try {",
  108. Template.indent([
  109. `var module = ${RuntimeGlobals.require}(id);`,
  110. "if(!module) return;",
  111. `var initFn = ${runtimeTemplate.returningFunction(
  112. `module && module.init && module.init(${RuntimeGlobals.shareScopeMap}[name], initScope)`,
  113. "module"
  114. )}`,
  115. "if(module.then) return promises.push(module.then(initFn, handleError));",
  116. "var initResult = initFn(module);",
  117. "if(initResult && initResult.then) return promises.push(initResult['catch'](handleError));"
  118. ]),
  119. "} catch(err) { handleError(err); }"
  120. ])}`,
  121. "var promises = [];",
  122. "switch(name) {",
  123. ...[...initCodePerScope]
  124. .sort(([a], [b]) => compareStrings(a, b))
  125. .map(([name, stages]) =>
  126. Template.indent([
  127. `case ${JSON.stringify(name)}: {`,
  128. Template.indent(
  129. [...stages]
  130. .sort(([a], [b]) => a - b)
  131. .map(([, initCode]) => Template.asString([...initCode]))
  132. ),
  133. "}",
  134. "break;"
  135. ])
  136. ),
  137. "}",
  138. "if(!promises.length) return initPromises[name] = 1;",
  139. `return initPromises[name] = Promise.all(promises).then(${runtimeTemplate.returningFunction(
  140. "initPromises[name] = 1"
  141. )});`
  142. ]
  143. )};`
  144. ]);
  145. }
  146. }
  147. module.exports = ShareRuntimeModule;