Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jacob-ebey/1a6edce80519c25b4725a05cc5618ee4 to your computer and use it in GitHub Desktop.
Save jacob-ebey/1a6edce80519c25b4725a05cc5618ee4 to your computer and use it in GitHub Desktop.
webpack-access-federated-containers-plugin.js
/*
Makes your remote containers low level API accessible via:
import accessFederatedContainer from "access-federated-containers";
accessFederatedContainer("app2")
*/
/** @typedef {import("webpack").Compiler} Compiler */
/** @typedef {import("webpack").Compilation} Compilation */
const VirtualModulesPlugin = require("webpack-virtual-modules");
const PLUGIN_NAME = "AccessFederatedContainersPlugin";
/**
*
* @param {import("webpack").Module[]} modules
*/
function createAccessFederatedContainersModule(modules) {
const containerAccessors = modules.map((mod) => {
const name = mod.userRequest.replace("webpack/container/reference/", "");
const chunks = mod
.getChunks()
.filter((chunk) => !!chunk.id)
.map((chunk) => `__webpack_require__.e(${JSON.stringify(chunk.id)})`);
return `${JSON.stringify(name)}: () => Promise.all([${chunks.join(
", "
)}]).then(() => __webpack_require__(${JSON.stringify(mod.id)}))`;
});
return `
const containerAccessors = {
${containerAccessors.join(",\n ")}
};
async function accessFederatedContainer(containerName) {
const accessor = containerAccessors[containerName];
if (!accessor) {
throw new Error(\`Can not access container "\${containerName}".\`);
}
return await accessor();
}
export default accessFederatedContainer;`;
}
const defaultOptions = {
exposedAs: "access-federated-containers",
};
class AccessFederatedContainersPlugin {
constructor(options = defaultOptions) {
this._rawRequest = options.exposedAs || defaultOptions.exposedAs;
this._exposedAs = `node_modules/${this._rawRequest}`;
}
/**
* @param {Compiler} compiler
*/
apply(compiler) {
const virtualModules = new VirtualModulesPlugin({
[this._exposedAs]: "",
});
virtualModules.apply(compiler);
let updatedAccessContainerModule = false;
const addedModules = new Map();
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
compilation.hooks.needAdditionalPass.tap(PLUGIN_NAME, () => {
console.log(
"updatedAccessContainerModule",
updatedAccessContainerModule ? "true" : "false"
);
let result = updatedAccessContainerModule;
updatedAccessContainerModule = false;
return result;
});
compilation.hooks.afterOptimizeModuleIds.tap(PLUGIN_NAME, (modules) => {
const filteredModules = [...modules].filter(
(mod) =>
(mod.constructor.name === "ExternalModule" &&
mod.userRequest &&
mod.userRequest.startsWith("webpack/container/reference/"))
);
if (
filteredModules.length > 0 &&
filteredModules.some((mod) => !addedModules.has(mod.id))
) {
updatedAccessContainerModule = true;
filteredModules.forEach((mod) => addedModules.set(mod.id, mod));
virtualModules.writeModule(
this._exposedAs,
createAccessFederatedContainersModule([...addedModules.values()])
);
}
});
});
}
}
module.exports = AccessFederatedContainersPlugin;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment