Merge pull request #10835 from webpack/mfe/bugs-and-dx

Module federation bugfixes and DX
This commit is contained in:
Tobias Koppers 2020-05-05 22:21:39 +02:00 committed by GitHub
commit e87ebdda53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 580 additions and 152 deletions

View File

@ -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) {

View File

@ -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";
}

View File

@ -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);
}
}

View File

@ -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)
});
}
};

View File

@ -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,

View File

@ -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)
);
}

View File

@ -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

View File

@ -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();
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);"]),
"}"
])});`
]),
"}"

View File

@ -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;

View File

@ -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": () =>

View File

@ -0,0 +1 @@
export * from "./b";

View File

@ -0,0 +1 @@
export * from "./shared";

View File

@ -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");
});
});

View File

@ -0,0 +1 @@
export default Object.keys(__webpack_modules__).sort();

View File

@ -0,0 +1 @@
export const value = "shared";

View File

@ -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"]
})
]
};

View File

@ -0,0 +1 @@
export * from "container-with-shared/b";

View File

@ -0,0 +1 @@
export * from "./shared";

View File

@ -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");
});
});

View File

@ -0,0 +1 @@
export { default } from "container-with-shared/modules";

View File

@ -0,0 +1 @@
export default Object.keys(__webpack_modules__).sort();

View File

@ -0,0 +1 @@
export const value = "shared";

View File

@ -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"
}
})
]
};

View File

@ -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"
]);
});

View File

@ -0,0 +1 @@
export const value = "new shared";

View File

@ -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"]
})
]
};

View File

@ -0,0 +1,6 @@
export let error;
try {
require("remote/module");
} catch (err) {
error = err;
}

View File

@ -0,0 +1,6 @@
export let error;
try {
await import("remote/module");
} catch (err) {
error = err;
}

View File

@ -0,0 +1 @@
import "remote/module";

View File

@ -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"
})
);
});

View File

@ -0,0 +1,6 @@
export let error;
try {
require("remote/invalid");
} catch (err) {
error = err;
}

View File

@ -0,0 +1,6 @@
export let error;
try {
await import("remote/invalid");
} catch (err) {
error = err;
}

View File

@ -0,0 +1 @@
import "remote/invalid";

View File

@ -0,0 +1,6 @@
export let error;
try {
require("invalid/module");
} catch (err) {
error = err;
}

View File

@ -0,0 +1,6 @@
export let error;
try {
await import("invalid/module");
} catch (err) {
error = err;
}

View File

@ -0,0 +1 @@
import "invalid/module";

View File

@ -0,0 +1 @@
throw new Error("evaluation error");

View File

@ -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
}
};

4
types.d.ts vendored
View File

@ -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