ConsumeSharedRuntimeModule.js 13 KB

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