Merge pull request #10835 from webpack/mfe/bugs-and-dx
Module federation bugfixes and DX
This commit is contained in:
commit
e87ebdda53
|
@ -80,8 +80,6 @@ const { arrayToSetDeprecation } = require("./util/deprecation");
|
|||
/** @typedef {import("./Template").RenderManifestEntry} RenderManifestEntry */
|
||||
/** @typedef {import("./Template").RenderManifestOptions} RenderManifestOptions */
|
||||
/** @typedef {import("./WebpackError")} WebpackError */
|
||||
/** @typedef {import("./dependencies/DllEntryDependency")} DllEntryDependency */
|
||||
/** @typedef {import("./dependencies/EntryDependency")} EntryDependency */
|
||||
/** @typedef {import("./stats/StatsFactory")} StatsFactory */
|
||||
/** @typedef {import("./stats/StatsPrinter")} StatsPrinter */
|
||||
/** @typedef {import("./util/Hash")} Hash */
|
||||
|
@ -147,7 +145,7 @@ const { arrayToSetDeprecation } = require("./util/deprecation");
|
|||
|
||||
/**
|
||||
* @typedef {Object} EntryData
|
||||
* @property {EntryDependency[]} dependencies dependencies of the entrypoint
|
||||
* @property {Dependency[]} dependencies dependencies of the entrypoint
|
||||
* @property {EntryOptions} options options of the entrypoint
|
||||
*/
|
||||
|
||||
|
@ -1416,7 +1414,7 @@ class Compilation {
|
|||
/**
|
||||
*
|
||||
* @param {string} context context path for entry
|
||||
* @param {EntryDependency} entry entry dependency being created
|
||||
* @param {Dependency} entry entry dependency being created
|
||||
* @param {string | EntryOptions} optionsOrName options or deprecated name of entry
|
||||
* @param {ModuleCallback} callback callback function
|
||||
* @returns {void} returns
|
||||
|
@ -1617,7 +1615,7 @@ class Compilation {
|
|||
connectChunkGroupAndChunk(entrypoint, chunk);
|
||||
|
||||
for (const dep of dependencies) {
|
||||
entrypoint.addOrigin(null, { name }, dep.request);
|
||||
entrypoint.addOrigin(null, { name }, /** @type {any} */ (dep).request);
|
||||
|
||||
const module = this.moduleGraph.getModule(dep);
|
||||
if (module) {
|
||||
|
|
|
@ -5,19 +5,28 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const EntryDependency = require("../dependencies/EntryDependency");
|
||||
const Dependency = require("../Dependency");
|
||||
const makeSerializable = require("../util/makeSerializable");
|
||||
|
||||
class ContainerEntryDependency extends EntryDependency {
|
||||
class ContainerEntryDependency extends Dependency {
|
||||
/**
|
||||
* @param {string} name entry name
|
||||
* @param {[string, string][]} exposes list of exposed modules
|
||||
*/
|
||||
constructor(exposes) {
|
||||
super(null);
|
||||
constructor(name, exposes) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.exposes = exposes;
|
||||
this.optional = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string | null} an identifier to merge equal requests
|
||||
*/
|
||||
getResourceIdentifier() {
|
||||
return `container-entry-${this.name}`;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return "container entry";
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ const ContainerExposedDependency = require("./ContainerExposedDependency");
|
|||
/** @typedef {import("../Compilation")} Compilation */
|
||||
/** @typedef {import("../Module").CodeGenerationContext} CodeGenerationContext */
|
||||
/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
|
||||
/** @typedef {import("../Module").LibIdentOptions} LibIdentOptions */
|
||||
/** @typedef {import("../Module").NeedBuildContext} NeedBuildContext */
|
||||
/** @typedef {import("../RequestShortener")} RequestShortener */
|
||||
/** @typedef {import("../ResolverFactory").ResolverWithOptions} ResolverWithOptions */
|
||||
|
@ -31,11 +32,13 @@ const SOURCE_TYPES = new Set(["javascript"]);
|
|||
|
||||
class ContainerEntryModule extends Module {
|
||||
/**
|
||||
* @param {string} name container entry name
|
||||
* @param {[string, string][]} exposes list of exposed modules
|
||||
*/
|
||||
constructor(exposes) {
|
||||
constructor(name, exposes) {
|
||||
super("javascript/dynamic", null);
|
||||
this.exposes = exposes;
|
||||
this._name = name;
|
||||
this._exposes = exposes;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,7 +52,7 @@ class ContainerEntryModule extends Module {
|
|||
* @returns {string} a unique identifier of the module
|
||||
*/
|
||||
identifier() {
|
||||
return `container entry ${JSON.stringify(this.exposes)}`;
|
||||
return `container entry ${JSON.stringify(this._exposes)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,6 +63,14 @@ class ContainerEntryModule extends Module {
|
|||
return `container entry`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LibIdentOptions} options options
|
||||
* @returns {string | null} an identifier for library inclusion
|
||||
*/
|
||||
libIdent(options) {
|
||||
return `webpack/container/entry/${this._name}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NeedBuildContext} context context info
|
||||
* @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
|
||||
|
@ -88,7 +99,7 @@ class ContainerEntryModule extends Module {
|
|||
const dependencies = [];
|
||||
|
||||
let idx = -1;
|
||||
for (const [name, request] of this.exposes) {
|
||||
for (const [name, request] of this._exposes) {
|
||||
++idx;
|
||||
const dep = new ContainerExposedDependency(name, request);
|
||||
dep.loc = {
|
||||
|
@ -172,7 +183,7 @@ class ContainerEntryModule extends Module {
|
|||
"? moduleMap[module]()",
|
||||
`: Promise.resolve().then(${runtimeTemplate.basicFunction(
|
||||
"",
|
||||
'throw new Error("Module " + module + " does not exist in container.");'
|
||||
"throw new Error('Module \"' + module + '\" does not exist in container.');"
|
||||
)})`
|
||||
])
|
||||
]),
|
||||
|
@ -181,7 +192,7 @@ class ContainerEntryModule extends Module {
|
|||
`var override = ${runtimeTemplate.basicFunction(
|
||||
"override",
|
||||
`Object.assign(${RuntimeGlobals.overrides}, override);`
|
||||
)}`,
|
||||
)};`,
|
||||
"",
|
||||
"// This exports getters to disallow modifications",
|
||||
`${RuntimeGlobals.definePropertyGetters}(exports, {`,
|
||||
|
@ -213,13 +224,13 @@ class ContainerEntryModule extends Module {
|
|||
|
||||
serialize(context) {
|
||||
const { write } = context;
|
||||
write(this.exposes);
|
||||
write(this._exposes);
|
||||
super.serialize(context);
|
||||
}
|
||||
|
||||
deserialize(context) {
|
||||
const { read } = context;
|
||||
this.exposes = read();
|
||||
this._exposes = read();
|
||||
super.deserialize(context);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ module.exports = class ContainerEntryModuleFactory extends ModuleFactory {
|
|||
create({ dependencies: [dependency] }, callback) {
|
||||
const dep = /** @type {ContainerEntryDependency} */ (dependency);
|
||||
callback(null, {
|
||||
module: new ContainerEntryModule(dep.exposes)
|
||||
module: new ContainerEntryModule(dep.name, dep.exposes)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -47,12 +47,10 @@ module.exports = class ContainerPlugin {
|
|||
|
||||
compiler.options.output.enabledLibraryTypes.push(library.type);
|
||||
|
||||
if (overridables) {
|
||||
new OverridablesPlugin(overridables).apply(compiler);
|
||||
}
|
||||
new OverridablesPlugin(overridables || []).apply(compiler);
|
||||
|
||||
compiler.hooks.make.tapAsync(PLUGIN_NAME, (compilation, callback) => {
|
||||
const dep = new ContainerEntryDependency(exposes);
|
||||
const dep = new ContainerEntryDependency(name, exposes);
|
||||
dep.loc = { name };
|
||||
compilation.addEntry(
|
||||
compilation.options.context,
|
||||
|
|
|
@ -11,10 +11,10 @@ const ExternalsPlugin = require("../ExternalsPlugin");
|
|||
const RuntimeGlobals = require("../RuntimeGlobals");
|
||||
const RemoteModule = require("./RemoteModule");
|
||||
const RemoteOverrideDependency = require("./RemoteOverrideDependency");
|
||||
const RemoteOverrideModuleFactory = require("./RemoteOverrideModuleFactory");
|
||||
const RemoteOverridesDependency = require("./RemoteOverridesDependency");
|
||||
const RemoteOverridesModuleFactory = require("./RemoteOverridesModuleFactory");
|
||||
const RemoteRuntimeModule = require("./RemoteRuntimeModule");
|
||||
const RemoteToExternalDependency = require("./RemoteToExternalDependency");
|
||||
const RemoteToOverrideDependency = require("./RemoteToOverrideDependency");
|
||||
const parseOptions = require("./parseOptions");
|
||||
|
||||
/** @typedef {import("../../declarations/plugins/container/ContainerReferencePlugin").ContainerReferencePluginOptions} ContainerReferencePluginOptions */
|
||||
|
@ -29,7 +29,11 @@ module.exports = class ContainerReferencePlugin {
|
|||
|
||||
this._remoteType = options.remoteType;
|
||||
this._remotes = parseOptions(options.remotes || []);
|
||||
this._overrides = parseOptions(options.overrides || {});
|
||||
this._overrides = parseOptions(options.overrides || {}).sort(([a], [b]) => {
|
||||
if (a < b) return -1;
|
||||
if (b < a) return 1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -42,7 +46,7 @@ module.exports = class ContainerReferencePlugin {
|
|||
|
||||
const remoteExternals = {};
|
||||
for (const [key, value] of remotes) {
|
||||
remoteExternals[`container-reference/${key}`] = value;
|
||||
remoteExternals[`webpack/container/reference/${key}`] = value;
|
||||
}
|
||||
|
||||
new ExternalsPlugin(remoteType, remoteExternals).apply(compiler);
|
||||
|
@ -61,8 +65,8 @@ module.exports = class ContainerReferencePlugin {
|
|||
);
|
||||
|
||||
compilation.dependencyFactories.set(
|
||||
RemoteToOverrideDependency,
|
||||
new RemoteOverrideModuleFactory()
|
||||
RemoteOverridesDependency,
|
||||
new RemoteOverridesModuleFactory()
|
||||
);
|
||||
|
||||
normalModuleFactory.hooks.factorize.tap(
|
||||
|
@ -74,7 +78,7 @@ module.exports = class ContainerReferencePlugin {
|
|||
return new RemoteModule(
|
||||
data.request,
|
||||
this._overrides,
|
||||
`container-reference/${key}`,
|
||||
`webpack/container/reference/${key}`,
|
||||
data.request.slice(key.length + 1)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ const OverridableOriginalDependency = require("./OverridableOriginalDependency")
|
|||
/** @typedef {import("../Compilation")} Compilation */
|
||||
/** @typedef {import("../Module").CodeGenerationContext} CodeGenerationContext */
|
||||
/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
|
||||
/** @typedef {import("../Module").LibIdentOptions} LibIdentOptions */
|
||||
/** @typedef {import("../Module").NeedBuildContext} NeedBuildContext */
|
||||
/** @typedef {import("../RequestShortener")} RequestShortener */
|
||||
/** @typedef {import("../ResolverFactory").ResolverWithOptions} ResolverWithOptions */
|
||||
|
@ -55,6 +56,14 @@ class OverridableModule extends Module {
|
|||
return `overridable ${this.name} -> ${this.originalRequest}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LibIdentOptions} options options
|
||||
* @returns {string | null} an identifier for library inclusion
|
||||
*/
|
||||
libIdent(options) {
|
||||
return `webpack/container/overridable/${this.name}=${this.originalRequest}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NeedBuildContext} context context info
|
||||
* @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
|
||||
|
|
|
@ -8,9 +8,10 @@
|
|||
const { RawSource } = require("webpack-sources");
|
||||
const Module = require("../Module");
|
||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
||||
const createHash = require("../util/createHash");
|
||||
const makeSerializable = require("../util/makeSerializable");
|
||||
const RemoteOverridesDependency = require("./RemoteOverridesDependency");
|
||||
const RemoteToExternalDependency = require("./RemoteToExternalDependency");
|
||||
const RemoteToOverrideDependency = require("./RemoteToOverrideDependency");
|
||||
|
||||
/** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
|
||||
/** @typedef {import("../ChunkGraph")} ChunkGraph */
|
||||
|
@ -18,6 +19,7 @@ const RemoteToOverrideDependency = require("./RemoteToOverrideDependency");
|
|||
/** @typedef {import("../Compilation")} Compilation */
|
||||
/** @typedef {import("../Module").CodeGenerationContext} CodeGenerationContext */
|
||||
/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
|
||||
/** @typedef {import("../Module").LibIdentOptions} LibIdentOptions */
|
||||
/** @typedef {import("../Module").NeedBuildContext} NeedBuildContext */
|
||||
/** @typedef {import("../RequestShortener")} RequestShortener */
|
||||
/** @typedef {import("../ResolverFactory").ResolverWithOptions} ResolverWithOptions */
|
||||
|
@ -35,13 +37,20 @@ class RemoteModule extends Module {
|
|||
this.overrides = overrides;
|
||||
this.externalRequest = externalRequest;
|
||||
this.internalRequest = internalRequest;
|
||||
const hash = createHash("md4");
|
||||
for (const [key, request] of overrides) {
|
||||
hash.update(key);
|
||||
hash.update(request);
|
||||
}
|
||||
this._overridesHash = hash.digest("hex");
|
||||
this._identifier = `remote ${this.externalRequest} ${this.internalRequest} ${this._overridesHash}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} a unique identifier of the module
|
||||
*/
|
||||
identifier() {
|
||||
return `remote ${this.externalRequest} ${this.internalRequest}`;
|
||||
return this._identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,6 +61,14 @@ class RemoteModule extends Module {
|
|||
return `remote ${this.request}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LibIdentOptions} options options
|
||||
* @returns {string | null} an identifier for library inclusion
|
||||
*/
|
||||
libIdent(options) {
|
||||
return `webpack/container/remote/${this.request}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NeedBuildContext} context context info
|
||||
* @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
|
||||
|
@ -76,13 +93,8 @@ class RemoteModule extends Module {
|
|||
};
|
||||
|
||||
this.clearDependenciesAndBlocks();
|
||||
if (this.overrides.length > 0) {
|
||||
this.addDependency(
|
||||
new RemoteToOverrideDependency(this.externalRequest, this.overrides)
|
||||
);
|
||||
} else {
|
||||
this.addDependency(new RemoteToExternalDependency(this.externalRequest));
|
||||
}
|
||||
this.addDependency(new RemoteToExternalDependency(this.externalRequest));
|
||||
this.addDependency(new RemoteOverridesDependency(this.overrides));
|
||||
|
||||
callback();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const Dependency = require("../Dependency");
|
||||
const makeSerializable = require("../util/makeSerializable");
|
||||
|
||||
class RemoteOverridesDependency extends Dependency {
|
||||
constructor(overrides) {
|
||||
super();
|
||||
this.overrides = overrides;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return "remote overrides";
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string | null} an identifier to merge equal requests
|
||||
*/
|
||||
getResourceIdentifier() {
|
||||
return `remote overrides ${this.overrides
|
||||
.map(([key, request]) => `${key}=${request}`)
|
||||
.join()}`;
|
||||
}
|
||||
|
||||
serialize(context) {
|
||||
context.write(this.overrides);
|
||||
super.serialize(context);
|
||||
}
|
||||
|
||||
deserialize(context) {
|
||||
this.overrides = context.read();
|
||||
super.deserialize(context);
|
||||
}
|
||||
}
|
||||
|
||||
makeSerializable(
|
||||
RemoteOverridesDependency,
|
||||
"webpack/lib/container/RemoteOverridesDependency"
|
||||
);
|
||||
|
||||
module.exports = RemoteOverridesDependency;
|
|
@ -10,9 +10,9 @@ const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
|
|||
const Module = require("../Module");
|
||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
||||
const Template = require("../Template");
|
||||
const createHash = require("../util/createHash");
|
||||
const makeSerializable = require("../util/makeSerializable");
|
||||
const RemoteOverrideDependency = require("./RemoteOverrideDependency");
|
||||
const RemoteToExternalDependency = require("./RemoteToExternalDependency");
|
||||
|
||||
/** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
|
||||
/** @typedef {import("../Chunk")} Chunk */
|
||||
|
@ -21,6 +21,7 @@ const RemoteToExternalDependency = require("./RemoteToExternalDependency");
|
|||
/** @typedef {import("../Compilation")} Compilation */
|
||||
/** @typedef {import("../Module").CodeGenerationContext} CodeGenerationContext */
|
||||
/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
|
||||
/** @typedef {import("../Module").LibIdentOptions} LibIdentOptions */
|
||||
/** @typedef {import("../Module").NeedBuildContext} NeedBuildContext */
|
||||
/** @typedef {import("../RequestShortener")} RequestShortener */
|
||||
/** @typedef {import("../ResolverFactory").ResolverWithOptions} ResolverWithOptions */
|
||||
|
@ -30,18 +31,27 @@ const RemoteToExternalDependency = require("./RemoteToExternalDependency");
|
|||
|
||||
const TYPES = new Set(["javascript"]);
|
||||
|
||||
class RemoteOverrideModule extends Module {
|
||||
constructor(request, overrides) {
|
||||
super("remote-override-module");
|
||||
this.request = request;
|
||||
this.overrides = overrides;
|
||||
class RemoteOverridesModule extends Module {
|
||||
constructor(overrides) {
|
||||
super("remote-overrides-module");
|
||||
this._overrides = overrides;
|
||||
if (overrides.length > 0) {
|
||||
const hash = createHash("md4");
|
||||
for (const [key, request] of overrides) {
|
||||
hash.update(key);
|
||||
hash.update(request);
|
||||
}
|
||||
this._overridesHash = hash.digest("hex");
|
||||
} else {
|
||||
this._overridesHash = "empty";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} a unique identifier of the module
|
||||
*/
|
||||
identifier() {
|
||||
return `remote override ${this.request}`;
|
||||
return `remote overrides ${this._overridesHash}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,7 +59,20 @@ class RemoteOverrideModule extends Module {
|
|||
* @returns {string} a user readable identifier of the module
|
||||
*/
|
||||
readableIdentifier(requestShortener) {
|
||||
return `remote override ${this.request}`;
|
||||
return `remote overrides ${this._overrides
|
||||
.map(([key, request]) => `${key} = ${request}`)
|
||||
.join(", ")}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LibIdentOptions} options options
|
||||
* @returns {string | null} an identifier for library inclusion
|
||||
*/
|
||||
libIdent(options) {
|
||||
return `webpack/container/remote-overrides/${this._overridesHash.slice(
|
||||
0,
|
||||
6
|
||||
)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,9 +99,7 @@ class RemoteOverrideModule extends Module {
|
|||
};
|
||||
|
||||
this.clearDependenciesAndBlocks();
|
||||
this.addDependency(new RemoteToExternalDependency(this.request));
|
||||
|
||||
for (const [, value] of this.overrides) {
|
||||
for (const [, value] of this._overrides) {
|
||||
const block = new AsyncDependenciesBlock({});
|
||||
const dep = new RemoteOverrideDependency(value);
|
||||
block.addDependency(dep);
|
||||
|
@ -125,45 +146,51 @@ class RemoteOverrideModule extends Module {
|
|||
*/
|
||||
codeGeneration({ runtimeTemplate, moduleGraph, chunkGraph }) {
|
||||
const runtimeRequirements = new Set([RuntimeGlobals.module]);
|
||||
const externalDep = this.dependencies[0];
|
||||
const externalModule = moduleGraph.getModule(externalDep);
|
||||
const externalModuleId = chunkGraph.getModuleId(externalModule);
|
||||
let i = 0;
|
||||
const source = Template.asString([
|
||||
`var external = __webpack_require__(${JSON.stringify(
|
||||
externalModuleId
|
||||
)});`,
|
||||
"external.override(Object.assign({",
|
||||
Template.indent(
|
||||
this.overrides
|
||||
.map(([key, value]) => {
|
||||
const block = this.blocks[i++];
|
||||
const dep = block.dependencies[0];
|
||||
const module = moduleGraph.getModule(dep);
|
||||
return `${JSON.stringify(key)}: ${runtimeTemplate.basicFunction(
|
||||
"",
|
||||
`return ${runtimeTemplate.blockPromise({
|
||||
block,
|
||||
message: "",
|
||||
chunkGraph,
|
||||
runtimeRequirements
|
||||
})}.then(${runtimeTemplate.basicFunction(
|
||||
"",
|
||||
`return ${runtimeTemplate.returningFunction(
|
||||
runtimeTemplate.moduleRaw({
|
||||
module,
|
||||
chunkGraph,
|
||||
request: "",
|
||||
runtimeRequirements
|
||||
`module.exports = ${runtimeTemplate.basicFunction(
|
||||
"external",
|
||||
this._overrides.length > 0
|
||||
? [
|
||||
"if(external.override) external.override(Object.assign({",
|
||||
Template.indent(
|
||||
this._overrides
|
||||
.map(([key, value]) => {
|
||||
const block = this.blocks[i++];
|
||||
const dep = block.dependencies[0];
|
||||
const module = moduleGraph.getModule(dep);
|
||||
return `${JSON.stringify(
|
||||
key
|
||||
)}: ${runtimeTemplate.basicFunction(
|
||||
"",
|
||||
`return ${runtimeTemplate.blockPromise({
|
||||
block,
|
||||
message: "",
|
||||
chunkGraph,
|
||||
runtimeRequirements
|
||||
})}.then(${runtimeTemplate.basicFunction(
|
||||
"",
|
||||
`return ${runtimeTemplate.returningFunction(
|
||||
runtimeTemplate.moduleRaw({
|
||||
module,
|
||||
chunkGraph,
|
||||
request: value,
|
||||
runtimeRequirements
|
||||
})
|
||||
)}`
|
||||
)})`
|
||||
)}`;
|
||||
})
|
||||
)}`
|
||||
)})`
|
||||
)}`;
|
||||
})
|
||||
.join(",\n")
|
||||
),
|
||||
`}, ${RuntimeGlobals.overrides}));`,
|
||||
"module.exports = external;"
|
||||
.join(",\n")
|
||||
),
|
||||
`}, ${RuntimeGlobals.overrides}));`,
|
||||
"return external;"
|
||||
]
|
||||
: [
|
||||
`if(external.override) external.override(${RuntimeGlobals.overrides});`,
|
||||
"return external;"
|
||||
]
|
||||
)};`
|
||||
]);
|
||||
const sources = new Map();
|
||||
sources.set(
|
||||
|
@ -176,21 +203,21 @@ class RemoteOverrideModule extends Module {
|
|||
serialize(context) {
|
||||
const { write } = context;
|
||||
write(this.request);
|
||||
write(this.overrides);
|
||||
write(this._overrides);
|
||||
super.serialize(context);
|
||||
}
|
||||
|
||||
deserialize(context) {
|
||||
const { read } = context;
|
||||
this.request = read();
|
||||
this.overrides = read();
|
||||
this._overrides = read();
|
||||
super.deserialize(context);
|
||||
}
|
||||
}
|
||||
|
||||
makeSerializable(
|
||||
RemoteOverrideModule,
|
||||
"webpack/lib/container/RemoteOverrideModule"
|
||||
RemoteOverridesModule,
|
||||
"webpack/lib/container/RemoteOverridesModule"
|
||||
);
|
||||
|
||||
module.exports = RemoteOverrideModule;
|
||||
module.exports = RemoteOverridesModule;
|
|
@ -6,25 +6,24 @@
|
|||
"use strict";
|
||||
|
||||
const ModuleFactory = require("../ModuleFactory");
|
||||
const RemoteOverrideModule = require("./RemoteOverrideModule");
|
||||
const RemoteOverridesModule = require("./RemoteOverridesModule");
|
||||
|
||||
/** @typedef {import("../ModuleFactory").ModuleFactoryCreateData} ModuleFactoryCreateData */
|
||||
/** @typedef {import("../ModuleFactory").ModuleFactoryResult} ModuleFactoryResult */
|
||||
/** @typedef {import("./RemoteToOverrideDependency")} RemoteToOverrideDependency */
|
||||
/** @typedef {import("./RemoteOverridesDependency")} RemoteOverridesDependency */
|
||||
|
||||
class RemoteOverrideModuleFactory extends ModuleFactory {
|
||||
class RemoteOverridesModuleFactory extends ModuleFactory {
|
||||
/**
|
||||
* @param {ModuleFactoryCreateData} data data object
|
||||
* @param {function(Error=, ModuleFactoryResult=): void} callback callback
|
||||
* @returns {void}
|
||||
*/
|
||||
create(data, callback) {
|
||||
const dep = /** @type {RemoteToOverrideDependency} */ (data
|
||||
.dependencies[0]);
|
||||
const dep = /** @type {RemoteOverridesDependency} */ (data.dependencies[0]);
|
||||
callback(null, {
|
||||
module: new RemoteOverrideModule(dep.request, dep.overrides)
|
||||
module: new RemoteOverridesModule(dep.overrides)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RemoteOverrideModuleFactory;
|
||||
module.exports = RemoteOverridesModuleFactory;
|
|
@ -35,12 +35,21 @@ class RemoteRuntimeModule extends RuntimeModule {
|
|||
const name = module.internalRequest;
|
||||
const id = chunkGraph.getModuleId(module);
|
||||
const externalModule = moduleGraph.getModule(module.dependencies[0]);
|
||||
const externalModuleId = chunkGraph.getModuleId(externalModule);
|
||||
const externalModuleId =
|
||||
externalModule && chunkGraph.getModuleId(externalModule);
|
||||
const overridesModule = moduleGraph.getModule(module.dependencies[1]);
|
||||
const overridesModuleId =
|
||||
overridesModule && chunkGraph.getModuleId(overridesModule);
|
||||
remotes.push(id);
|
||||
idToExternalAndNameMapping[id] = [externalModuleId, name];
|
||||
idToExternalAndNameMapping[id] = [
|
||||
overridesModuleId,
|
||||
externalModuleId,
|
||||
name
|
||||
];
|
||||
}
|
||||
}
|
||||
return Template.asString([
|
||||
"var installedModules = {};",
|
||||
`var chunkMapping = ${JSON.stringify(
|
||||
chunkToRemotesMapping,
|
||||
null,
|
||||
|
@ -57,17 +66,35 @@ class RemoteRuntimeModule extends RuntimeModule {
|
|||
`if(${RuntimeGlobals.hasOwnProperty}(chunkMapping, chunkId)) {`,
|
||||
Template.indent([
|
||||
`chunkMapping[chunkId].forEach(${runtimeTemplate.basicFunction("id", [
|
||||
"if(__webpack_modules__[id]) return;",
|
||||
`if(${RuntimeGlobals.hasOwnProperty}(installedModules, id)) return installedModules[id] && promises.push(installedModules[id]);`,
|
||||
"var data = idToExternalAndNameMapping[id];",
|
||||
`promises.push(Promise.resolve(__webpack_require__(data[0]).get(data[1])).then(${runtimeTemplate.basicFunction(
|
||||
"factory",
|
||||
[
|
||||
`__webpack_modules__[id] = ${runtimeTemplate.basicFunction(
|
||||
"module",
|
||||
["module.exports = factory();"]
|
||||
)}`
|
||||
]
|
||||
)}))`
|
||||
`var onError = ${runtimeTemplate.basicFunction("error", [
|
||||
"if(error && typeof error.message === \"string\") error.message += '\\nwhile loading \"' + data[2] + '\" from ' + data[1];",
|
||||
`__webpack_modules__[id] = ${runtimeTemplate.basicFunction("", [
|
||||
"throw error;"
|
||||
])}`,
|
||||
"delete installedModules[id];"
|
||||
])};`,
|
||||
`var onFactory = ${runtimeTemplate.basicFunction("factory", [
|
||||
`__webpack_modules__[id] = ${runtimeTemplate.basicFunction(
|
||||
"module",
|
||||
["module.exports = factory();"]
|
||||
)}`
|
||||
])};`,
|
||||
"try {",
|
||||
Template.indent([
|
||||
"var promise = __webpack_require__(data[0])(__webpack_require__(data[1])).get(data[2]);",
|
||||
"if(promise && promise.then) {",
|
||||
Template.indent([
|
||||
`promises.push(installedModules[id] = promise.then(onFactory, onError));`
|
||||
]),
|
||||
"} else {",
|
||||
Template.indent([`onFactory(promise);`]),
|
||||
"}"
|
||||
]),
|
||||
"} catch(error) {",
|
||||
Template.indent(["onError(error);"]),
|
||||
"}"
|
||||
])});`
|
||||
]),
|
||||
"}"
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const ModuleDependency = require("../dependencies/ModuleDependency");
|
||||
const makeSerializable = require("../util/makeSerializable");
|
||||
|
||||
class RemoteToOverrideDependency extends ModuleDependency {
|
||||
constructor(request, overrides) {
|
||||
super(request);
|
||||
this.overrides = overrides;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return "remote to override";
|
||||
}
|
||||
|
||||
serialize(context) {
|
||||
context.write(this.overrides);
|
||||
super.serialize(context);
|
||||
}
|
||||
|
||||
deserialize(context) {
|
||||
this.overrides = context.read();
|
||||
super.deserialize(context);
|
||||
}
|
||||
}
|
||||
|
||||
makeSerializable(
|
||||
RemoteToOverrideDependency,
|
||||
"webpack/lib/container/RemoteToOverrideDependency"
|
||||
);
|
||||
|
||||
module.exports = RemoteToOverrideDependency;
|
|
@ -30,12 +30,12 @@ module.exports = {
|
|||
"container/RemoteModule": () => require("../container/RemoteModule"),
|
||||
"container/RemoteOverrideDependency": () =>
|
||||
require("../container/RemoteOverrideDependency"),
|
||||
"container/RemoteOverrideModule": () =>
|
||||
require("../container/RemoteOverrideModule"),
|
||||
"container/RemoteOverridesDependency": () =>
|
||||
require("../container/RemoteOverridesDependency"),
|
||||
"container/RemoteOverridesModule": () =>
|
||||
require("../container/RemoteOverridesModule"),
|
||||
"container/RemoteToExternalDependency": () =>
|
||||
require("../container/RemoteToExternalDependency"),
|
||||
"container/RemoteToOverrideDependency": () =>
|
||||
require("../container/RemoteToOverrideDependency"),
|
||||
"dependencies/AMDDefineDependency": () =>
|
||||
require("../dependencies/AMDDefineDependency"),
|
||||
"dependencies/AMDRequireArrayDependency": () =>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export * from "./b";
|
|
@ -0,0 +1 @@
|
|||
export * from "./shared";
|
|
@ -0,0 +1,23 @@
|
|||
it("should work normally (a)", () => {
|
||||
return import("./a").then(({ value }) => {
|
||||
expect(value).toBe("shared");
|
||||
});
|
||||
});
|
||||
|
||||
it("should work normally (b)", () => {
|
||||
return import("./b").then(({ value }) => {
|
||||
expect(value).toBe("shared");
|
||||
});
|
||||
});
|
||||
|
||||
it("should work normally (container-with-shared/a)", () => {
|
||||
return import("container-with-shared/a").then(({ value }) => {
|
||||
expect(value).toBe("shared");
|
||||
});
|
||||
});
|
||||
|
||||
it("should work normally (container-with-shared/b)", () => {
|
||||
return import("container-with-shared/b").then(({ value }) => {
|
||||
expect(value).toBe("shared");
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
export default Object.keys(__webpack_modules__).sort();
|
|
@ -0,0 +1 @@
|
|||
export const value = "shared";
|
|
@ -0,0 +1,21 @@
|
|||
const { ModuleFederationPlugin } = require("../../../../").container;
|
||||
|
||||
/** @type {import("../../../../").Configuration} */
|
||||
module.exports = {
|
||||
optimization: {
|
||||
chunkIds: "named",
|
||||
moduleIds: "named"
|
||||
},
|
||||
plugins: [
|
||||
new ModuleFederationPlugin({
|
||||
name: "container-with-shared",
|
||||
library: { type: "commonjs-module" },
|
||||
filename: "container-with-shared.js",
|
||||
exposes: ["./a", "./b", "./modules"],
|
||||
remotes: {
|
||||
"container-with-shared": "./container-with-shared.js"
|
||||
},
|
||||
shared: ["./shared"]
|
||||
})
|
||||
]
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
export * from "container-with-shared/b";
|
|
@ -0,0 +1 @@
|
|||
export * from "./shared";
|
|
@ -0,0 +1,35 @@
|
|||
it("should work normally (a)", () => {
|
||||
return import("./a").then(({ value }) => {
|
||||
expect(value).toBe("shared");
|
||||
});
|
||||
});
|
||||
|
||||
it("should work normally (b)", () => {
|
||||
return import("./b").then(({ value }) => {
|
||||
expect(value).toBe("shared");
|
||||
});
|
||||
});
|
||||
|
||||
it("should work normally (container-with-shared/a)", () => {
|
||||
return import("container-with-shared/a").then(({ value }) => {
|
||||
expect(value).toBe("shared");
|
||||
});
|
||||
});
|
||||
|
||||
it("should work normally (container-with-shared/b)", () => {
|
||||
return import("container-with-shared/b").then(({ value }) => {
|
||||
expect(value).toBe("shared");
|
||||
});
|
||||
});
|
||||
|
||||
it("should work normally (container-no-shared/a)", () => {
|
||||
return import("container-no-shared/a").then(({ value }) => {
|
||||
expect(value).toBe("shared");
|
||||
});
|
||||
});
|
||||
|
||||
it("should work normally (container-no-shared/b)", () => {
|
||||
return import("container-no-shared/b").then(({ value }) => {
|
||||
expect(value).toBe("shared");
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
export { default } from "container-with-shared/modules";
|
|
@ -0,0 +1 @@
|
|||
export default Object.keys(__webpack_modules__).sort();
|
|
@ -0,0 +1 @@
|
|||
export const value = "shared";
|
|
@ -0,0 +1,22 @@
|
|||
const { ModuleFederationPlugin } = require("../../../../").container;
|
||||
|
||||
/** @type {import("../../../../").Configuration} */
|
||||
module.exports = {
|
||||
optimization: {
|
||||
chunkIds: "named",
|
||||
moduleIds: "named"
|
||||
},
|
||||
plugins: [
|
||||
new ModuleFederationPlugin({
|
||||
name: "container-no-shared",
|
||||
library: { type: "commonjs-module" },
|
||||
filename: "container-no-shared.js",
|
||||
exposes: ["./a", "./b", "./modules", "./modules-from-remote"],
|
||||
remotes: {
|
||||
"container-with-shared":
|
||||
"../0-transitive-overriding/container-with-shared.js",
|
||||
"container-no-shared": "./container-no-shared.js"
|
||||
}
|
||||
})
|
||||
]
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
it("should allow transitive overrides (container-no-shared/a)", () => {
|
||||
return import("container-no-shared/a").then(({ value }) => {
|
||||
expect(value).toBe("new shared");
|
||||
});
|
||||
});
|
||||
|
||||
it("should not override non-overridables (container-no-shared/b)", () => {
|
||||
return import("container-no-shared/b").then(({ value }) => {
|
||||
expect(value).toBe("shared");
|
||||
});
|
||||
});
|
||||
|
||||
it("should have good module ids", async () => {
|
||||
const { default: m0 } = await import(
|
||||
"container-no-shared/modules-from-remote"
|
||||
);
|
||||
const { default: m1 } = await import("container-no-shared/modules");
|
||||
const m2 = Object.keys(__webpack_modules__).sort();
|
||||
expect(m0).toEqual([
|
||||
"./b.js",
|
||||
"./modules.js",
|
||||
"webpack/container/entry/container-with-shared",
|
||||
"webpack/container/overridable/shared=./shared"
|
||||
]);
|
||||
expect(m1).toEqual([
|
||||
"./a.js",
|
||||
"./b.js",
|
||||
"./modules-from-remote.js",
|
||||
"./modules.js",
|
||||
"webpack/container/entry/container-no-shared",
|
||||
"webpack/container/reference/container-with-shared",
|
||||
"webpack/container/remote-overrides/empty",
|
||||
"webpack/container/remote/container-with-shared/b",
|
||||
"webpack/container/remote/container-with-shared/modules"
|
||||
]);
|
||||
expect(m2).toEqual([
|
||||
"./index.js",
|
||||
"./shared.js",
|
||||
"webpack/container/reference/container-no-shared",
|
||||
"webpack/container/remote-overrides/7d3b8a",
|
||||
"webpack/container/remote/container-no-shared/a",
|
||||
"webpack/container/remote/container-no-shared/b",
|
||||
"webpack/container/remote/container-no-shared/modules",
|
||||
"webpack/container/remote/container-no-shared/modules-from-remote"
|
||||
]);
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
export const value = "new shared";
|
|
@ -0,0 +1,19 @@
|
|||
const { ModuleFederationPlugin } = require("../../../../").container;
|
||||
|
||||
/** @type {import("../../../../").Configuration} */
|
||||
module.exports = {
|
||||
optimization: {
|
||||
chunkIds: "named",
|
||||
moduleIds: "named"
|
||||
},
|
||||
plugins: [
|
||||
new ModuleFederationPlugin({
|
||||
remoteType: "commonjs-module",
|
||||
remotes: {
|
||||
"container-no-shared":
|
||||
"../1-transitive-overriding/container-no-shared.js"
|
||||
},
|
||||
shared: ["./shared"]
|
||||
})
|
||||
]
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
export let error;
|
||||
try {
|
||||
require("remote/module");
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export let error;
|
||||
try {
|
||||
await import("remote/module");
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
import "remote/module";
|
|
@ -0,0 +1,82 @@
|
|||
"use strict";
|
||||
|
||||
it("should allow to handle remote loading error with import()", async () => {
|
||||
await expect(import("./loading-error")).rejects.toEqual(
|
||||
expect.objectContaining({
|
||||
code: "ENOENT"
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("should allow to handle remote loading error with require", async () => {
|
||||
const { error } = await import("./loading-error-cjs");
|
||||
expect(error).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "ENOENT"
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("should allow to handle remote loading error with top-level-await import()", async () => {
|
||||
const { error } = await import("./loading-error-tl-await");
|
||||
expect(error).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "ENOENT"
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("should allow to handle invalid remote module error with import()", async () => {
|
||||
await expect(import("./invalid-module")).rejects.toEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
'Module "invalid" does not exist in container.\nwhile loading "invalid" from webpack/container/reference/remote'
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("should allow to handle invalid remote module error with require", async () => {
|
||||
const { error } = await import("./invalid-module-cjs");
|
||||
expect(error).toEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
'Module "invalid" does not exist in container.\nwhile loading "invalid" from webpack/container/reference/remote'
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("should allow to handle invalid remote module error with top-level-await import()", async () => {
|
||||
const { error } = await import("./invalid-module-tl-await");
|
||||
expect(error).toEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
'Module "invalid" does not exist in container.\nwhile loading "invalid" from webpack/container/reference/remote'
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("should allow to handle remote module evaluation error with import()", async () => {
|
||||
await expect(import("./evaluation-error")).rejects.toEqual(
|
||||
expect.objectContaining({
|
||||
message: "evaluation error"
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("should allow to handle remote module evaluation error with require", async () => {
|
||||
const { error } = await import("./evaluation-error-cjs");
|
||||
expect(error).toEqual(
|
||||
expect.objectContaining({
|
||||
message: "evaluation error"
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("should allow to handle remote module evaluation error with top-level-await import()", async () => {
|
||||
const { error } = await import("./evaluation-error-tl-await");
|
||||
expect(error).toEqual(
|
||||
expect.objectContaining({
|
||||
message: "evaluation error"
|
||||
})
|
||||
);
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
export let error;
|
||||
try {
|
||||
require("remote/invalid");
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export let error;
|
||||
try {
|
||||
await import("remote/invalid");
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
import "remote/invalid";
|
|
@ -0,0 +1,6 @@
|
|||
export let error;
|
||||
try {
|
||||
require("invalid/module");
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export let error;
|
||||
try {
|
||||
await import("invalid/module");
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
import "invalid/module";
|
|
@ -0,0 +1 @@
|
|||
throw new Error("evaluation error");
|
|
@ -0,0 +1,27 @@
|
|||
const { ModuleFederationPlugin } = require("../../../../").container;
|
||||
|
||||
/** @type {import("../../../../").Configuration} */
|
||||
module.exports = {
|
||||
optimization: {
|
||||
chunkIds: "named",
|
||||
moduleIds: "named"
|
||||
},
|
||||
output: {
|
||||
strictModuleExceptionHandling: true
|
||||
},
|
||||
plugins: [
|
||||
new ModuleFederationPlugin({
|
||||
name: "container",
|
||||
library: { type: "commonjs-module" },
|
||||
filename: "container.js",
|
||||
exposes: ["./module"],
|
||||
remotes: {
|
||||
remote: "./container.js",
|
||||
invalid: "./invalid.js"
|
||||
}
|
||||
})
|
||||
],
|
||||
experiments: {
|
||||
topLevelAwait: true
|
||||
}
|
||||
};
|
|
@ -1086,7 +1086,7 @@ declare class Compilation {
|
|||
): void;
|
||||
addEntry(
|
||||
context: string,
|
||||
entry: EntryDependency,
|
||||
entry: Dependency,
|
||||
optionsOrName:
|
||||
| string
|
||||
| ({ name: string } & Pick<
|
||||
|
@ -1936,7 +1936,7 @@ declare interface EntryData {
|
|||
/**
|
||||
* dependencies of the entrypoint
|
||||
*/
|
||||
dependencies: EntryDependency[];
|
||||
dependencies: Dependency[];
|
||||
|
||||
/**
|
||||
* options of the entrypoint
|
||||
|
|
Loading…
Reference in New Issue