2020-05-23 22:08:51 +08:00
|
|
|
/*
|
|
|
|
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");
|
2020-07-02 19:13:13 +08:00
|
|
|
const {
|
|
|
|
compareModulesByIdentifier,
|
|
|
|
compareStrings
|
|
|
|
} = require("../util/comparators");
|
2020-05-23 22:08:51 +08:00
|
|
|
|
|
|
|
class ShareRuntimeModule extends RuntimeModule {
|
|
|
|
constructor() {
|
|
|
|
super("sharing");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @returns {string} runtime code
|
|
|
|
*/
|
|
|
|
generate() {
|
|
|
|
const {
|
|
|
|
runtimeTemplate,
|
|
|
|
chunkGraph,
|
|
|
|
codeGenerationResults
|
|
|
|
} = this.compilation;
|
2020-05-26 23:11:21 +08:00
|
|
|
/** @type {Map<string, Map<number, Set<string>>>} */
|
2020-05-23 22:08:51 +08:00
|
|
|
const initCodePerScope = new Map();
|
|
|
|
for (const chunk of this.chunk.getAllReferencedChunks()) {
|
2020-07-02 19:13:13 +08:00
|
|
|
const modules = chunkGraph.getOrderedChunkModulesIterableBySourceType(
|
2020-05-23 22:08:51 +08:00
|
|
|
chunk,
|
2020-07-02 19:13:13 +08:00
|
|
|
"share-init",
|
|
|
|
compareModulesByIdentifier
|
2020-05-23 22:08:51 +08:00
|
|
|
);
|
|
|
|
if (!modules) continue;
|
|
|
|
for (const m of modules) {
|
|
|
|
const codeGen = codeGenerationResults.get(m);
|
|
|
|
if (!codeGen) continue;
|
|
|
|
const data = codeGen.data && codeGen.data.get("share-init");
|
|
|
|
if (!data) continue;
|
|
|
|
for (const item of data) {
|
2020-05-26 23:11:21 +08:00
|
|
|
const { shareScope, initStage, init } = item;
|
|
|
|
let stages = initCodePerScope.get(shareScope);
|
|
|
|
if (stages === undefined) {
|
|
|
|
initCodePerScope.set(shareScope, (stages = new Map()));
|
|
|
|
}
|
|
|
|
let list = stages.get(initStage || 0);
|
2020-05-23 22:08:51 +08:00
|
|
|
if (list === undefined) {
|
2020-05-26 23:11:21 +08:00
|
|
|
stages.set(initStage || 0, (list = new Set()));
|
2020-05-23 22:08:51 +08:00
|
|
|
}
|
2020-05-26 23:11:21 +08:00
|
|
|
list.add(init);
|
2020-05-23 22:08:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Template.asString([
|
|
|
|
`${RuntimeGlobals.shareScopeMap} = {};`,
|
|
|
|
"var initPromises = {};",
|
2020-05-26 23:11:21 +08:00
|
|
|
`${RuntimeGlobals.initializeSharing} = ${runtimeTemplate.basicFunction(
|
|
|
|
"name",
|
|
|
|
[
|
|
|
|
"// only runs once",
|
|
|
|
"if(initPromises[name]) return initPromises[name];",
|
|
|
|
"// handling circular init calls",
|
|
|
|
"initPromises[name] = 1;",
|
|
|
|
"// creates a new share scope if needed",
|
|
|
|
`if(!${RuntimeGlobals.hasOwnProperty}(${RuntimeGlobals.shareScopeMap}, name)) ${RuntimeGlobals.shareScopeMap}[name] = {};`,
|
|
|
|
"// runs all init snippets from all modules reachable",
|
|
|
|
`var scope = ${RuntimeGlobals.shareScopeMap}[name];`,
|
|
|
|
`var warn = ${runtimeTemplate.returningFunction(
|
|
|
|
'typeof console !== "undefined" && console.warn && console.warn(msg);',
|
|
|
|
"msg"
|
|
|
|
)};`,
|
|
|
|
`var register = ${runtimeTemplate.basicFunction(
|
2020-05-27 20:42:05 +08:00
|
|
|
"name, version, factory, currentName",
|
2020-05-26 23:11:21 +08:00
|
|
|
[
|
2020-05-27 20:42:05 +08:00
|
|
|
"version = version || [];",
|
|
|
|
"currentName = name;",
|
2020-05-26 23:11:21 +08:00
|
|
|
`var versionConflict = ${runtimeTemplate.returningFunction(
|
|
|
|
'warn("Version conflict for shared modules: " + name + " " + (v && v.join(".")) + " <=> " + (version && version.join(".")));'
|
|
|
|
)};`,
|
2020-05-27 20:42:05 +08:00
|
|
|
`var registerCurrent = ${runtimeTemplate.basicFunction("", [
|
|
|
|
"if(scope[currentName]) {",
|
2020-05-23 22:08:51 +08:00
|
|
|
Template.indent([
|
2020-05-27 20:42:05 +08:00
|
|
|
"var v = scope[currentName].version || [];",
|
|
|
|
"for(var i = 0; i < version.length && i < v.length; i++) {",
|
2020-05-23 22:08:51 +08:00
|
|
|
Template.indent([
|
2020-05-26 23:11:21 +08:00
|
|
|
"if(v[i] != version[i]) { // loose equal is intentional to match string and number",
|
|
|
|
Template.indent([
|
|
|
|
'if(typeof v[i] === "string" || typeof version[i] === "string") return versionConflict();',
|
|
|
|
"if(v[i] > version[i]) return;",
|
2020-05-27 20:42:05 +08:00
|
|
|
"if(v[i] < version[i]) { i = -1; break; }"
|
2020-05-26 23:11:21 +08:00
|
|
|
]),
|
|
|
|
"}"
|
2020-05-23 22:08:51 +08:00
|
|
|
]),
|
2020-05-26 23:11:21 +08:00
|
|
|
"}",
|
2020-05-27 20:42:05 +08:00
|
|
|
"if(i >= 0 && version.length <= v.length) return;",
|
|
|
|
'if(scope[currentName].loaded) return warn("Ignoring providing of already used shared module: " + name);'
|
2020-05-23 22:08:51 +08:00
|
|
|
]),
|
2020-05-27 20:42:05 +08:00
|
|
|
"}",
|
|
|
|
"scope[currentName] = { get: factory, version: version };"
|
|
|
|
])};`,
|
|
|
|
"registerCurrent();",
|
|
|
|
`version.forEach(${runtimeTemplate.basicFunction("part", [
|
|
|
|
'currentName += "`" + part;',
|
|
|
|
"registerCurrent();"
|
|
|
|
])});`
|
2020-05-26 23:11:21 +08:00
|
|
|
]
|
|
|
|
)};`,
|
|
|
|
`var initExternal = ${runtimeTemplate.basicFunction("id", [
|
|
|
|
`var handleError = ${runtimeTemplate.returningFunction(
|
|
|
|
'warn("Initialization of sharing external failed: " + err)',
|
|
|
|
"err"
|
|
|
|
)};`,
|
|
|
|
"try {",
|
|
|
|
Template.indent([
|
|
|
|
"var module = __webpack_require__(id);",
|
|
|
|
"if(!module) return;",
|
|
|
|
`var initFn = ${runtimeTemplate.returningFunction(
|
|
|
|
`module && module.init && module.init(${RuntimeGlobals.shareScopeMap}[name])`,
|
|
|
|
"module"
|
|
|
|
)}`,
|
|
|
|
"if(module.then) return promises.push(module.then(initFn, handleError));",
|
|
|
|
"var initResult = initFn(module);",
|
|
|
|
"if(initResult && initResult.then) return promises.push(initResult.catch(handleError));"
|
2020-05-23 22:08:51 +08:00
|
|
|
]),
|
2020-05-26 23:11:21 +08:00
|
|
|
"} catch(err) { handleError(err); }"
|
|
|
|
])}`,
|
|
|
|
"var promises = [];",
|
|
|
|
"switch(name) {",
|
2020-07-02 19:13:13 +08:00
|
|
|
...Array.from(initCodePerScope)
|
|
|
|
.sort(([a], [b]) => compareStrings(a, b))
|
|
|
|
.map(([name, stages]) =>
|
|
|
|
Template.indent([
|
|
|
|
`case ${JSON.stringify(name)}: {`,
|
|
|
|
Template.indent(
|
|
|
|
Array.from(stages)
|
|
|
|
.sort(([a], [b]) => a - b)
|
|
|
|
.map(([, initCode]) =>
|
|
|
|
Template.asString(Array.from(initCode))
|
|
|
|
)
|
|
|
|
),
|
|
|
|
"}",
|
|
|
|
"break;"
|
|
|
|
])
|
|
|
|
),
|
2020-05-26 23:11:21 +08:00
|
|
|
"}",
|
|
|
|
`return promises.length && (initPromises[name] = Promise.all(promises).then(${runtimeTemplate.returningFunction(
|
|
|
|
"initPromises[name] = 1"
|
|
|
|
)}));`
|
|
|
|
]
|
|
|
|
)};`
|
2020-05-23 22:08:51 +08:00
|
|
|
]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = ShareRuntimeModule;
|