every-plugin
Version:
103 lines (101 loc) • 4.44 kB
JavaScript
import { ModuleFederationError } from "../errors.mjs";
import { MF_CORE_SHARED_DEPS } from "../mf-config.mjs";
import { getNormalizedRemoteName } from "./normalize.mjs";
import { Effect } from "effect";
import { createInstance, getInstance } from "@module-federation/enhanced/runtime";
import { setGlobalFederationInstance } from "@module-federation/runtime-core";
//#region src/runtime/services/module-federation.service.ts
const coreModuleLoaders = {
"every-plugin": () => import("../../index.mjs"),
effect: () => import("effect"),
zod: () => import("zod"),
"@orpc/contract": () => import("@orpc/contract"),
"@orpc/server": () => import("@orpc/server")
};
function buildSharedConfig() {
return Object.fromEntries(Object.entries(MF_CORE_SHARED_DEPS).map(([name, config]) => {
const load = coreModuleLoaders[name];
if (!load) throw new Error(`Missing core shared module loader for ${name}`);
return [name, {
version: config.version,
get: () => load().then((mod) => () => mod),
shareConfig: config.shareConfig
}];
}));
}
const createModuleFederationInstance = Effect.cached(Effect.sync(() => {
try {
const shared = buildSharedConfig();
let instance = getInstance();
if (!instance) {
instance = createInstance({
name: "host",
remotes: [],
shared
});
setGlobalFederationInstance(instance);
} else instance.registerShared(shared);
return instance;
} catch (error) {
throw new Error(`Failed to initialize Module Federation: ${error}`);
}
}));
var ModuleFederationService = class extends Effect.Service()("ModuleFederationService", { effect: Effect.gen(function* () {
const mf = yield* Effect.flatten(createModuleFederationInstance);
return {
registerRemote: (pluginId, url) => Effect.gen(function* () {
console.log(`[MF] Registering ${pluginId}`);
const remoteName = getNormalizedRemoteName(pluginId);
const type = url.endsWith("/mf-manifest.json") ? "manifest" : url.endsWith("/remoteEntry.js") ? "script" : void 0;
yield* Effect.try({
try: () => mf.registerRemotes([{
name: remoteName,
entry: url,
...type ? { type } : {}
}]),
catch: (error) => new ModuleFederationError({
pluginId,
remoteUrl: url,
cause: error instanceof Error ? error : new Error(String(error))
})
});
console.log(`[MF] ✅ Registered ${pluginId}`);
}),
loadRemoteConstructor: (pluginId, url) => Effect.gen(function* () {
const remoteName = getNormalizedRemoteName(pluginId);
console.log(`[MF] Loading remote ${remoteName}`);
const modulePath = `${remoteName}/plugin`;
return yield* Effect.tryPromise({
try: async () => {
const container = await mf.loadRemote(modulePath);
if (!container) throw new Error(`No container returned for ${modulePath}`);
let Constructor;
if (typeof container === "function") Constructor = container;
else if (container.default) Constructor = container.default;
else {
Constructor = Object.values(container).find((exp) => typeof exp === "function" && exp.binding !== void 0);
if (!Constructor) Constructor = Object.values(container).find((exp) => typeof exp === "function" && exp.prototype?.constructor === exp);
}
if (!Constructor || typeof Constructor !== "function") {
const containerInfo = typeof container === "object" ? `Available exports: ${Object.keys(container).join(", ")}` : `Container type: ${typeof container}`;
throw new Error(`No valid plugin constructor found for '${pluginId}'.\nSupported patterns:\n - export const YourPlugin = createPlugin({...})\n - export default createPlugin({...})\n${containerInfo}`);
}
if (!Constructor.binding) {
const containerInfo = typeof container === "object" ? `Found exports: ${Object.keys(container).join(", ")}` : `Container type: ${typeof container}`;
throw new Error(`Invalid plugin constructor for '${pluginId}'. The exported value must be created with createPlugin(). Found a function but it's missing the required 'binding' property.\n${containerInfo}`);
}
console.log(`[MF] ✅ Loaded constructor for ${pluginId}`);
return Constructor;
},
catch: (error) => new ModuleFederationError({
pluginId,
remoteUrl: url,
cause: error instanceof Error ? error : new Error(String(error))
})
});
})
};
}) }) {};
//#endregion
export { ModuleFederationService };
//# sourceMappingURL=module-federation.service.mjs.map