Skip to content

Commit

Permalink
Merge pull request #14827 from webpack/bugfix/mf-module
Browse files Browse the repository at this point in the history
fix and test module federation with ESM
  • Loading branch information
sokra authored Nov 25, 2021
2 parents ac9a2c8 + 041287f commit 093eadf
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 116 deletions.
3 changes: 3 additions & 0 deletions lib/container/ContainerEntryModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
const Module = require("../Module");
const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");
const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
const makeSerializable = require("../util/makeSerializable");
const ContainerExposedDependency = require("./ContainerExposedDependency");

Expand Down Expand Up @@ -104,6 +105,7 @@ class ContainerEntryModule extends Module {
strict: true,
topLevelDeclarations: new Set(["moduleMap", "get", "init"])
};
this.buildMeta.exportsType = "namespace";

this.clearDependenciesAndBlocks();

Expand All @@ -127,6 +129,7 @@ class ContainerEntryModule extends Module {
}
this.addBlock(block);
}
this.addDependency(new StaticExportsDependency(["get", "init"], false));

callback();
}
Expand Down
147 changes: 80 additions & 67 deletions test/ConfigTestCases.template.js
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,8 @@ const describeCases = config => {
};

const requireCache = Object.create(null);
const esmCache = new Map();
const esmIdentifier = `${category.name}-${testName}-${i}`;
// eslint-disable-next-line no-loop-func
const _require = (
currentDirectory,
Expand All @@ -335,7 +337,7 @@ const describeCases = config => {
) => {
if (testConfig === undefined) {
throw new Error(
`_require(${module}) called after all tests have completed`
`_require(${module}) called after all tests from ${category.name} ${testName} have completed`
);
}
if (Array.isArray(module) || /^\.\.?\//.test(module)) {
Expand Down Expand Up @@ -373,16 +375,15 @@ const describeCases = config => {
);
}
}
if (p in requireCache) {
return requireCache[p].exports;
}
const m = {
exports: {}
};
requireCache[p] = m;
const isModule =
p.endsWith(".mjs") &&
options.experiments &&
options.experiments.outputModule;

let runInNewContext = false;

const moduleScope = {
console: console,
it: _it,
beforeEach: _beforeEach,
afterEach: _afterEach,
Expand All @@ -396,36 +397,7 @@ const describeCases = config => {
return m;
}
};
const isModule =
p.endsWith(".mjs") &&
options.experiments &&
options.experiments.outputModule;
if (!isModule) {
Object.assign(moduleScope, {
require: _require.bind(
null,
path.dirname(p),
options
),
importScripts: url => {
expect(url).toMatch(
/^https:\/\/test\.cases\/path\//
);
_require(
outputDirectory,
options,
`.${url.slice(
"https://test.cases/path".length
)}`
);
},
module: m,
exports: m.exports,
__dirname: path.dirname(p),
__filename: p,
_globalAssign: { expect }
});
}

if (
options.target === "web" ||
options.target === "webworker"
Expand All @@ -439,48 +411,55 @@ const describeCases = config => {
});
runInNewContext = true;
}
if (testConfig.moduleScope) {
testConfig.moduleScope(moduleScope);
}
if (isModule) {
if (testConfig.moduleScope) {
testConfig.moduleScope(moduleScope);
}
if (!vm.SourceTextModule)
throw new Error(
"Running this test requires '--experimental-vm-modules'.\nRun with 'node --experimental-vm-modules node_modules/jest-cli/bin/jest'."
);
const esm = new vm.SourceTextModule(content, {
identifier: p,
url: pathToFileURL(p).href,
context:
(parentModule && parentModule.context) ||
vm.createContext(moduleScope, {
name: `context for ${p}`
}),
initializeImportMeta: (meta, module) => {
meta.url = pathToFileURL(p).href;
},
importModuleDynamically: async (
specifier,
module
) => {
const result = await _require(
path.dirname(p),
options,
let esm = esmCache.get(p);
if (!esm) {
esm = new vm.SourceTextModule(content, {
identifier: esmIdentifier + "-" + p,
url: pathToFileURL(p).href + "?" + esmIdentifier,
context:
(parentModule && parentModule.context) ||
vm.createContext(moduleScope, {
name: `context for ${p}`
}),
initializeImportMeta: (meta, module) => {
meta.url = pathToFileURL(p).href;
},
importModuleDynamically: async (
specifier,
"evaluated",
module
);
return await asModule(result, module.context);
}
});
) => {
const result = await _require(
path.dirname(p),
options,
specifier,
"evaluated",
module
);
return await asModule(result, module.context);
}
});
esmCache.set(p, esm);
}
if (esmMode === "unlinked") return esm;
return (async () => {
await esm.link(
async (specifier, referencingModule) => {
return await asModule(
await _require(
path.dirname(
referencingModule.identifier ||
fileURLToPath(referencingModule.url)
referencingModule.identifier
? referencingModule.identifier.slice(
esmIdentifier.length + 1
)
: fileURLToPath(referencingModule.url)
),
options,
specifier,
Expand All @@ -502,6 +481,40 @@ const describeCases = config => {
: ns;
})();
} else {
if (p in requireCache) {
return requireCache[p].exports;
}
const m = {
exports: {}
};
requireCache[p] = m;
Object.assign(moduleScope, {
require: _require.bind(
null,
path.dirname(p),
options
),
importScripts: url => {
expect(url).toMatch(
/^https:\/\/test\.cases\/path\//
);
_require(
outputDirectory,
options,
`.${url.slice(
"https://test.cases/path".length
)}`
);
},
module: m,
exports: m.exports,
__dirname: path.dirname(p),
__filename: p,
_globalAssign: { expect }
});
if (testConfig.moduleScope) {
testConfig.moduleScope(moduleScope);
}
if (!runInNewContext)
content = `Object.assign(global, _globalAssign); ${content}`;
const args = Object.keys(moduleScope);
Expand All @@ -517,8 +530,8 @@ const describeCases = config => {
: vm.runInThisContext(code, p);
fn.call(m.exports, ...argValues);
document.currentScript = oldCurrentScript;
return m.exports;
}
return m.exports;
} else if (
testConfig.modules &&
module in testConfig.modules
Expand Down
5 changes: 5 additions & 0 deletions test/configCases/container/0-container-full/test.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
findBundle: function (i, options) {
return i === 0 ? "./main.js" : "./module/main.mjs";
}
};
83 changes: 58 additions & 25 deletions test/configCases/container/0-container-full/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,61 @@
const { ModuleFederationPlugin } = require("../../../../").container;

/** @type {import("../../../../").Configuration} */
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "container",
library: { type: "commonjs-module" },
filename: "container.js",
exposes: {
"./ComponentA": {
import: "./ComponentA"
}
},
remotes: {
containerA: {
external: "./container.js"
}
},
shared: {
react: {
version: false,
requiredVersion: false
}
}
})
]
/** @type {ConstructorParameters<typeof ModuleFederationPlugin>[0]} */
const common = {
name: "container",
exposes: {
"./ComponentA": {
import: "./ComponentA"
}
},
shared: {
react: {
version: false,
requiredVersion: false
}
}
};

/** @type {import("../../../../").Configuration[]} */
module.exports = [
{
output: {
filename: "[name].js",
uniqueName: "0-container-full"
},
plugins: [
new ModuleFederationPlugin({
library: { type: "commonjs-module" },
filename: "container.js",
remotes: {
containerA: {
external: "./container.js"
}
},
...common
})
]
},
{
experiments: {
outputModule: true
},
output: {
filename: "module/[name].mjs",
uniqueName: "0-container-full-mjs"
},
plugins: [
new ModuleFederationPlugin({
library: { type: "module" },
filename: "module/container.mjs",
remotes: {
containerA: {
external: "./container.mjs"
}
},
...common
})
],
target: "node14"
}
];
4 changes: 4 additions & 0 deletions test/configCases/container/1-container-full/package.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"private": true,
"engines": {
"node": ">=10.13.0"
},
"dependencies": {
"react": "*"
}
Expand Down
5 changes: 5 additions & 0 deletions test/configCases/container/1-container-full/test.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
findBundle: function (i, options) {
return i === 0 ? "./main.js" : "./module/main.mjs";
}
};
Loading

0 comments on commit 093eadf

Please sign in to comment.