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