/* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ "use strict"; const RuntimeGlobals = require("../RuntimeGlobals"); const RuntimeModule = require("../RuntimeModule"); const Template = require("../Template"); const { parseVersionRuntimeCode, versionLtRuntimeCode, rangeToStringRuntimeCode, satisfyRuntimeCode } = require("../util/semver"); /** @typedef {import("webpack-sources").Source} Source */ /** @typedef {import("../Chunk")} Chunk */ /** @typedef {import("../Chunk").ChunkId} ChunkId */ /** @typedef {import("../ChunkGraph")} ChunkGraph */ /** @typedef {import("../Compilation")} Compilation */ /** @typedef {import("../Module")} Module */ /** @typedef {import("../Module").ReadOnlyRuntimeRequirements} ReadOnlyRuntimeRequirements */ /** @typedef {import("./ConsumeSharedModule")} ConsumeSharedModule */ class ConsumeSharedRuntimeModule extends RuntimeModule { /** * @param {ReadOnlyRuntimeRequirements} runtimeRequirements runtime requirements */ constructor(runtimeRequirements) { super("consumes", RuntimeModule.STAGE_ATTACH); this._runtimeRequirements = runtimeRequirements; } /** * @returns {string | null} runtime code */ generate() { const compilation = /** @type {Compilation} */ (this.compilation); const chunkGraph = /** @type {ChunkGraph} */ (this.chunkGraph); const { runtimeTemplate, codeGenerationResults } = compilation; /** @type {Record} */ const chunkToModuleMapping = {}; /** @type {Map} */ const moduleIdToSourceMapping = new Map(); /** @type {(string | number)[]} */ const initialConsumes = []; /** * * @param {Iterable} modules modules * @param {Chunk} chunk the chunk * @param {(string | number)[]} list list of ids */ const addModules = (modules, chunk, list) => { for (const m of modules) { const module = /** @type {ConsumeSharedModule} */ (m); const id = chunkGraph.getModuleId(module); list.push(id); moduleIdToSourceMapping.set( id, codeGenerationResults.getSource( module, chunk.runtime, "consume-shared" ) ); } }; for (const chunk of /** @type {Chunk} */ (this.chunk).getAllAsyncChunks()) { const modules = chunkGraph.getChunkModulesIterableBySourceType( chunk, "consume-shared" ); if (!modules) continue; addModules( modules, chunk, (chunkToModuleMapping[/** @type {ChunkId} */ (chunk.id)] = []) ); } for (const chunk of /** @type {Chunk} */ ( this.chunk ).getAllInitialChunks()) { const modules = chunkGraph.getChunkModulesIterableBySourceType( chunk, "consume-shared" ); if (!modules) continue; addModules(modules, chunk, initialConsumes); } if (moduleIdToSourceMapping.size === 0) return null; return Template.asString([ parseVersionRuntimeCode(runtimeTemplate), versionLtRuntimeCode(runtimeTemplate), rangeToStringRuntimeCode(runtimeTemplate), satisfyRuntimeCode(runtimeTemplate), `var ensureExistence = ${runtimeTemplate.basicFunction("scopeName, key", [ `var scope = ${RuntimeGlobals.shareScopeMap}[scopeName];`, `if(!scope || !${RuntimeGlobals.hasOwnProperty}(scope, key)) throw new Error("Shared module " + key + " doesn't exist in shared scope " + scopeName);`, "return scope;" ])};`, `var findVersion = ${runtimeTemplate.basicFunction("scope, key", [ "var versions = scope[key];", `var key = Object.keys(versions).reduce(${runtimeTemplate.basicFunction( "a, b", ["return !a || versionLt(a, b) ? b : a;"] )}, 0);`, "return key && versions[key]" ])};`, `var findSingletonVersionKey = ${runtimeTemplate.basicFunction( "scope, key", [ "var versions = scope[key];", `return Object.keys(versions).reduce(${runtimeTemplate.basicFunction( "a, b", ["return !a || (!versions[a].loaded && versionLt(a, b)) ? b : a;"] )}, 0);` ] )};`, `var getInvalidSingletonVersionMessage = ${runtimeTemplate.basicFunction( "scope, key, version, requiredVersion", [ `return "Unsatisfied version " + version + " from " + (version && scope[key][version].from) + " of shared singleton module " + key + " (required " + rangeToString(requiredVersion) + ")"` ] )};`, `var getSingleton = ${runtimeTemplate.basicFunction( "scope, scopeName, key, requiredVersion", [ "var version = findSingletonVersionKey(scope, key);", "return get(scope[key][version]);" ] )};`, `var getSingletonVersion = ${runtimeTemplate.basicFunction( "scope, scopeName, key, requiredVersion", [ "var version = findSingletonVersionKey(scope, key);", "if (!satisfy(requiredVersion, version)) warn(getInvalidSingletonVersionMessage(scope, key, version, requiredVersion));", "return get(scope[key][version]);" ] )};`, `var getStrictSingletonVersion = ${runtimeTemplate.basicFunction( "scope, scopeName, key, requiredVersion", [ "var version = findSingletonVersionKey(scope, key);", "if (!satisfy(requiredVersion, version)) " + "throw new Error(getInvalidSingletonVersionMessage(scope, key, version, requiredVersion));", "return get(scope[key][version]);" ] )};`, `var findValidVersion = ${runtimeTemplate.basicFunction( "scope, key, requiredVersion", [ "var versions = scope[key];", `var key = Object.keys(versions).reduce(${runtimeTemplate.basicFunction( "a, b", [ "if (!satisfy(requiredVersion, b)) return a;", "return !a || versionLt(a, b) ? b : a;" ] )}, 0);`, "return key && versions[key]" ] )};`, `var getInvalidVersionMessage = ${runtimeTemplate.basicFunction( "scope, scopeName, key, requiredVersion", [ "var versions = scope[key];", 'return "No satisfying version (" + rangeToString(requiredVersion) + ") of shared module " + key + " found in shared scope " + scopeName + ".\\n" +', `\t"Available versions: " + Object.keys(versions).map(${runtimeTemplate.basicFunction( "key", ['return key + " from " + versions[key].from;'] )}).join(", ");` ] )};`, `var getValidVersion = ${runtimeTemplate.basicFunction( "scope, scopeName, key, requiredVersion", [ "var entry = findValidVersion(scope, key, requiredVersion);", "if(entry) return get(entry);", "throw new Error(getInvalidVersionMessage(scope, scopeName, key, requiredVersion));" ] )};`, `var warn = ${ compilation.outputOptions.ignoreBrowserWarnings ? runtimeTemplate.basicFunction("", "") : runtimeTemplate.basicFunction("msg", [ 'if (typeof console !== "undefined" && console.warn) console.warn(msg);' ]) };`, `var warnInvalidVersion = ${runtimeTemplate.basicFunction( "scope, scopeName, key, requiredVersion", [ "warn(getInvalidVersionMessage(scope, scopeName, key, requiredVersion));" ] )};`, `var get = ${runtimeTemplate.basicFunction("entry", [ "entry.loaded = 1;", "return entry.get()" ])};`, `var init = ${runtimeTemplate.returningFunction( Template.asString([ "function(scopeName, a, b, c) {", Template.indent([ `var promise = ${RuntimeGlobals.initializeSharing}(scopeName);`, `if (promise && promise.then) return promise.then(fn.bind(fn, scopeName, ${RuntimeGlobals.shareScopeMap}[scopeName], a, b, c));`, `return fn(scopeName, ${RuntimeGlobals.shareScopeMap}[scopeName], a, b, c);` ]), "}" ]), "fn" )};`, "", `var load = /*#__PURE__*/ init(${runtimeTemplate.basicFunction( "scopeName, scope, key", [ "ensureExistence(scopeName, key);", "return get(findVersion(scope, key));" ] )});`, `var loadFallback = /*#__PURE__*/ init(${runtimeTemplate.basicFunction( "scopeName, scope, key, fallback", [ `return scope && ${RuntimeGlobals.hasOwnProperty}(scope, key) ? get(findVersion(scope, key)) : fallback();` ] )});`, `var loadVersionCheck = /*#__PURE__*/ init(${runtimeTemplate.basicFunction( "scopeName, scope, key, version", [ "ensureExistence(scopeName, key);", "return get(findValidVersion(scope, key, version) || warnInvalidVersion(scope, scopeName, key, version) || findVersion(scope, key));" ] )});`, `var loadSingleton = /*#__PURE__*/ init(${runtimeTemplate.basicFunction( "scopeName, scope, key", [ "ensureExistence(scopeName, key);", "return getSingleton(scope, scopeName, key);" ] )});`, `var loadSingletonVersionCheck = /*#__PURE__*/ init(${runtimeTemplate.basicFunction( "scopeName, scope, key, version", [ "ensureExistence(scopeName, key);", "return getSingletonVersion(scope, scopeName, key, version);" ] )});`, `var loadStrictVersionCheck = /*#__PURE__*/ init(${runtimeTemplate.basicFunction( "scopeName, scope, key, version", [ "ensureExistence(scopeName, key);", "return getValidVersion(scope, scopeName, key, version);" ] )});`, `var loadStrictSingletonVersionCheck = /*#__PURE__*/ init(${runtimeTemplate.basicFunction( "scopeName, scope, key, version", [ "ensureExistence(scopeName, key);", "return getStrictSingletonVersion(scope, scopeName, key, version);" ] )});`, `var loadVersionCheckFallback = /*#__PURE__*/ init(${runtimeTemplate.basicFunction( "scopeName, scope, key, version, fallback", [ `if(!scope || !${RuntimeGlobals.hasOwnProperty}(scope, key)) return fallback();`, "return get(findValidVersion(scope, key, version) || warnInvalidVersion(scope, scopeName, key, version) || findVersion(scope, key));" ] )});`, `var loadSingletonFallback = /*#__PURE__*/ init(${runtimeTemplate.basicFunction( "scopeName, scope, key, fallback", [ `if(!scope || !${RuntimeGlobals.hasOwnProperty}(scope, key)) return fallback();`, "return getSingleton(scope, scopeName, key);" ] )});`, `var loadSingletonVersionCheckFallback = /*#__PURE__*/ init(${runtimeTemplate.basicFunction( "scopeName, scope, key, version, fallback", [ `if(!scope || !${RuntimeGlobals.hasOwnProperty}(scope, key)) return fallback();`, "return getSingletonVersion(scope, scopeName, key, version);" ] )});`, `var loadStrictVersionCheckFallback = /*#__PURE__*/ init(${runtimeTemplate.basicFunction( "scopeName, scope, key, version, fallback", [ `var entry = scope && ${RuntimeGlobals.hasOwnProperty}(scope, key) && findValidVersion(scope, key, version);`, `return entry ? get(entry) : fallback();` ] )});`, `var loadStrictSingletonVersionCheckFallback = /*#__PURE__*/ init(${runtimeTemplate.basicFunction( "scopeName, scope, key, version, fallback", [ `if(!scope || !${RuntimeGlobals.hasOwnProperty}(scope, key)) return fallback();`, "return getStrictSingletonVersion(scope, scopeName, key, version);" ] )});`, "var installedModules = {};", "var moduleToHandlerMapping = {", Template.indent( Array.from( moduleIdToSourceMapping, ([key, source]) => `${JSON.stringify(key)}: ${source.source()}` ).join(",\n") ), "};", initialConsumes.length > 0 ? Template.asString([ `var initialConsumes = ${JSON.stringify(initialConsumes)};`, `initialConsumes.forEach(${runtimeTemplate.basicFunction("id", [ `${ RuntimeGlobals.moduleFactories }[id] = ${runtimeTemplate.basicFunction("module", [ "// Handle case when module is used sync", "installedModules[id] = 0;", `delete ${RuntimeGlobals.moduleCache}[id];`, "var factory = moduleToHandlerMapping[id]();", 'if(typeof factory !== "function") throw new Error("Shared module is not available for eager consumption: " + id);', `module.exports = factory();` ])}` ])});` ]) : "// no consumes in initial chunks", this._runtimeRequirements.has(RuntimeGlobals.ensureChunkHandlers) ? Template.asString([ `var chunkMapping = ${JSON.stringify( chunkToModuleMapping, null, "\t" )};`, "var startedInstallModules = {};", `${ RuntimeGlobals.ensureChunkHandlers }.consumes = ${runtimeTemplate.basicFunction("chunkId, promises", [ `if(${RuntimeGlobals.hasOwnProperty}(chunkMapping, chunkId)) {`, Template.indent([ `chunkMapping[chunkId].forEach(${runtimeTemplate.basicFunction( "id", [ `if(${RuntimeGlobals.hasOwnProperty}(installedModules, id)) return promises.push(installedModules[id]);`, "if(!startedInstallModules[id]) {", `var onFactory = ${runtimeTemplate.basicFunction( "factory", [ "installedModules[id] = 0;", `${ RuntimeGlobals.moduleFactories }[id] = ${runtimeTemplate.basicFunction("module", [ `delete ${RuntimeGlobals.moduleCache}[id];`, "module.exports = factory();" ])}` ] )};`, "startedInstallModules[id] = true;", `var onError = ${runtimeTemplate.basicFunction("error", [ "delete installedModules[id];", `${ RuntimeGlobals.moduleFactories }[id] = ${runtimeTemplate.basicFunction("module", [ `delete ${RuntimeGlobals.moduleCache}[id];`, "throw error;" ])}` ])};`, "try {", Template.indent([ "var promise = moduleToHandlerMapping[id]();", "if(promise.then) {", Template.indent( "promises.push(installedModules[id] = promise.then(onFactory)['catch'](onError));" ), "} else onFactory(promise);" ]), "} catch(e) { onError(e); }", "}" ] )});` ]), "}" ])}` ]) : "// no chunk loading of consumes" ]); } } module.exports = ConsumeSharedRuntimeModule;