ShareRuntimeModule.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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. /** @import Chunk from "../Chunk" */
  14. /** @import ChunkGraph from "../ChunkGraph" */
  15. /** @import Compilation from "../Compilation" */
  16. /** @import CodeGenerationResults from "../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. const cst = runtimeTemplate.renderConst();
  68. const lt = runtimeTemplate.renderLet();
  69. return Template.asString([
  70. `${RuntimeGlobals.shareScopeMap} = {};`,
  71. `${cst} initPromises = {};`,
  72. `${cst} initTokens = {};`,
  73. `${RuntimeGlobals.initializeSharing} = ${runtimeTemplate.basicFunction(
  74. "name, initScope",
  75. [
  76. "if(!initScope) initScope = [];",
  77. "// handling circular init calls",
  78. `${lt} initToken = initTokens[name];`,
  79. "if(!initToken) initToken = initTokens[name] = {};",
  80. "if(initScope.indexOf(initToken) >= 0) return;",
  81. "initScope.push(initToken);",
  82. "// only runs once",
  83. "if(initPromises[name]) return initPromises[name];",
  84. "// creates a new share scope if needed",
  85. `if(!${RuntimeGlobals.hasOwnProperty}(${RuntimeGlobals.shareScopeMap}, name)) ${RuntimeGlobals.shareScopeMap}[name] = {};`,
  86. "// runs all init snippets from all modules reachable",
  87. `${cst} scope = ${RuntimeGlobals.shareScopeMap}[name];`,
  88. `${cst} warn = ${
  89. ignoreBrowserWarnings
  90. ? runtimeTemplate.basicFunction("", "")
  91. : runtimeTemplate.basicFunction("msg", [
  92. 'if (typeof console !== "undefined" && console.warn) console.warn(msg);'
  93. ])
  94. };`,
  95. `${cst} uniqueName = ${JSON.stringify(uniqueName || undefined)};`,
  96. `${cst} register = ${runtimeTemplate.basicFunction(
  97. "name, version, factory, eager",
  98. [
  99. `${cst} versions = ${runtimeTemplate.assignOr("scope[name]", "{}")};`,
  100. `${cst} activeVersion = versions[version];`,
  101. "if(!activeVersion || (!activeVersion.loaded && (!eager != !activeVersion.eager ? eager : uniqueName > activeVersion.from))) versions[version] = { get: factory, from: uniqueName, eager: !!eager };"
  102. ]
  103. )};`,
  104. `${cst} initExternal = ${runtimeTemplate.basicFunction("id", [
  105. `${cst} handleError = ${runtimeTemplate.expressionFunction(
  106. 'warn("Initialization of sharing external failed: " + err)',
  107. "err"
  108. )};`,
  109. "try {",
  110. Template.indent([
  111. `${cst} module = ${RuntimeGlobals.require}(id);`,
  112. "if(!module) return;",
  113. `${cst} initFn = ${runtimeTemplate.returningFunction(
  114. `module && module.init && module.init(${RuntimeGlobals.shareScopeMap}[name], initScope)`,
  115. "module"
  116. )}`,
  117. "if(module.then) return promises.push(module.then(initFn, handleError));",
  118. `${cst} initResult = initFn(module);`,
  119. `if(${runtimeTemplate.optionalChaining("initResult", "then")}) return promises.push(initResult['catch'](handleError));`
  120. ]),
  121. "} catch(err) { handleError(err); }"
  122. ])}`,
  123. `${cst} promises = [];`,
  124. "switch(name) {",
  125. ...[...initCodePerScope]
  126. .sort(([a], [b]) => compareStrings(a, b))
  127. .map(([name, stages]) =>
  128. Template.indent([
  129. `case ${JSON.stringify(name)}: {`,
  130. Template.indent(
  131. [...stages]
  132. .sort(([a], [b]) => a - b)
  133. .map(([, initCode]) => Template.asString([...initCode]))
  134. ),
  135. "}",
  136. "break;"
  137. ])
  138. ),
  139. "}",
  140. "if(!promises.length) return initPromises[name] = 1;",
  141. `return initPromises[name] = Promise.all(promises).then(${runtimeTemplate.returningFunction(
  142. "initPromises[name] = 1"
  143. )});`
  144. ]
  145. )};`
  146. ]);
  147. }
  148. }
  149. module.exports = ShareRuntimeModule;