ConsumeSharedRuntimeModule.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  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. parseVersionRuntimeCode,
  11. versionLtRuntimeCode,
  12. rangeToStringRuntimeCode,
  13. satisfyRuntimeCode
  14. } = require("../util/semver");
  15. /** @typedef {import("webpack-sources").Source} Source */
  16. /** @typedef {import("../Chunk")} Chunk */
  17. /** @typedef {import("../Module")} Module */
  18. /** @typedef {import("./ConsumeSharedModule")} ConsumeSharedModule */
  19. class ConsumeSharedRuntimeModule extends RuntimeModule {
  20. constructor(runtimeRequirements) {
  21. super("consumes", RuntimeModule.STAGE_ATTACH);
  22. this._runtimeRequirements = runtimeRequirements;
  23. }
  24. /**
  25. * @returns {string} runtime code
  26. */
  27. generate() {
  28. const { compilation, chunkGraph } = this;
  29. const { runtimeTemplate, codeGenerationResults } = compilation;
  30. const chunkToModuleMapping = {};
  31. /** @type {Map<string | number, Source>} */
  32. const moduleIdToSourceMapping = new Map();
  33. const initialConsumes = [];
  34. /**
  35. *
  36. * @param {Iterable<Module>} modules modules
  37. * @param {Chunk} chunk the chunk
  38. * @param {(string | number)[]} list list of ids
  39. */
  40. const addModules = (modules, chunk, list) => {
  41. for (const m of modules) {
  42. const module = /** @type {ConsumeSharedModule} */ (m);
  43. const id = chunkGraph.getModuleId(module);
  44. list.push(id);
  45. moduleIdToSourceMapping.set(
  46. id,
  47. codeGenerationResults.getSource(
  48. module,
  49. chunk.runtime,
  50. "consume-shared"
  51. )
  52. );
  53. }
  54. };
  55. for (const chunk of this.chunk.getAllAsyncChunks()) {
  56. const modules = chunkGraph.getChunkModulesIterableBySourceType(
  57. chunk,
  58. "consume-shared"
  59. );
  60. if (!modules) continue;
  61. addModules(modules, chunk, (chunkToModuleMapping[chunk.id] = []));
  62. }
  63. for (const chunk of this.chunk.getAllInitialChunks()) {
  64. const modules = chunkGraph.getChunkModulesIterableBySourceType(
  65. chunk,
  66. "consume-shared"
  67. );
  68. if (!modules) continue;
  69. addModules(modules, chunk, initialConsumes);
  70. }
  71. if (moduleIdToSourceMapping.size === 0) return null;
  72. return Template.asString([
  73. parseVersionRuntimeCode(runtimeTemplate),
  74. versionLtRuntimeCode(runtimeTemplate),
  75. rangeToStringRuntimeCode(runtimeTemplate),
  76. satisfyRuntimeCode(runtimeTemplate),
  77. `var ensureExistence = ${runtimeTemplate.basicFunction("scopeName, key", [
  78. `var scope = ${RuntimeGlobals.shareScopeMap}[scopeName];`,
  79. `if(!scope || !${RuntimeGlobals.hasOwnProperty}(scope, key)) throw new Error("Shared module " + key + " doesn't exist in shared scope " + scopeName);`,
  80. "return scope;"
  81. ])};`,
  82. `var findVersion = ${runtimeTemplate.basicFunction("scope, key", [
  83. "var versions = scope[key];",
  84. `var key = Object.keys(versions).reduce(${runtimeTemplate.basicFunction(
  85. "a, b",
  86. ["return !a || versionLt(a, b) ? b : a;"]
  87. )}, 0);`,
  88. "return key && versions[key]"
  89. ])};`,
  90. `var findSingletonVersionKey = ${runtimeTemplate.basicFunction(
  91. "scope, key",
  92. [
  93. "var versions = scope[key];",
  94. `return Object.keys(versions).reduce(${runtimeTemplate.basicFunction(
  95. "a, b",
  96. ["return !a || (!versions[a].loaded && versionLt(a, b)) ? b : a;"]
  97. )}, 0);`
  98. ]
  99. )};`,
  100. `var getInvalidSingletonVersionMessage = ${runtimeTemplate.basicFunction(
  101. "scope, key, version, requiredVersion",
  102. [
  103. `return "Unsatisfied version " + version + " from " + (version && scope[key][version].from) + " of shared singleton module " + key + " (required " + rangeToString(requiredVersion) + ")"`
  104. ]
  105. )};`,
  106. `var getSingleton = ${runtimeTemplate.basicFunction(
  107. "scope, scopeName, key, requiredVersion",
  108. [
  109. "var version = findSingletonVersionKey(scope, key);",
  110. "return get(scope[key][version]);"
  111. ]
  112. )};`,
  113. `var getSingletonVersion = ${runtimeTemplate.basicFunction(
  114. "scope, scopeName, key, requiredVersion",
  115. [
  116. "var version = findSingletonVersionKey(scope, key);",
  117. "if (!satisfy(requiredVersion, version)) " +
  118. 'typeof console !== "undefined" && console.warn && console.warn(getInvalidSingletonVersionMessage(scope, key, version, requiredVersion));',
  119. "return get(scope[key][version]);"
  120. ]
  121. )};`,
  122. `var getStrictSingletonVersion = ${runtimeTemplate.basicFunction(
  123. "scope, scopeName, key, requiredVersion",
  124. [
  125. "var version = findSingletonVersionKey(scope, key);",
  126. "if (!satisfy(requiredVersion, version)) " +
  127. "throw new Error(getInvalidSingletonVersionMessage(scope, key, version, requiredVersion));",
  128. "return get(scope[key][version]);"
  129. ]
  130. )};`,
  131. `var findValidVersion = ${runtimeTemplate.basicFunction(
  132. "scope, key, requiredVersion",
  133. [
  134. "var versions = scope[key];",
  135. `var key = Object.keys(versions).reduce(${runtimeTemplate.basicFunction(
  136. "a, b",
  137. [
  138. "if (!satisfy(requiredVersion, b)) return a;",
  139. "return !a || versionLt(a, b) ? b : a;"
  140. ]
  141. )}, 0);`,
  142. "return key && versions[key]"
  143. ]
  144. )};`,
  145. `var getInvalidVersionMessage = ${runtimeTemplate.basicFunction(
  146. "scope, scopeName, key, requiredVersion",
  147. [
  148. "var versions = scope[key];",
  149. 'return "No satisfying version (" + rangeToString(requiredVersion) + ") of shared module " + key + " found in shared scope " + scopeName + ".\\n" +',
  150. `\t"Available versions: " + Object.keys(versions).map(${runtimeTemplate.basicFunction(
  151. "key",
  152. ['return key + " from " + versions[key].from;']
  153. )}).join(", ");`
  154. ]
  155. )};`,
  156. `var getValidVersion = ${runtimeTemplate.basicFunction(
  157. "scope, scopeName, key, requiredVersion",
  158. [
  159. "var entry = findValidVersion(scope, key, requiredVersion);",
  160. "if(entry) return get(entry);",
  161. "throw new Error(getInvalidVersionMessage(scope, scopeName, key, requiredVersion));"
  162. ]
  163. )};`,
  164. `var warnInvalidVersion = ${runtimeTemplate.basicFunction(
  165. "scope, scopeName, key, requiredVersion",
  166. [
  167. 'typeof console !== "undefined" && console.warn && console.warn(getInvalidVersionMessage(scope, scopeName, key, requiredVersion));'
  168. ]
  169. )};`,
  170. `var get = ${runtimeTemplate.basicFunction("entry", [
  171. "entry.loaded = 1;",
  172. "return entry.get()"
  173. ])};`,
  174. `var init = ${runtimeTemplate.returningFunction(
  175. Template.asString([
  176. "function(scopeName, a, b, c) {",
  177. Template.indent([
  178. `var promise = ${RuntimeGlobals.initializeSharing}(scopeName);`,
  179. `if (promise && promise.then) return promise.then(fn.bind(fn, scopeName, ${RuntimeGlobals.shareScopeMap}[scopeName], a, b, c));`,
  180. `return fn(scopeName, ${RuntimeGlobals.shareScopeMap}[scopeName], a, b, c);`
  181. ]),
  182. "}"
  183. ]),
  184. "fn"
  185. )};`,
  186. "",
  187. `var load = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
  188. "scopeName, scope, key",
  189. [
  190. "ensureExistence(scopeName, key);",
  191. "return get(findVersion(scope, key));"
  192. ]
  193. )});`,
  194. `var loadFallback = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
  195. "scopeName, scope, key, fallback",
  196. [
  197. `return scope && ${RuntimeGlobals.hasOwnProperty}(scope, key) ? get(findVersion(scope, key)) : fallback();`
  198. ]
  199. )});`,
  200. `var loadVersionCheck = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
  201. "scopeName, scope, key, version",
  202. [
  203. "ensureExistence(scopeName, key);",
  204. "return get(findValidVersion(scope, key, version) || warnInvalidVersion(scope, scopeName, key, version) || findVersion(scope, key));"
  205. ]
  206. )});`,
  207. `var loadSingleton = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
  208. "scopeName, scope, key",
  209. [
  210. "ensureExistence(scopeName, key);",
  211. "return getSingleton(scope, scopeName, key);"
  212. ]
  213. )});`,
  214. `var loadSingletonVersionCheck = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
  215. "scopeName, scope, key, version",
  216. [
  217. "ensureExistence(scopeName, key);",
  218. "return getSingletonVersion(scope, scopeName, key, version);"
  219. ]
  220. )});`,
  221. `var loadStrictVersionCheck = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
  222. "scopeName, scope, key, version",
  223. [
  224. "ensureExistence(scopeName, key);",
  225. "return getValidVersion(scope, scopeName, key, version);"
  226. ]
  227. )});`,
  228. `var loadStrictSingletonVersionCheck = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
  229. "scopeName, scope, key, version",
  230. [
  231. "ensureExistence(scopeName, key);",
  232. "return getStrictSingletonVersion(scope, scopeName, key, version);"
  233. ]
  234. )});`,
  235. `var loadVersionCheckFallback = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
  236. "scopeName, scope, key, version, fallback",
  237. [
  238. `if(!scope || !${RuntimeGlobals.hasOwnProperty}(scope, key)) return fallback();`,
  239. "return get(findValidVersion(scope, key, version) || warnInvalidVersion(scope, scopeName, key, version) || findVersion(scope, key));"
  240. ]
  241. )});`,
  242. `var loadSingletonFallback = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
  243. "scopeName, scope, key, fallback",
  244. [
  245. `if(!scope || !${RuntimeGlobals.hasOwnProperty}(scope, key)) return fallback();`,
  246. "return getSingleton(scope, scopeName, key);"
  247. ]
  248. )});`,
  249. `var loadSingletonVersionCheckFallback = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
  250. "scopeName, scope, key, version, fallback",
  251. [
  252. `if(!scope || !${RuntimeGlobals.hasOwnProperty}(scope, key)) return fallback();`,
  253. "return getSingletonVersion(scope, scopeName, key, version);"
  254. ]
  255. )});`,
  256. `var loadStrictVersionCheckFallback = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
  257. "scopeName, scope, key, version, fallback",
  258. [
  259. `var entry = scope && ${RuntimeGlobals.hasOwnProperty}(scope, key) && findValidVersion(scope, key, version);`,
  260. `return entry ? get(entry) : fallback();`
  261. ]
  262. )});`,
  263. `var loadStrictSingletonVersionCheckFallback = /*#__PURE__*/ init(${runtimeTemplate.basicFunction(
  264. "scopeName, scope, key, version, fallback",
  265. [
  266. `if(!scope || !${RuntimeGlobals.hasOwnProperty}(scope, key)) return fallback();`,
  267. "return getStrictSingletonVersion(scope, scopeName, key, version);"
  268. ]
  269. )});`,
  270. "var installedModules = {};",
  271. "var moduleToHandlerMapping = {",
  272. Template.indent(
  273. Array.from(
  274. moduleIdToSourceMapping,
  275. ([key, source]) => `${JSON.stringify(key)}: ${source.source()}`
  276. ).join(",\n")
  277. ),
  278. "};",
  279. initialConsumes.length > 0
  280. ? Template.asString([
  281. `var initialConsumes = ${JSON.stringify(initialConsumes)};`,
  282. `initialConsumes.forEach(${runtimeTemplate.basicFunction("id", [
  283. `${
  284. RuntimeGlobals.moduleFactories
  285. }[id] = ${runtimeTemplate.basicFunction("module", [
  286. "// Handle case when module is used sync",
  287. "installedModules[id] = 0;",
  288. `delete ${RuntimeGlobals.moduleCache}[id];`,
  289. "var factory = moduleToHandlerMapping[id]();",
  290. 'if(typeof factory !== "function") throw new Error("Shared module is not available for eager consumption: " + id);',
  291. `module.exports = factory();`
  292. ])}`
  293. ])});`
  294. ])
  295. : "// no consumes in initial chunks",
  296. this._runtimeRequirements.has(RuntimeGlobals.ensureChunkHandlers)
  297. ? Template.asString([
  298. `var chunkMapping = ${JSON.stringify(
  299. chunkToModuleMapping,
  300. null,
  301. "\t"
  302. )};`,
  303. `${
  304. RuntimeGlobals.ensureChunkHandlers
  305. }.consumes = ${runtimeTemplate.basicFunction("chunkId, promises", [
  306. `if(${RuntimeGlobals.hasOwnProperty}(chunkMapping, chunkId)) {`,
  307. Template.indent([
  308. `chunkMapping[chunkId].forEach(${runtimeTemplate.basicFunction(
  309. "id",
  310. [
  311. `if(${RuntimeGlobals.hasOwnProperty}(installedModules, id)) return promises.push(installedModules[id]);`,
  312. `var onFactory = ${runtimeTemplate.basicFunction(
  313. "factory",
  314. [
  315. "installedModules[id] = 0;",
  316. `${
  317. RuntimeGlobals.moduleFactories
  318. }[id] = ${runtimeTemplate.basicFunction("module", [
  319. `delete ${RuntimeGlobals.moduleCache}[id];`,
  320. "module.exports = factory();"
  321. ])}`
  322. ]
  323. )};`,
  324. `var onError = ${runtimeTemplate.basicFunction("error", [
  325. "delete installedModules[id];",
  326. `${
  327. RuntimeGlobals.moduleFactories
  328. }[id] = ${runtimeTemplate.basicFunction("module", [
  329. `delete ${RuntimeGlobals.moduleCache}[id];`,
  330. "throw error;"
  331. ])}`
  332. ])};`,
  333. "try {",
  334. Template.indent([
  335. "var promise = moduleToHandlerMapping[id]();",
  336. "if(promise.then) {",
  337. Template.indent(
  338. "promises.push(installedModules[id] = promise.then(onFactory)['catch'](onError));"
  339. ),
  340. "} else onFactory(promise);"
  341. ]),
  342. "} catch(e) { onError(e); }"
  343. ]
  344. )});`
  345. ]),
  346. "}"
  347. ])}`
  348. ])
  349. : "// no chunk loading of consumes"
  350. ]);
  351. }
  352. }
  353. module.exports = ConsumeSharedRuntimeModule;