ShareRuntimeModule.js 4.5 KB

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