🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@module-federation/dts-plugin

Package Overview
Dependencies
Maintainers
4
Versions
1026
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@module-federation/dts-plugin - npm Package Compare versions

Comparing version
2.5.1
to
2.6.0
+241
dist/consumeTypes-DuDkcp8N.js
const require_Action = require('./Action-CzhPMw2i.js');
const require_expose_rpc = require('./expose-rpc-BNzQqY2X.js');
let url = require("url");
let path = require("path");
path = require_Action.__toESM(path);
let crypto = require("crypto");
let child_process = require("child_process");
child_process = require_Action.__toESM(child_process);
let process$1 = require("process");
process$1 = require_Action.__toESM(process$1);
//#region src/core/rpc/rpc-error.ts
var RpcExitError = class extends Error {
constructor(message, code, signal) {
super(message);
this.code = code;
this.signal = signal;
this.name = "RpcExitError";
}
};
//#endregion
//#region src/core/rpc/wrap-rpc.ts
function createControlledPromise() {
let resolve = () => void 0;
let reject = () => void 0;
return {
promise: new Promise((aResolve, aReject) => {
resolve = aResolve;
reject = aReject;
}),
resolve,
reject
};
}
function wrapRpc(childProcess, options) {
return (async (...args) => {
if (!childProcess.send) throw new Error(`Process ${childProcess.pid} doesn't have IPC channels`);
else if (!childProcess.connected) throw new Error(`Process ${childProcess.pid} doesn't have open IPC channels`);
const { id, once } = options;
const { promise: resultPromise, resolve: resolveResult, reject: rejectResult } = createControlledPromise();
const { promise: sendPromise, resolve: resolveSend, reject: rejectSend } = createControlledPromise();
const handleMessage = (message) => {
if (message?.id === id) {
if (message.type === require_expose_rpc.RpcGMCallTypes.RESOLVE) resolveResult(message.value);
else if (message.type === require_expose_rpc.RpcGMCallTypes.REJECT) rejectResult(message.error);
}
if (once && childProcess?.kill) childProcess.kill("SIGTERM");
};
const handleClose = (code, signal) => {
rejectResult(new RpcExitError(code ? `Process ${childProcess.pid} exited with code ${code}${signal ? ` [${signal}]` : ""}` : `Process ${childProcess.pid} exited${signal ? ` [${signal}]` : ""}`, code, signal));
removeHandlers();
};
const removeHandlers = () => {
childProcess.off("message", handleMessage);
childProcess.off("close", handleClose);
};
if (once) childProcess.once("message", handleMessage);
else childProcess.on("message", handleMessage);
childProcess.on("close", handleClose);
childProcess.send({
type: require_expose_rpc.RpcGMCallTypes.CALL,
id,
args
}, (error) => {
if (error) {
rejectSend(error);
removeHandlers();
} else resolveSend(void 0);
});
return sendPromise.then(() => resultPromise);
});
}
//#endregion
//#region src/core/rpc/rpc-worker.ts
const FEDERATION_WORKER_DATA_ENV_KEY = "VMOK_WORKER_DATA_ENV";
function createRpcWorker(modulePath, data, memoryLimit, once) {
const options = {
env: {
...process$1.env,
[FEDERATION_WORKER_DATA_ENV_KEY]: JSON.stringify(data || {})
},
stdio: [
"inherit",
"inherit",
"inherit",
"ipc"
],
serialization: "advanced"
};
if (memoryLimit) options.execArgv = [`--max-old-space-size=${memoryLimit}`];
let childProcess, remoteMethod;
const id = (0, crypto.randomUUID)();
return {
connect(...args) {
if (childProcess && !childProcess.connected) {
childProcess.send({
type: require_expose_rpc.RpcGMCallTypes.EXIT,
id
});
childProcess = void 0;
remoteMethod = void 0;
}
if (!childProcess?.connected) {
childProcess = child_process.fork(modulePath, options);
remoteMethod = wrapRpc(childProcess, {
id,
once
});
}
if (!remoteMethod) return Promise.reject(/* @__PURE__ */ new Error("Worker is not connected - cannot perform RPC."));
return remoteMethod(...args);
},
terminate() {
try {
if (childProcess.connected) childProcess.send({
type: require_expose_rpc.RpcGMCallTypes.EXIT,
id
}, (err) => {
if (err) console.error("Error sending message:", err);
});
} catch (error) {
if (error.code === "EPIPE") console.error("Pipe closed before message could be sent:", error);
else console.error("Unexpected error:", error);
}
childProcess = void 0;
remoteMethod = void 0;
},
get connected() {
return Boolean(childProcess?.connected);
},
get process() {
return childProcess;
},
get id() {
return id;
}
};
}
function getRpcWorkerData() {
return JSON.parse(process$1.env[FEDERATION_WORKER_DATA_ENV_KEY] || "{}");
}
//#endregion
//#region src/core/rpc/index.ts
var rpc_exports = /* @__PURE__ */ require_Action.__exportAll({
RpcExitError: () => RpcExitError,
RpcGMCallTypes: () => require_expose_rpc.RpcGMCallTypes,
createRpcWorker: () => createRpcWorker,
exposeRpc: () => require_expose_rpc.exposeRpc,
getRpcWorkerData: () => getRpcWorkerData,
wrapRpc: () => wrapRpc
});
//#endregion
//#region src/core/lib/DtsWorker.ts
const __filename$1 = (0, url.fileURLToPath)(require("url").pathToFileURL(__filename).href);
const __dirname$1 = path.default.dirname(__filename$1);
const __extname = path.default.extname(__filename$1);
var DtsWorker = class {
constructor(options) {
this._options = require_expose_rpc.cloneDeepOptions(options);
this.removeUnSerializationOptions();
this.rpcWorker = createRpcWorker(path.default.resolve(__dirname$1, `./fork-generate-dts${__extname}`), {}, void 0, true);
this._res = this.rpcWorker.connect(this._options);
}
removeUnSerializationOptions() {
if (this._options.remote?.moduleFederationConfig?.manifest) delete this._options.remote?.moduleFederationConfig?.manifest;
if (this._options.host?.moduleFederationConfig?.manifest) delete this._options.host?.moduleFederationConfig?.manifest;
}
get controlledPromise() {
const ensureChildProcessExit = () => {
try {
const pid = this.rpcWorker.process?.pid;
const rootPid = process.pid;
if (pid && rootPid !== pid) process.kill(pid, 0);
} catch (error) {
if (require_expose_rpc.isDebugMode()) console.error(error);
}
};
return Promise.resolve(this._res).then(() => {
this.exit();
ensureChildProcessExit();
}).catch((err) => {
if (require_expose_rpc.isDebugMode()) console.error(err);
ensureChildProcessExit();
});
}
exit() {
try {
this.rpcWorker?.terminate();
} catch (err) {
if (require_expose_rpc.isDebugMode()) console.error(err);
}
}
};
//#endregion
//#region src/core/lib/generateTypesInChildProcess.ts
async function generateTypesInChildProcess(options) {
return new DtsWorker(options).controlledPromise;
}
//#endregion
//#region src/core/lib/consumeTypes.ts
async function consumeTypes(options) {
await new (require_expose_rpc.getDTSManagerConstructor(options.host?.implementation))(options).consumeTypes();
}
//#endregion
Object.defineProperty(exports, 'DtsWorker', {
enumerable: true,
get: function () {
return DtsWorker;
}
});
Object.defineProperty(exports, 'consumeTypes', {
enumerable: true,
get: function () {
return consumeTypes;
}
});
Object.defineProperty(exports, 'createRpcWorker', {
enumerable: true,
get: function () {
return createRpcWorker;
}
});
Object.defineProperty(exports, 'generateTypesInChildProcess', {
enumerable: true,
get: function () {
return generateTypesInChildProcess;
}
});
Object.defineProperty(exports, 'rpc_exports', {
enumerable: true,
get: function () {
return rpc_exports;
}
});
import { a as cloneDeepOptions, n as RpcGMCallTypes, o as getDTSManagerConstructor, s as isDebugMode, t as exposeRpc, x as __exportAll } from "./expose-rpc-BiwGpqZ3.mjs";
import { fileURLToPath } from "url";
import path from "path";
import { randomUUID } from "crypto";
import * as child_process from "child_process";
import * as process$2 from "process";
//#region src/core/rpc/rpc-error.ts
var RpcExitError = class extends Error {
constructor(message, code, signal) {
super(message);
this.code = code;
this.signal = signal;
this.name = "RpcExitError";
}
};
//#endregion
//#region src/core/rpc/wrap-rpc.ts
function createControlledPromise() {
let resolve = () => void 0;
let reject = () => void 0;
return {
promise: new Promise((aResolve, aReject) => {
resolve = aResolve;
reject = aReject;
}),
resolve,
reject
};
}
function wrapRpc(childProcess, options) {
return (async (...args) => {
if (!childProcess.send) throw new Error(`Process ${childProcess.pid} doesn't have IPC channels`);
else if (!childProcess.connected) throw new Error(`Process ${childProcess.pid} doesn't have open IPC channels`);
const { id, once } = options;
const { promise: resultPromise, resolve: resolveResult, reject: rejectResult } = createControlledPromise();
const { promise: sendPromise, resolve: resolveSend, reject: rejectSend } = createControlledPromise();
const handleMessage = (message) => {
if (message?.id === id) {
if (message.type === RpcGMCallTypes.RESOLVE) resolveResult(message.value);
else if (message.type === RpcGMCallTypes.REJECT) rejectResult(message.error);
}
if (once && childProcess?.kill) childProcess.kill("SIGTERM");
};
const handleClose = (code, signal) => {
rejectResult(new RpcExitError(code ? `Process ${childProcess.pid} exited with code ${code}${signal ? ` [${signal}]` : ""}` : `Process ${childProcess.pid} exited${signal ? ` [${signal}]` : ""}`, code, signal));
removeHandlers();
};
const removeHandlers = () => {
childProcess.off("message", handleMessage);
childProcess.off("close", handleClose);
};
if (once) childProcess.once("message", handleMessage);
else childProcess.on("message", handleMessage);
childProcess.on("close", handleClose);
childProcess.send({
type: RpcGMCallTypes.CALL,
id,
args
}, (error) => {
if (error) {
rejectSend(error);
removeHandlers();
} else resolveSend(void 0);
});
return sendPromise.then(() => resultPromise);
});
}
//#endregion
//#region src/core/rpc/rpc-worker.ts
const FEDERATION_WORKER_DATA_ENV_KEY = "VMOK_WORKER_DATA_ENV";
function createRpcWorker(modulePath, data, memoryLimit, once) {
const options = {
env: {
...process$2.env,
[FEDERATION_WORKER_DATA_ENV_KEY]: JSON.stringify(data || {})
},
stdio: [
"inherit",
"inherit",
"inherit",
"ipc"
],
serialization: "advanced"
};
if (memoryLimit) options.execArgv = [`--max-old-space-size=${memoryLimit}`];
let childProcess, remoteMethod;
const id = randomUUID();
return {
connect(...args) {
if (childProcess && !childProcess.connected) {
childProcess.send({
type: RpcGMCallTypes.EXIT,
id
});
childProcess = void 0;
remoteMethod = void 0;
}
if (!childProcess?.connected) {
childProcess = child_process.fork(modulePath, options);
remoteMethod = wrapRpc(childProcess, {
id,
once
});
}
if (!remoteMethod) return Promise.reject(/* @__PURE__ */ new Error("Worker is not connected - cannot perform RPC."));
return remoteMethod(...args);
},
terminate() {
try {
if (childProcess.connected) childProcess.send({
type: RpcGMCallTypes.EXIT,
id
}, (err) => {
if (err) console.error("Error sending message:", err);
});
} catch (error) {
if (error.code === "EPIPE") console.error("Pipe closed before message could be sent:", error);
else console.error("Unexpected error:", error);
}
childProcess = void 0;
remoteMethod = void 0;
},
get connected() {
return Boolean(childProcess?.connected);
},
get process() {
return childProcess;
},
get id() {
return id;
}
};
}
function getRpcWorkerData() {
return JSON.parse(process$2.env[FEDERATION_WORKER_DATA_ENV_KEY] || "{}");
}
//#endregion
//#region src/core/rpc/index.ts
var rpc_exports = /* @__PURE__ */ __exportAll({
RpcExitError: () => RpcExitError,
RpcGMCallTypes: () => RpcGMCallTypes,
createRpcWorker: () => createRpcWorker,
exposeRpc: () => exposeRpc,
getRpcWorkerData: () => getRpcWorkerData,
wrapRpc: () => wrapRpc
});
//#endregion
//#region src/core/lib/DtsWorker.ts
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const __extname = path.extname(__filename);
var DtsWorker = class {
constructor(options) {
this._options = cloneDeepOptions(options);
this.removeUnSerializationOptions();
this.rpcWorker = createRpcWorker(path.resolve(__dirname, `./fork-generate-dts${__extname}`), {}, void 0, true);
this._res = this.rpcWorker.connect(this._options);
}
removeUnSerializationOptions() {
if (this._options.remote?.moduleFederationConfig?.manifest) delete this._options.remote?.moduleFederationConfig?.manifest;
if (this._options.host?.moduleFederationConfig?.manifest) delete this._options.host?.moduleFederationConfig?.manifest;
}
get controlledPromise() {
const ensureChildProcessExit = () => {
try {
const pid = this.rpcWorker.process?.pid;
const rootPid = process.pid;
if (pid && rootPid !== pid) process.kill(pid, 0);
} catch (error) {
if (isDebugMode()) console.error(error);
}
};
return Promise.resolve(this._res).then(() => {
this.exit();
ensureChildProcessExit();
}).catch((err) => {
if (isDebugMode()) console.error(err);
ensureChildProcessExit();
});
}
exit() {
try {
this.rpcWorker?.terminate();
} catch (err) {
if (isDebugMode()) console.error(err);
}
}
};
//#endregion
//#region src/core/lib/generateTypesInChildProcess.ts
async function generateTypesInChildProcess(options) {
return new DtsWorker(options).controlledPromise;
}
//#endregion
//#region src/core/lib/consumeTypes.ts
async function consumeTypes(options) {
await new (getDTSManagerConstructor(options.host?.implementation))(options).consumeTypes();
}
//#endregion
export { createRpcWorker as a, rpc_exports as i, generateTypesInChildProcess as n, DtsWorker as r, consumeTypes as t };
import { a as MF_SERVER_IDENTIFIER, n as ActionKind, o as UpdateMode, r as DEFAULT_TAR_NAME, t as Action } from "./Action-DNNg2YDh.mjs";
import { a as getIdentifier, c as logger$1, i as getFreePort, l as LogKind, n as UpdateKind, o as getIPV4, r as fib, s as fileLog, t as Broker, u as APIKind } from "./Broker-Cmbh_XVO.mjs";
import { createRequire } from "node:module";
import fs, { existsSync, mkdirSync, writeFileSync } from "fs";
import { fileURLToPath } from "url";
import path, { dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from "path";
import { cp, mkdir, readFile, readdir, rm, stat, writeFile } from "fs/promises";
import { utils } from "@module-federation/managers";
import typescript from "typescript";
import { ENCODE_NAME_PREFIX, MANIFEST_EXT, TEMP_DIR, decodeName, getProcessEnv, inferAutoPublicPath, parseEntry } from "@module-federation/sdk";
import ansiColors from "ansi-colors";
import { Agent } from "undici";
import { ThirdPartyExtractor } from "@module-federation/third-party-dts-extractor";
import AdmZip from "adm-zip";
import crypto from "crypto";
import { TYPE_001, typeDescMap } from "@module-federation/error-codes";
import { logAndReport } from "@module-federation/error-codes/node";
import { execFile, fork } from "child_process";
import util from "util";
import WebSocket from "isomorphic-ws";
import http from "http";
import process$1 from "process";
//#region \0rolldown/runtime.js
var __defProp = Object.defineProperty;
var __exportAll = (all, no_symbols) => {
let target = {};
for (var name in all) {
__defProp(target, name, {
get: all[name],
enumerable: true
});
}
if (!no_symbols) {
__defProp(target, Symbol.toStringTag, { value: "Module" });
}
return target;
};
var __require = /* @__PURE__ */ createRequire(import.meta.url);
//#endregion
//#region src/server/message/Action/AddPublisher.ts
var AddPublisherAction = class extends Action {
constructor(payload) {
super({ payload }, ActionKind.ADD_PUBLISHER);
}
};
//#endregion
//#region src/server/message/Action/AddSubscriber.ts
var AddSubscriberAction = class extends Action {
constructor(payload) {
super({ payload }, ActionKind.ADD_SUBSCRIBER);
}
};
//#endregion
//#region src/server/message/Action/ExitSubscriber.ts
var ExitSubscriberAction = class extends Action {
constructor(payload) {
super({ payload }, ActionKind.EXIT_SUBSCRIBER);
}
};
//#endregion
//#region src/server/message/Action/ExitPublisher.ts
var ExitPublisherAction = class extends Action {
constructor(payload) {
super({ payload }, ActionKind.EXIT_PUBLISHER);
}
};
//#endregion
//#region src/server/message/Action/NotifyWebClient.ts
var NotifyWebClientAction = class extends Action {
constructor(payload) {
super({ payload }, ActionKind.NOTIFY_WEB_CLIENT);
}
};
//#endregion
//#region src/server/message/Action/UpdatePublisher.ts
var UpdatePublisherAction = class extends Action {
constructor(payload) {
super({ payload }, ActionKind.UPDATE_PUBLISHER);
}
};
//#endregion
//#region src/server/broker/createBroker.ts
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
function createBroker() {
const sub = fork(path.resolve(__dirname, "./start-broker.js"), [], {
detached: true,
stdio: "ignore",
env: process.env
});
sub.send("start");
sub.unref();
return sub;
}
//#endregion
//#region src/server/DevServer.ts
var ModuleFederationDevServer = class {
constructor(ctx) {
this._publishWebSocket = null;
this._subscriberWebsocketMap = {};
this._reconnect = true;
this._reconnectTimes = 0;
this._isConnected = false;
this._isReconnecting = false;
this._updateCallback = () => Promise.resolve(void 0);
const { name, remotes, remoteTypeTarPath, updateCallback } = ctx;
this._ip = getIPV4();
this._name = name;
this._remotes = remotes;
this._remoteTypeTarPath = remoteTypeTarPath;
this._updateCallback = updateCallback;
this._stopWhenSIGTERMOrSIGINT();
this._handleUnexpectedExit();
this._connectPublishToServer();
}
_connectPublishToServer() {
if (!this._reconnect) return;
fileLog(`Publisher:${this._name} Trying to connect to ws://${this._ip}:${Broker.DEFAULT_WEB_SOCKET_PORT}...`, MF_SERVER_IDENTIFIER, "info");
this._publishWebSocket = new WebSocket(`ws://${this._ip}:${Broker.DEFAULT_WEB_SOCKET_PORT}?WEB_SOCKET_CONNECT_MAGIC_ID=${Broker.WEB_SOCKET_CONNECT_MAGIC_ID}`);
this._publishWebSocket.on("open", () => {
fileLog(`Current pid: ${process.pid}, publisher:${this._name} connected to ws://${this._ip}:${Broker.DEFAULT_WEB_SOCKET_PORT}, starting service...`, MF_SERVER_IDENTIFIER, "info");
this._isConnected = true;
const addPublisherAction = new AddPublisherAction({
name: this._name,
ip: this._ip,
remoteTypeTarPath: this._remoteTypeTarPath
});
this._publishWebSocket?.send(JSON.stringify(addPublisherAction));
this._connectSubscribers();
});
this._publishWebSocket.on("message", async (message) => {
try {
const parsedMessage = JSON.parse(message.toString());
if (parsedMessage.type === "Log") {
if (parsedMessage.kind === LogKind.BrokerExitLog) {
fileLog(`Receive broker exit signal, ${this._name} service will exit...`, MF_SERVER_IDENTIFIER, "warn");
this._exit();
}
}
if (parsedMessage.type === "API") {
if (parsedMessage.kind === APIKind.FETCH_TYPES) {
const { payload: { remoteInfo } } = parsedMessage;
fileLog(`${this._name} Receive broker FETCH_TYPES, payload as follows: ${JSON.stringify(remoteInfo, null, 2)}.`, MF_SERVER_IDENTIFIER, "info");
await this.fetchDynamicRemoteTypes({ remoteInfo });
}
}
} catch (err) {
console.error(err);
const exitPublisher = new ExitPublisherAction({
name: this._name,
ip: this._ip
});
const exitSubscriber = new ExitSubscriberAction({
name: this._name,
ip: this._ip,
publishers: this._remotes.map((remote) => ({
name: remote.name,
ip: remote.ip
}))
});
this._publishWebSocket?.send(JSON.stringify(exitPublisher));
this._publishWebSocket?.send(JSON.stringify(exitSubscriber));
fileLog("Parse messages error, ModuleFederationDevServer will exit...", MF_SERVER_IDENTIFIER, "fatal");
this._exit();
}
});
this._publishWebSocket.on("close", (code) => {
fileLog(`Connection closed with code ${code}.`, MF_SERVER_IDENTIFIER, "warn");
this._publishWebSocket && this._publishWebSocket.close();
this._publishWebSocket = null;
if (!this._reconnect) return;
const reconnectTime = fib(++this._reconnectTimes);
fileLog(`start reconnecting to server after ${reconnectTime}s.`, MF_SERVER_IDENTIFIER, "info");
setTimeout(() => this._connectPublishToServer(), reconnectTime * 1e3);
});
this._publishWebSocket.on("error", this._tryCreateBackgroundBroker.bind(this));
}
_connectSubscriberToServer(remote) {
const { name, ip } = remote;
fileLog(`remote module:${name} trying to connect to ws://${ip}:${Broker.DEFAULT_WEB_SOCKET_PORT}...`, MF_SERVER_IDENTIFIER, "info");
const identifier = getIdentifier({
name,
ip
});
this._subscriberWebsocketMap[identifier] = new WebSocket(`ws://${ip}:${Broker.DEFAULT_WEB_SOCKET_PORT}?WEB_SOCKET_CONNECT_MAGIC_ID=${Broker.WEB_SOCKET_CONNECT_MAGIC_ID}`);
this._subscriberWebsocketMap[identifier].on("open", () => {
fileLog(`Current pid: ${process.pid} remote module: ${name} connected to ws://${ip}:${Broker.DEFAULT_WEB_SOCKET_PORT}, starting service...`, MF_SERVER_IDENTIFIER, "info");
const addSubscriber = new AddSubscriberAction({
name: this._name,
ip: this._ip,
publishers: [{
name,
ip
}]
});
this._subscriberWebsocketMap[identifier].send(JSON.stringify(addSubscriber));
});
this._subscriberWebsocketMap[identifier].on("message", async (message) => {
try {
const parsedMessage = JSON.parse(message.toString());
if (parsedMessage.type === "Log") {
if (parsedMessage.kind === LogKind.BrokerExitLog) {
fileLog(`${identifier}'s Server exit, thus ${identifier} will no longer has reload ability.`, MF_SERVER_IDENTIFIER, "warn");
this._exit();
}
}
if (parsedMessage.type === "API") {
if (parsedMessage.kind === APIKind.UPDATE_SUBSCRIBER) {
const { payload: { updateKind, updateSourcePaths, name: subscribeName, remoteTypeTarPath, updateMode } } = parsedMessage;
await this._updateSubscriber({
remoteTypeTarPath,
name: subscribeName,
updateKind,
updateMode,
updateSourcePaths
});
}
}
} catch (err) {
console.error(err);
const exitSubscriber = new ExitSubscriberAction({
name: this._name,
ip: this._ip,
publishers: [{
name,
ip
}]
});
this._subscriberWebsocketMap[identifier].send(JSON.stringify(exitSubscriber));
fileLog(`${identifier} exit,
error: ${err instanceof Error ? err.toString() : JSON.stringify(err)}
`, MF_SERVER_IDENTIFIER, "warn");
}
});
this._subscriberWebsocketMap[identifier].on("close", (code) => {
fileLog(`Connection closed with code ${code}.`, MF_SERVER_IDENTIFIER, "warn");
this._subscriberWebsocketMap[identifier]?.close();
delete this._subscriberWebsocketMap[identifier];
});
this._subscriberWebsocketMap[identifier].on("error", (err) => {
if ("code" in err && err.code === "ETIMEDOUT") fileLog(`Can not connect ${JSON.stringify(remote)}, please make sure this remote is started locally.`, MF_SERVER_IDENTIFIER, "warn");
else console.error(err);
this._subscriberWebsocketMap[identifier]?.close();
delete this._subscriberWebsocketMap[identifier];
});
}
_connectSubscribers() {
this._remotes.forEach((remote) => {
this._connectSubscriberToServer(remote);
});
}
async _updateSubscriber(options) {
const { updateMode, updateKind, updateSourcePaths, name, remoteTypeTarPath, remoteInfo } = options;
fileLog(`[_updateSubscriber] run, options: ${JSON.stringify(options, null, 2)}`, MF_SERVER_IDENTIFIER, "warn");
if (updateMode === UpdateMode.PASSIVE && updateSourcePaths.includes(this._name)) {
fileLog(`[_updateSubscriber] run, updateSourcePaths:${updateSourcePaths} includes ${this._name}, update ignore!`, MF_SERVER_IDENTIFIER, "warn");
return;
}
if (updateSourcePaths.slice(-1)[0] === this._name) {
fileLog(`[_updateSubscriber] run, updateSourcePaths:${updateSourcePaths} ends is ${this._name}, update ignore!`, MF_SERVER_IDENTIFIER, "warn");
return;
}
fileLog(`[_updateSubscriber] run, updateSourcePaths:${updateSourcePaths}, current module:${this._name}, update start...`, MF_SERVER_IDENTIFIER, "info");
await this._updateCallback({
name,
updateMode,
updateKind,
updateSourcePaths,
remoteTypeTarPath,
remoteInfo
});
const newUpdateSourcePaths = updateSourcePaths.concat(this._name);
const updatePublisher = new UpdatePublisherAction({
name: this._name,
ip: this._ip,
updateMode: UpdateMode.PASSIVE,
updateKind,
updateSourcePaths: newUpdateSourcePaths,
remoteTypeTarPath: this._remoteTypeTarPath
});
fileLog(`[_updateSubscriber] run, updateSourcePaths:${newUpdateSourcePaths}, update publisher ${this._name} start...`, MF_SERVER_IDENTIFIER, "info");
this._publishWebSocket?.send(JSON.stringify(updatePublisher));
}
_tryCreateBackgroundBroker(err) {
if (!((err?.code === "ECONNREFUSED" || err?.code === "ETIMEDOUT") && err.port === Broker.DEFAULT_WEB_SOCKET_PORT)) {
fileLog(`websocket error: ${err.stack}`, MF_SERVER_IDENTIFIER, "fatal");
return;
}
fileLog(`Failed to connect to ws://${this._ip}:${Broker.DEFAULT_WEB_SOCKET_PORT}...`, MF_SERVER_IDENTIFIER, "fatal");
this._isReconnecting = true;
setTimeout(() => {
this._isReconnecting = false;
if (this._reconnect === false) return;
fileLog("Creating new background broker...", MF_SERVER_IDENTIFIER, "warn");
createBroker().on("message", (message) => {
if (message === "ready") {
fileLog("background broker started.", MF_SERVER_IDENTIFIER, "info");
this._reconnectTimes = 1;
if (process.send) process.send("ready");
}
});
}, Math.ceil(100 * Math.random()));
}
_stopWhenSIGTERMOrSIGINT() {
process.on("SIGTERM", () => {
fileLog(`Process(${process.pid}) SIGTERM, ModuleFederationDevServer will exit...`, MF_SERVER_IDENTIFIER, "warn");
this._exit();
});
process.on("SIGINT", () => {
fileLog(`Process(${process.pid}) SIGINT, ModuleFederationDevServer will exit...`, MF_SERVER_IDENTIFIER, "warn");
this._exit();
});
}
_handleUnexpectedExit() {
process.on("unhandledRejection", (error) => {
if (this._isReconnecting) return;
console.error("Unhandled Rejection Error: ", error);
fileLog(`Process(${process.pid}) unhandledRejection, garfishModuleServer will exit...`, MF_SERVER_IDENTIFIER, "error");
this._exit();
});
process.on("uncaughtException", (error) => {
if (this._isReconnecting) return;
console.error("Unhandled Exception Error: ", error);
fileLog(`Process(${process.pid}) uncaughtException, garfishModuleServer will exit...`, MF_SERVER_IDENTIFIER, "error");
this._exit();
});
}
_exit() {
this._reconnect = false;
if (this._publishWebSocket) {
const exitPublisher = new ExitPublisherAction({
name: this._name,
ip: this._ip
});
this._publishWebSocket.send(JSON.stringify(exitPublisher));
this._publishWebSocket.on("message", (message) => {
const parsedMessage = JSON.parse(message.toString());
fileLog(`[${parsedMessage.kind}]: ${JSON.stringify(parsedMessage)}`, MF_SERVER_IDENTIFIER, "info");
});
}
if (this._publishWebSocket) {
this._publishWebSocket.close();
this._publishWebSocket = null;
}
process.exit(0);
}
exit() {
this._exit();
}
update(options) {
if (!this._publishWebSocket || !this._isConnected) return;
const { updateKind, updateMode, updateSourcePaths, clientName } = options;
fileLog(`update run, ${this._name} module update, updateKind: ${updateKind}, updateMode: ${updateMode}, updateSourcePaths: ${updateSourcePaths}`, MF_SERVER_IDENTIFIER, "info");
if (updateKind === UpdateKind.RELOAD_PAGE) {
const notifyWebClient = new NotifyWebClientAction({
name: clientName || this._name,
updateMode
});
this._publishWebSocket.send(JSON.stringify(notifyWebClient));
return;
}
const updatePublisher = new UpdatePublisherAction({
name: this._name,
ip: this._ip,
updateMode,
updateKind,
updateSourcePaths: [this._name],
remoteTypeTarPath: this._remoteTypeTarPath
});
this._publishWebSocket.send(JSON.stringify(updatePublisher));
}
async fetchDynamicRemoteTypes(options) {
const { remoteInfo, once } = options;
const updateMode = UpdateMode.PASSIVE;
const updateKind = UpdateKind.UPDATE_TYPE;
fileLog(`fetchDynamicRemoteTypes: remoteInfo: ${JSON.stringify(remoteInfo)}`, MF_SERVER_IDENTIFIER, "info");
await this._updateCallback({
name: this._name,
updateMode,
updateKind,
updateSourcePaths: [],
remoteTypeTarPath: "",
remoteInfo,
once
});
const updatePublisher = new UpdatePublisherAction({
name: this._name,
ip: this._ip,
updateMode,
updateKind,
updateSourcePaths: [this._name],
remoteTypeTarPath: this._remoteTypeTarPath
});
this._publishWebSocket.send(JSON.stringify(updatePublisher));
}
};
//#endregion
//#region src/server/createHttpServer.ts
async function createHttpServer(options) {
const { typeTarPath } = options;
const freeport = await getFreePort();
const server = http.createServer((req, res) => {
if ((req.url?.split("?")[0] ?? "/") === `/${DEFAULT_TAR_NAME}`) {
res.statusCode = 200;
res.setHeader("Content-Type", "application/x-gzip");
if (req.method === "HEAD") {
res.end();
return;
}
const stream = fs.createReadStream(typeTarPath);
stream.on("error", () => {
if (!res.headersSent) res.statusCode = 500;
res.end();
});
res.on("close", () => {
stream.destroy();
});
stream.pipe(res);
return;
}
res.statusCode = 404;
res.end();
});
server.listen(freeport);
return {
server,
serverAddress: `http://${getIPV4()}:${freeport}`
};
}
//#endregion
//#region src/core/lib/typeScriptCompiler.ts
const STARTS_WITH_SLASH = /^\//;
const DEFINITION_FILE_EXTENSION = ".d.ts";
const retrieveMfTypesPath = (tsConfig, remoteOptions) => normalize(tsConfig.compilerOptions.outDir.replace(remoteOptions.compiledTypesFolder, ""));
const retrieveOriginalOutDir = (tsConfig, remoteOptions) => normalize(tsConfig.compilerOptions.outDir.replace(remoteOptions.compiledTypesFolder, "").replace(remoteOptions.typesFolder, ""));
const retrieveMfAPITypesPath = (tsConfig, remoteOptions) => join(retrieveOriginalOutDir(tsConfig, remoteOptions), `${remoteOptions.typesFolder}.d.ts`);
function writeTempTsConfig(tsConfig, context, name, cwd) {
const createHash = (contents) => {
return crypto.createHash("md5").update(contents).digest("hex");
};
const hash = createHash(`${JSON.stringify(tsConfig)}${name}${Date.now()}`);
const tempTsConfigJsonPath = resolve(cwd ?? context, "node_modules", TEMP_DIR, `tsconfig.${hash}.json`);
mkdirSync(dirname(tempTsConfigJsonPath), { recursive: true });
writeFileSync(tempTsConfigJsonPath, JSON.stringify(tsConfig, null, 2));
return tempTsConfigJsonPath;
}
const removeExt = (f) => {
const vueExt = ".vue";
const ext = extname(f);
if (ext === vueExt) return f;
const regexPattern = new RegExp(`\\${ext}$`);
return f.replace(regexPattern, "");
};
function getExposeKey(options) {
const { filePath, rootDir, outDir, mapExposeToEntry } = options;
return mapExposeToEntry[relative(outDir, filePath.replace(new RegExp(`\\.d.ts$`), ""))];
}
const processTypesFile = async (options) => {
const { outDir, filePath, rootDir, cb, mapExposeToEntry, mfTypePath } = options;
if (!existsSync(filePath)) return;
if ((await stat(filePath)).isDirectory()) {
const files = await readdir(filePath);
await Promise.all(files.map((file) => processTypesFile({
...options,
filePath: join(filePath, file)
})));
} else if (filePath.endsWith(".d.ts")) {
const exposeKey = getExposeKey({
filePath,
rootDir,
outDir,
mapExposeToEntry
});
if (exposeKey) {
const mfeTypeEntry = join(mfTypePath, `${exposeKey === "." ? "index" : exposeKey}${DEFINITION_FILE_EXTENSION}`);
const mfeTypeEntryDirectory = dirname(mfeTypeEntry);
const relativePathToOutput = relative(mfeTypeEntryDirectory, filePath).replace(DEFINITION_FILE_EXTENSION, "").replace(STARTS_WITH_SLASH, "").split(sep).join("/");
mkdirSync(mfeTypeEntryDirectory, { recursive: true });
await writeFile(mfeTypeEntry, `export * from './${relativePathToOutput}';\nexport { default } from './${relativePathToOutput}';`);
}
cb(await readFile(filePath, "utf8"));
}
};
const getPMFromUserAgent = () => {
const userAgent = process.env["npm_config_user_agent"];
if (userAgent == null) return "null";
return userAgent.split("/")[0];
};
const resolvePackageManagerExecutable = () => {
switch (getPMFromUserAgent()) {
case "yarn": return "yarn";
default: return "npx";
}
};
const splitCommandArgs = (value) => {
const args = [];
let current = "";
let quote = null;
let escaped = false;
for (const char of value) {
if (escaped) {
current += char;
escaped = false;
continue;
}
if (char === "\\") {
escaped = true;
continue;
}
if (quote) {
if (char === quote) quote = null;
else current += char;
continue;
}
if (char === "\"" || char === "'") {
quote = char;
continue;
}
if (char.trim() === "") {
if (current) {
args.push(current);
current = "";
}
continue;
}
current += char;
}
if (current) args.push(current);
return args;
};
const formatCommandForDisplay = (executable, args) => {
const formatArg = (arg) => {
if (/[\s'"]/.test(arg)) return JSON.stringify(arg);
return arg;
};
return [executable, ...args].map(formatArg).join(" ");
};
const compileTs = async (mapComponentsToExpose, tsConfig, remoteOptions) => {
if (!Object.keys(mapComponentsToExpose).length) return;
const { compilerOptions } = tsConfig;
const tempTsConfigJsonPath = writeTempTsConfig(tsConfig, remoteOptions.context, remoteOptions.moduleFederationConfig.name || "mf", typeof remoteOptions.moduleFederationConfig.dts !== "boolean" ? remoteOptions.moduleFederationConfig.dts?.cwd ?? void 0 : void 0);
logger$1.debug(`tempTsConfigJsonPath: ${tempTsConfigJsonPath}`);
try {
const mfTypePath = retrieveMfTypesPath(tsConfig, remoteOptions);
const thirdPartyExtractor = new ThirdPartyExtractor({
destDir: resolve(mfTypePath, "node_modules"),
context: remoteOptions.context,
exclude: typeof remoteOptions.extractThirdParty === "object" ? remoteOptions.extractThirdParty.exclude : void 0
});
const execPromise = util.promisify(execFile);
const pmExecutable = resolvePackageManagerExecutable();
const compilerArgs = splitCommandArgs(remoteOptions.compilerInstance);
const cmdArgs = [
...compilerArgs.length > 0 ? compilerArgs : [remoteOptions.compilerInstance],
"--project",
tempTsConfigJsonPath
];
const cmd = formatCommandForDisplay(pmExecutable, cmdArgs);
try {
await execPromise(pmExecutable, cmdArgs, {
cwd: typeof remoteOptions.moduleFederationConfig.dts !== "boolean" ? remoteOptions.moduleFederationConfig.dts?.cwd ?? void 0 : void 0,
shell: process.platform === "win32"
});
} catch (err) {
if (compilerOptions.tsBuildInfoFile) try {
await rm(compilerOptions.tsBuildInfoFile);
} catch (e) {}
logAndReport(TYPE_001, typeDescMap, { cmd }, (msg) => {
throw new Error(msg);
}, void 0);
}
const mapExposeToEntry = Object.fromEntries(Object.entries(mapComponentsToExpose).map(([exposed, filename]) => {
const normalizedFileName = normalize(filename);
let relativeFileName = "";
if (isAbsolute(normalizedFileName)) relativeFileName = relative(tsConfig.compilerOptions.rootDir, normalizedFileName);
else relativeFileName = relative(tsConfig.compilerOptions.rootDir, resolve(remoteOptions.context, normalizedFileName));
return [removeExt(relativeFileName), exposed];
}));
const cb = remoteOptions.extractThirdParty ? thirdPartyExtractor.collectPkgs.bind(thirdPartyExtractor) : () => void 0;
await processTypesFile({
outDir: compilerOptions.outDir,
filePath: compilerOptions.outDir,
rootDir: compilerOptions.rootDir,
mfTypePath,
cb,
mapExposeToEntry
});
if (remoteOptions.extractThirdParty) await thirdPartyExtractor.copyDts();
if (remoteOptions.deleteTsConfig) await rm(tempTsConfigJsonPath);
} catch (err) {
throw err;
}
};
//#endregion
//#region src/core/lib/archiveHandler.ts
const retrieveTypesZipPath = (mfTypesPath, remoteOptions) => join(mfTypesPath.replace(remoteOptions.typesFolder, ""), `${remoteOptions.typesFolder}.zip`);
const createTypesArchive = async (tsConfig, remoteOptions) => {
const mfTypesPath = retrieveMfTypesPath(tsConfig, remoteOptions);
const zip = new AdmZip();
zip.addLocalFolder(mfTypesPath);
return zip.writeZipPromise(retrieveTypesZipPath(mfTypesPath, remoteOptions));
};
const downloadErrorLogger = (destinationFolder, fileToDownload) => (reason) => {
throw {
...reason,
message: `Network error: Unable to download federated mocks for '${destinationFolder}' from '${fileToDownload}' because '${reason.message}'`
};
};
const retrieveTypesArchiveDestinationPath = (hostOptions, destinationFolder) => {
return resolve(hostOptions.context, hostOptions.typesFolder, destinationFolder);
};
const downloadTypesArchive = (hostOptions) => {
let retries = 0;
return async ([destinationFolder, fileToDownload]) => {
const destinationPath = retrieveTypesArchiveDestinationPath(hostOptions, destinationFolder);
while (retries++ < hostOptions.maxRetries) try {
const url = new URL(fileToDownload).href;
const response = await nativeFetch(url, {
responseType: "arraybuffer",
timeout: hostOptions.timeout,
family: hostOptions.family
}).catch(downloadErrorLogger(destinationFolder, url));
if (typeof response.headers?.["content-type"] === "string" && response.headers["content-type"].includes("text/html")) throw new Error(`${url} receives invalid content-type: ${response.headers["content-type"]}`);
try {
if (hostOptions.deleteTypesFolder) await rm(destinationPath, {
recursive: true,
force: true
});
} catch (error) {
fileLog(`Unable to remove types folder, ${error}`, "downloadTypesArchive", "error");
}
new AdmZip(Buffer.from(response.data)).extractAllTo(destinationPath, true);
fileLog(`zip.extractAllTo success destinationPath: ${destinationPath}; url: ${url}`, "downloadTypesArchive", "info");
return [destinationFolder, destinationPath];
} catch (error) {
fileLog(`Error during types archive download: ${error?.message || "unknown error"}`, "downloadTypesArchive", "error");
if (retries >= hostOptions.maxRetries) {
logger$1.error(`Failed to download types archive from "${fileToDownload}". Set FEDERATION_DEBUG=true for details.`);
if (hostOptions.abortOnError !== false) throw error;
return;
}
}
};
};
//#endregion
//#region src/core/configurations/hostPlugin.ts
const defaultOptions$1 = {
typesFolder: "@mf-types",
remoteTypesFolder: "@mf-types",
deleteTypesFolder: true,
maxRetries: 3,
implementation: "",
context: process.cwd(),
abortOnError: true,
consumeAPITypes: false,
runtimePkgs: [],
remoteTypeUrls: {},
timeout: 6e4,
typesOnBuild: false,
family: 0
};
const buildZipUrl = (hostOptions, url) => {
const remoteUrl = new URL(url, "file:");
remoteUrl.pathname = `${remoteUrl.pathname.split("/").slice(0, -1).join("/")}/${hostOptions.remoteTypesFolder}.zip`;
return remoteUrl.protocol === "file:" ? remoteUrl.pathname : remoteUrl.href;
};
const buildApiTypeUrl = (zipUrl) => {
if (!zipUrl) return;
return zipUrl.replace(".zip", ".d.ts");
};
const retrieveRemoteInfo = (options) => {
const { hostOptions, remoteAlias, remote } = options;
const { remoteTypeUrls } = hostOptions;
let decodedRemote = remote;
if (decodedRemote.startsWith(ENCODE_NAME_PREFIX)) decodedRemote = decodeName(decodedRemote, ENCODE_NAME_PREFIX);
const parsedInfo = parseEntry(decodedRemote, void 0, "@");
const url = "entry" in parsedInfo ? parsedInfo.entry : parsedInfo.name === decodedRemote ? decodedRemote : "";
let zipUrl = "";
let apiTypeUrl = "";
const name = parsedInfo.name || remoteAlias;
const remoteTypeUrl = typeof remoteTypeUrls === "object" && remoteTypeUrls[name];
if (remoteTypeUrl) {
zipUrl = remoteTypeUrl.zip;
apiTypeUrl = remoteTypeUrl.api;
}
const shouldResolveTypeUrlsByConvention = Boolean(url && !url.includes(MANIFEST_EXT));
if (!zipUrl && shouldResolveTypeUrlsByConvention) zipUrl = buildZipUrl(hostOptions, url);
if (!apiTypeUrl && zipUrl && (remoteTypeUrl || shouldResolveTypeUrlsByConvention)) apiTypeUrl = buildApiTypeUrl(zipUrl);
return {
name,
url,
zipUrl,
apiTypeUrl,
alias: remoteAlias
};
};
const resolveRemotes = (hostOptions) => {
const parsedOptions = utils.parseOptions(hostOptions.moduleFederationConfig.remotes || {}, (item, key) => ({
remote: Array.isArray(item) ? item[0] : item,
key
}), (item, key) => ({
remote: Array.isArray(item.external) ? item.external[0] : item.external,
key
}));
const remoteTypeUrls = hostOptions.remoteTypeUrls ?? {};
if (typeof remoteTypeUrls !== "object") throw new Error("remoteTypeUrls must be consumed before resolveRemotes");
const remoteInfos = Object.keys(remoteTypeUrls).reduce((sum, remoteName) => {
const { zip, api, alias } = remoteTypeUrls[remoteName];
sum[alias] = {
name: remoteName,
url: "",
zipUrl: zip,
apiTypeUrl: api,
alias: alias || remoteName
};
return sum;
}, {});
return parsedOptions.reduce((accumulator, item) => {
const { key, remote } = item[1];
const res = retrieveRemoteInfo({
hostOptions,
remoteAlias: key,
remote
});
if (accumulator[key]) {
accumulator[key] = {
...accumulator[key],
url: res.url,
apiTypeUrl: accumulator[key].apiTypeUrl || res.apiTypeUrl
};
return accumulator;
}
accumulator[key] = res;
return accumulator;
}, remoteInfos);
};
const retrieveHostConfig = (options) => {
validateOptions(options);
const hostOptions = {
...defaultOptions$1,
...options
};
return {
hostOptions,
mapRemotesToDownload: resolveRemotes(hostOptions)
};
};
//#endregion
//#region src/core/constant.ts
const REMOTE_ALIAS_IDENTIFIER = "REMOTE_ALIAS_IDENTIFIER";
const REMOTE_API_TYPES_FILE_NAME = "apis.d.ts";
const HOST_API_TYPES_FILE_NAME = "index.d.ts";
//#endregion
//#region src/core/lib/DTSManager.ts
var DTSManager = class {
constructor(options) {
this.options = cloneDeepOptions(options);
this.runtimePkgs = [
"@module-federation/runtime",
"@module-federation/enhanced/runtime",
"@module-federation/runtime-tools"
];
this.loadedRemoteAPIAlias = /* @__PURE__ */ new Set();
this.remoteAliasMap = {};
this.extraOptions = options?.extraOptions || {};
this.updatedRemoteInfos = {};
}
generateAPITypes(mapComponentsToExpose) {
const exposePaths = /* @__PURE__ */ new Set();
const packageType = Object.keys(mapComponentsToExpose).reduce((sum, exposeKey) => {
const exposePath = path.join(REMOTE_ALIAS_IDENTIFIER, exposeKey).split(path.sep).join("/");
exposePaths.add(`'${exposePath}'`);
sum = `T extends '${exposePath}' ? typeof import('${exposePath}') :` + sum;
return sum;
}, "any;");
return `
export type RemoteKeys = ${[...exposePaths].join(" | ")};
type PackageType<T> = ${packageType}`;
}
async extractRemoteTypes(options) {
const { remoteOptions, tsConfig } = options;
if (!remoteOptions.extractRemoteTypes) return;
let hasRemotes = false;
const remotes = remoteOptions.moduleFederationConfig.remotes;
if (remotes) {
if (Array.isArray(remotes)) hasRemotes = Boolean(remotes.length);
else if (typeof remotes === "object") hasRemotes = Boolean(Object.keys(remotes).length);
}
const mfTypesPath = retrieveMfTypesPath(tsConfig, remoteOptions);
if (hasRemotes && this.options.host) try {
const { hostOptions } = retrieveHostConfig(this.options.host);
const remoteTypesFolder = path.resolve(hostOptions.context, hostOptions.typesFolder);
const targetDir = path.join(mfTypesPath, "node_modules");
if (fs.existsSync(remoteTypesFolder)) {
const targetFolder = path.resolve(remoteOptions.context, targetDir);
await mkdir(targetFolder, { recursive: true });
await cp(remoteTypesFolder, targetFolder, {
recursive: true,
force: true
});
}
} catch (err) {
if (this.options.host?.abortOnError === false) fileLog(`Unable to copy remote types, ${err}`, "extractRemoteTypes", "error");
else throw err;
}
}
async generateTypes() {
try {
const { options } = this;
if (!options.remote) throw new Error("options.remote is required if you want to generateTypes");
const { remoteOptions, tsConfig, mapComponentsToExpose } = retrieveRemoteConfig(options.remote);
if (!Object.keys(mapComponentsToExpose).length) return;
if (!tsConfig.files?.length) {
logger$1.info("No type files to compile, skip");
return;
}
if (tsConfig.compilerOptions.tsBuildInfoFile) try {
const tsBuildInfoFile = path.resolve(remoteOptions.context, tsConfig.compilerOptions.tsBuildInfoFile);
const mfTypesPath = retrieveMfTypesPath(tsConfig, remoteOptions);
if (!fs.existsSync(mfTypesPath)) fs.rmSync(tsBuildInfoFile, { force: true });
} catch (e) {}
await this.extractRemoteTypes({
remoteOptions,
tsConfig,
mapComponentsToExpose
});
await compileTs(mapComponentsToExpose, tsConfig, remoteOptions);
await createTypesArchive(tsConfig, remoteOptions);
let apiTypesPath = "";
if (remoteOptions.generateAPITypes) {
const apiTypes = this.generateAPITypes(mapComponentsToExpose);
apiTypesPath = retrieveMfAPITypesPath(tsConfig, remoteOptions);
fs.writeFileSync(apiTypesPath, apiTypes);
}
try {
if (remoteOptions.deleteTypesFolder) await rm(retrieveMfTypesPath(tsConfig, remoteOptions), {
recursive: true,
force: true
});
} catch (err) {
if (isDebugMode()) console.error(err);
}
logger$1.success("Federated types created correctly");
} catch (error) {
if (this.options.remote?.abortOnError === false) {
if (this.options.displayErrorInTerminal) logger$1.error(error);
} else throw error;
}
}
async requestRemoteManifest(remoteInfo, hostOptions) {
try {
if (!remoteInfo.url.includes(MANIFEST_EXT)) return remoteInfo;
if (remoteInfo.zipUrl) return remoteInfo;
const url = remoteInfo.url;
const manifestJson = (await nativeFetch(url, {
timeout: hostOptions.timeout,
family: hostOptions.family
})).data;
if (!manifestJson.metaData.types.zip) throw new Error(`Can not get ${remoteInfo.name}'s types archive url!`);
const addProtocol = (u) => {
if (u.startsWith("//")) return `https:${u}`;
return u;
};
let publicPath;
if ("publicPath" in manifestJson.metaData) publicPath = manifestJson.metaData.publicPath;
else {
const getPublicPath = new Function(manifestJson.metaData.getPublicPath);
if (manifestJson.metaData.getPublicPath.startsWith("function")) publicPath = getPublicPath()();
else publicPath = getPublicPath();
}
if (publicPath === "auto") publicPath = inferAutoPublicPath(remoteInfo.url);
const normalizedPublicPath = addProtocol(publicPath).endsWith("/") ? addProtocol(publicPath) : `${addProtocol(publicPath)}/`;
remoteInfo.zipUrl = new URL(manifestJson.metaData.types.zip, normalizedPublicPath).href;
if (!manifestJson.metaData.types.api) {
console.warn(`Can not get ${remoteInfo.name}'s api types url!`);
remoteInfo.apiTypeUrl = "";
return remoteInfo;
}
remoteInfo.apiTypeUrl = new URL(manifestJson.metaData.types.api, normalizedPublicPath).href;
return remoteInfo;
} catch (_err) {
fileLog(`fetch manifest failed, ${_err}, ${remoteInfo.name} will be ignored`, "requestRemoteManifest", "error");
return remoteInfo;
}
}
async consumeTargetRemotes(hostOptions, remoteInfo) {
if (!remoteInfo.zipUrl) throw new Error(`Can not get ${remoteInfo.name}'s types archive url!`);
return downloadTypesArchive(hostOptions)([remoteInfo.alias, remoteInfo.zipUrl]);
}
async downloadAPITypes(remoteInfo, destinationPath, hostOptions) {
const { apiTypeUrl } = remoteInfo;
if (!apiTypeUrl) return;
try {
let apiTypeFile = (await nativeFetch(apiTypeUrl, {
timeout: hostOptions.timeout,
family: hostOptions.family
})).data;
apiTypeFile = apiTypeFile.replaceAll(REMOTE_ALIAS_IDENTIFIER, remoteInfo.alias);
const filePath = path.join(destinationPath, REMOTE_API_TYPES_FILE_NAME);
fs.writeFileSync(filePath, apiTypeFile);
const existed = this.loadedRemoteAPIAlias.has(remoteInfo.alias);
this.loadedRemoteAPIAlias.add(remoteInfo.alias);
fileLog(`success`, "downloadAPITypes", "info");
return existed;
} catch (err) {
fileLog(`Unable to download "${remoteInfo.name}" api types, ${err}`, "downloadAPITypes", "error");
}
}
consumeAPITypes(hostOptions) {
const apiTypeFileName = path.join(hostOptions.context, hostOptions.typesFolder, HOST_API_TYPES_FILE_NAME);
try {
const existedFile = fs.readFileSync(apiTypeFileName, "utf-8");
new ThirdPartyExtractor({ destDir: "" }).collectTypeImports(existedFile).forEach((existedImport) => {
const alias = existedImport.split("./").slice(1).join("./").replace("/apis.d.ts", "");
this.loadedRemoteAPIAlias.add(alias);
});
} catch (err) {}
if (!this.loadedRemoteAPIAlias.size) return;
const packageTypes = [];
const remoteKeys = [];
const importTypeStr = [...this.loadedRemoteAPIAlias].sort().map((alias, index) => {
const remoteKey = `RemoteKeys_${index}`;
const packageType = `PackageType_${index}`;
packageTypes.push(`T extends ${remoteKey} ? ${packageType}<T>`);
remoteKeys.push(remoteKey);
return `import type { PackageType as ${packageType},RemoteKeys as ${remoteKey} } from './${alias}/apis.d.ts';`;
}).join("\n");
const remoteKeysStr = `type RemoteKeys = ${remoteKeys.join(" | ")};`;
const packageTypesStr = `type PackageType<T, Y=any> = ${[...packageTypes, "Y"].join(" :\n")} ;`;
const runtimePkgs = /* @__PURE__ */ new Set();
[...this.runtimePkgs, ...hostOptions.runtimePkgs].forEach((pkg) => {
runtimePkgs.add(pkg);
});
const fileStr = `${importTypeStr}
${[...runtimePkgs].map((pkg) => {
return `declare module "${pkg}" {
${remoteKeysStr}
${packageTypesStr}
export function loadRemote<T extends RemoteKeys,Y>(packageName: T): Promise<PackageType<T, Y>>;
export function loadRemote<T extends string,Y>(packageName: T): Promise<PackageType<T, Y>>;
}`;
}).join("\n")}
`;
fs.writeFileSync(path.join(hostOptions.context, hostOptions.typesFolder, HOST_API_TYPES_FILE_NAME), fileStr);
}
async consumeArchiveTypes(options) {
const { hostOptions, mapRemotesToDownload } = retrieveHostConfig(options);
const downloadPromises = Object.entries(mapRemotesToDownload).map(async (item) => {
const remoteInfo = item[1];
if (!this.remoteAliasMap[remoteInfo.alias]) {
const requiredRemoteInfo = await this.requestRemoteManifest(remoteInfo, hostOptions);
this.remoteAliasMap[remoteInfo.alias] = requiredRemoteInfo;
}
return this.consumeTargetRemotes(hostOptions, this.remoteAliasMap[remoteInfo.alias]);
});
return {
hostOptions,
downloadPromisesResult: await Promise.allSettled(downloadPromises)
};
}
async consumeTypes() {
try {
const { options } = this;
if (!options.host) throw new Error("options.host is required if you want to consumeTypes");
const { mapRemotesToDownload } = retrieveHostConfig(options.host);
if (!Object.keys(mapRemotesToDownload).length) return;
const { downloadPromisesResult, hostOptions } = await this.consumeArchiveTypes(options.host);
if (hostOptions.consumeAPITypes) {
await Promise.all(downloadPromisesResult.map(async (item) => {
if (item.status === "rejected" || !item.value) return;
const [alias, destinationPath] = item.value;
const remoteInfo = this.remoteAliasMap[alias];
if (!remoteInfo) return;
await this.downloadAPITypes(remoteInfo, destinationPath, hostOptions);
}));
this.consumeAPITypes(hostOptions);
}
logger$1.success("Federated types extraction completed");
} catch (err) {
if (this.options.host?.abortOnError === false) fileLog(`Unable to consume federated types, ${err}`, "consumeTypes", "error");
else throw err;
}
}
async updateTypes(options) {
try {
const { remoteName, updateMode, remoteTarPath, remoteInfo: updatedRemoteInfo, once } = options;
const hostName = this.options?.host?.moduleFederationConfig?.name;
fileLog(`options: ${JSON.stringify(options, null, 2)};\nhostName: ${hostName}`, "updateTypes", "info");
if (updateMode === UpdateMode.POSITIVE && remoteName === hostName) {
if (!this.options.remote) return;
await this.generateTypes();
} else {
const { remoteAliasMap } = this;
if (!this.options.host) return;
const { hostOptions, mapRemotesToDownload } = retrieveHostConfig(this.options.host);
const loadedRemoteInfo = Object.values(remoteAliasMap).find((i) => i.name === remoteName);
const consumeTypes = async (requiredRemoteInfo) => {
fileLog(`consumeTypes start`, "updateTypes", "info");
if (!requiredRemoteInfo.zipUrl) throw new Error(`Can not get ${requiredRemoteInfo.name}'s types archive url!`);
const [_alias, destinationPath] = await this.consumeTargetRemotes(hostOptions, {
...requiredRemoteInfo,
zipUrl: remoteTarPath || requiredRemoteInfo.zipUrl
});
if (await this.downloadAPITypes(requiredRemoteInfo, destinationPath, hostOptions)) this.consumeAPITypes(hostOptions);
fileLog(`consumeTypes end`, "updateTypes", "info");
};
fileLog(`loadedRemoteInfo: ${JSON.stringify(loadedRemoteInfo, null, 2)}`, "updateTypes", "info");
if (!loadedRemoteInfo) {
const remoteInfo = Object.values(mapRemotesToDownload).find((item) => {
return item.name === remoteName;
});
fileLog(`remoteInfo: ${JSON.stringify(remoteInfo, null, 2)}`, "updateTypes", "info");
if (remoteInfo) {
if (!this.remoteAliasMap[remoteInfo.alias]) {
const requiredRemoteInfo = await this.requestRemoteManifest(remoteInfo, hostOptions);
this.remoteAliasMap[remoteInfo.alias] = requiredRemoteInfo;
}
await consumeTypes(this.remoteAliasMap[remoteInfo.alias]);
} else if (updatedRemoteInfo) {
const consumeDynamicRemoteTypes = async () => {
await consumeTypes(this.updatedRemoteInfos[updatedRemoteInfo.name]);
};
if (!this.updatedRemoteInfos[updatedRemoteInfo.name]) {
const parsedRemoteInfo = retrieveRemoteInfo({
hostOptions,
remoteAlias: updatedRemoteInfo.alias || updatedRemoteInfo.name,
remote: updatedRemoteInfo.url
});
fileLog(`start request manifest`, "consumeTypes", "info");
this.updatedRemoteInfos[updatedRemoteInfo.name] = await this.requestRemoteManifest(parsedRemoteInfo, hostOptions);
fileLog(`end request manifest, this.updatedRemoteInfos[updatedRemoteInfo.name]: ${JSON.stringify(this.updatedRemoteInfos[updatedRemoteInfo.name], null, 2)}`, "updateTypes", "info");
await consumeDynamicRemoteTypes();
}
if (!once && this.updatedRemoteInfos[updatedRemoteInfo.name]) await consumeDynamicRemoteTypes();
}
} else await consumeTypes(loadedRemoteInfo);
}
} catch (err) {
fileLog(`updateTypes fail, ${err}`, "updateTypes", "error");
}
}
};
//#endregion
//#region src/core/lib/utils.ts
const dispatcherCache = /* @__PURE__ */ new Map();
function getDTSManagerConstructor(implementation) {
if (implementation) {
const NewConstructor = __require(implementation);
return NewConstructor.default ? NewConstructor.default : NewConstructor;
}
return DTSManager;
}
const validateOptions = (options) => {
if (!options.moduleFederationConfig) throw new Error("moduleFederationConfig is required");
};
function retrieveTypesAssetsInfo(options) {
let apiTypesPath = "";
let zipTypesPath = "";
try {
const { tsConfig, remoteOptions, mapComponentsToExpose } = retrieveRemoteConfig(options);
if (!Object.keys(mapComponentsToExpose).length || !tsConfig.files.length) return {
apiTypesPath,
zipTypesPath,
zipName: "",
apiFileName: ""
};
zipTypesPath = retrieveTypesZipPath(retrieveMfTypesPath(tsConfig, remoteOptions), remoteOptions);
if (remoteOptions.generateAPITypes) apiTypesPath = retrieveMfAPITypesPath(tsConfig, remoteOptions);
return {
apiTypesPath,
zipTypesPath,
zipName: path.basename(zipTypesPath),
apiFileName: path.basename(apiTypesPath)
};
} catch (err) {
console.error(ansiColors.red(`Unable to compile federated types, ${err}`));
return {
apiTypesPath: "",
zipTypesPath: "",
zipName: "",
apiFileName: ""
};
}
}
function isDebugMode() {
return Boolean(process.env["FEDERATION_DEBUG"]) || process.env["NODE_ENV"] === "test";
}
const isTSProject = (dtsOptions, context = process.cwd()) => {
if (dtsOptions === false) return false;
try {
let filepath = "";
if (typeof dtsOptions === "object" && dtsOptions.tsConfigPath) filepath = dtsOptions.tsConfigPath;
else filepath = path.resolve(context, "./tsconfig.json");
if (!path.isAbsolute(filepath)) filepath = path.resolve(context, filepath);
return fs.existsSync(filepath);
} catch (err) {
return false;
}
};
function cloneDeepOptions(options) {
const excludeKeys = new Set(["manifest", "async"]);
const cache = /* @__PURE__ */ new WeakMap();
function sanitize(val, key) {
if (key !== void 0 && excludeKeys.has(key) || typeof val === "function") return false;
if (key === "extractThirdParty" && Array.isArray(val)) return val.map(String);
if (Array.isArray(val)) {
if (cache.has(val)) return cache.get(val);
const out = [];
cache.set(val, out);
val.forEach((v, i) => out.push(sanitize(v, String(i))));
return out;
}
if (val !== null && typeof val === "object" && Object.getPrototypeOf(val) === Object.prototype) {
const obj = val;
if (cache.has(obj)) return cache.get(obj);
const out = {};
cache.set(obj, out);
for (const [k, v] of Object.entries(obj)) out[k] = sanitize(v, k);
return out;
}
return val;
}
return structuredClone(sanitize(options));
}
const getEnvHeaders = () => {
const headersStr = getProcessEnv()["MF_ENV_HEADERS"];
if (!headersStr || headersStr === "undefined") return {};
try {
return { ...JSON.parse(headersStr) };
} catch {
return {};
}
};
const createDispatcherFromFamily = (family) => {
if (!family) return void 0;
if (dispatcherCache.has(family)) return dispatcherCache.get(family);
try {
const dispatcher = new Agent({ connect: { family } });
dispatcherCache.set(family, dispatcher);
return dispatcher;
} catch {}
};
const toHeaderRecord = (headers) => {
const out = {};
headers.forEach((value, key) => {
out[key.toLowerCase()] = value;
});
return out;
};
async function nativeFetch(url, config) {
const controller = new AbortController();
const timeoutMs = config?.timeout ?? 6e4;
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
const headers = {
...getEnvHeaders(),
...config?.headers ?? {}
};
const dispatcher = config?.dispatcher ?? createDispatcherFromFamily(config?.family);
try {
const resp = await fetch(url, {
headers,
signal: controller.signal,
...dispatcher ? { dispatcher } : {},
...config?.agent ? { agent: config.agent } : {}
});
const headerRecord = toHeaderRecord(resp.headers);
if (!resp.ok) throw new Error(`Request failed with status ${resp.status}`);
if (config?.responseType === "arraybuffer") return {
data: await resp.arrayBuffer(),
headers: headerRecord,
status: resp.status
};
return {
data: (resp.headers.get("content-type") || "").includes("application/json") || url.endsWith(".json") ? await resp.json() : await resp.text(),
headers: headerRecord,
status: resp.status
};
} finally {
clearTimeout(timeoutId);
}
}
//#endregion
//#region src/core/configurations/remotePlugin.ts
const defaultOptions = {
tsConfigPath: "./tsconfig.json",
typesFolder: "@mf-types",
compiledTypesFolder: "compiled-types",
hostRemoteTypesFolder: "@mf-types",
deleteTypesFolder: true,
additionalFilesToCompile: [],
compilerInstance: "tsc",
compileInChildProcess: false,
implementation: "",
generateAPITypes: false,
context: process.cwd(),
abortOnError: true,
extractRemoteTypes: false,
extractThirdParty: false,
outputDir: "",
deleteTsConfig: true
};
function getEffectiveRootDir(parsedCommandLine) {
const compilerOptions = parsedCommandLine.options;
if (compilerOptions.rootDir) return compilerOptions.rootDir;
const files = parsedCommandLine.fileNames;
if (files.length > 0) return files.map((file) => dirname(file)).reduce((commonPath, fileDir) => {
while (!fileDir.startsWith(commonPath)) commonPath = dirname(commonPath);
return commonPath;
}, files[0]);
if (parsedCommandLine.projectReferences.length) {
const relativeReferences = parsedCommandLine.projectReferences.filter((reference) => !isAbsolute(reference.originalPath ?? reference.path));
const referencesForRoot = relativeReferences.length ? relativeReferences : parsedCommandLine.projectReferences;
return referencesForRoot.map((reference) => dirname(reference.path)).reduce((commonPath, filePath) => {
while (!filePath.startsWith(commonPath)) commonPath = dirname(commonPath);
return commonPath;
}, dirname(referencesForRoot[0].path));
}
throw new Error("Can not get effective rootDir, please set compilerOptions.rootDir !");
}
const getDependentFiles = (rootFiles, configContent, rootDir) => {
const dependentFiles = typescript.createProgram(rootFiles, configContent.options).getSourceFiles().map((file) => file.fileName).filter((file) => !file.endsWith(".d.ts") && file.startsWith(rootDir + "/"));
return dependentFiles.length ? dependentFiles : rootFiles;
};
const readTsConfig = ({ tsConfigPath, typesFolder, compiledTypesFolder, context, additionalFilesToCompile, outputDir }, mapComponentsToExpose) => {
const resolvedTsConfigPath = resolve(context, tsConfigPath);
const readResult = typescript.readConfigFile(resolvedTsConfigPath, typescript.sys.readFile);
if (readResult.error) throw new Error(readResult.error.messageText.toString());
const rawTsConfigJson = readResult.config;
const configContent = typescript.parseJsonConfigFileContent(rawTsConfigJson, typescript.sys, dirname(resolvedTsConfigPath));
const rootDir = getEffectiveRootDir(configContent);
const outDir = resolve(context, outputDir || configContent.options.outDir || "dist", typesFolder, compiledTypesFolder);
const defaultCompilerOptions = {
rootDir,
emitDeclarationOnly: true,
noEmit: false,
declaration: true,
outDir
};
rawTsConfigJson.compilerOptions = rawTsConfigJson.compilerOptions || {};
rawTsConfigJson.compilerOptions = {
incremental: true,
tsBuildInfoFile: resolve(context, "node_modules/.cache/mf-types/.tsbuildinfo"),
...rawTsConfigJson.compilerOptions,
...defaultCompilerOptions
};
const { paths, baseUrl, ...restCompilerOptions } = rawTsConfigJson.compilerOptions || {};
rawTsConfigJson.compilerOptions = restCompilerOptions;
const outDirWithoutTypesFolder = resolve(context, outputDir || configContent.options.outDir || "dist");
const excludeExtensions = [".mdx", ".md"];
const filesToCompile = [...getDependentFiles([...Object.values(mapComponentsToExpose), ...additionalFilesToCompile].filter((filename) => !excludeExtensions.some((ext) => filename.endsWith(ext))), configContent, rootDir), ...configContent.fileNames.filter((filename) => filename.endsWith(".d.ts") && !filename.startsWith(outDirWithoutTypesFolder))];
rawTsConfigJson.include = [];
rawTsConfigJson.files = [...new Set(filesToCompile)];
rawTsConfigJson.exclude = [];
"references" in rawTsConfigJson && delete rawTsConfigJson.references;
rawTsConfigJson.extends = resolvedTsConfigPath;
rawTsConfigJson.compilerOptions.declarationDir = outDir;
return rawTsConfigJson;
};
const TS_EXTENSIONS = [
".ts",
".tsx",
".vue",
".svelte",
".js",
".jsx"
];
const resolveWithExtension = (exposedPath, context) => {
const explicitExtension = extname(exposedPath);
if (TS_EXTENSIONS.includes(explicitExtension)) return resolve(context, exposedPath);
for (const extension of TS_EXTENSIONS) {
const exposedPathWithExtension = resolve(context, `${exposedPath}${extension}`);
if (existsSync(exposedPathWithExtension)) return exposedPathWithExtension;
}
};
const resolveExposes = (remoteOptions) => {
return utils.parseOptions(remoteOptions.moduleFederationConfig.exposes || {}, (item, key) => ({
exposePath: Array.isArray(item) ? item[0] : item,
key
}), (item, key) => ({
exposePath: Array.isArray(item.import) ? item.import[0] : item.import[0],
key
})).reduce((accumulator, item) => {
const { exposePath, key } = item[1];
accumulator[key] = resolveWithExtension(exposePath, remoteOptions.context) || resolveWithExtension(join(exposePath, "index"), remoteOptions.context) || exposePath;
return accumulator;
}, {});
};
const retrieveRemoteConfig = (options) => {
validateOptions(options);
const remoteOptions = {
...defaultOptions,
...options
};
const mapComponentsToExpose = resolveExposes(remoteOptions);
const tsConfig = readTsConfig(remoteOptions, mapComponentsToExpose);
if (tsConfig.compilerOptions.incremental && tsConfig.compilerOptions.tsBuildInfoFile && options.deleteTypesFolder !== true) remoteOptions.deleteTypesFolder = false;
return {
tsConfig,
mapComponentsToExpose,
remoteOptions
};
};
//#endregion
//#region src/core/lib/generateTypes.ts
async function generateTypes(options) {
return new (getDTSManagerConstructor(options.remote?.implementation))(options).generateTypes();
}
//#endregion
//#region src/core/rpc/types.ts
let RpcGMCallTypes = /* @__PURE__ */ function(RpcGMCallTypes) {
RpcGMCallTypes["CALL"] = "mf_call";
RpcGMCallTypes["RESOLVE"] = "mf_resolve";
RpcGMCallTypes["REJECT"] = "mf_reject";
RpcGMCallTypes["EXIT"] = "mf_exit";
return RpcGMCallTypes;
}({});
//#endregion
//#region src/core/rpc/expose-rpc.ts
function exposeRpc(fn) {
const sendMessage = (message) => new Promise((resolve, reject) => {
if (!process$1.send) reject(/* @__PURE__ */ new Error(`Process ${process$1.pid} doesn't have IPC channels`));
else if (!process$1.connected) reject(/* @__PURE__ */ new Error(`Process ${process$1.pid} doesn't have open IPC channels`));
else process$1.send(message, void 0, void 0, (error) => {
if (error) reject(error);
else resolve(void 0);
});
});
const handleMessage = async (message) => {
if (message.type === RpcGMCallTypes.CALL) {
if (!process$1.send) return;
let value, error;
try {
value = await fn(...message.args);
} catch (fnError) {
error = fnError;
}
try {
if (error) await sendMessage({
type: RpcGMCallTypes.REJECT,
id: message.id,
error
});
else await sendMessage({
type: RpcGMCallTypes.RESOLVE,
id: message.id,
value
});
} catch (sendError) {
if (error) {
if (error instanceof Error) console.error(error);
}
console.error(sendError);
}
}
};
process$1.on("message", handleMessage);
}
//#endregion
export { retrieveMfTypesPath as _, cloneDeepOptions as a, ModuleFederationDevServer as b, isTSProject as c, DTSManager as d, HOST_API_TYPES_FILE_NAME as f, retrieveTypesZipPath as g, retrieveHostConfig as h, retrieveRemoteConfig as i, retrieveTypesAssetsInfo as l, REMOTE_API_TYPES_FILE_NAME as m, RpcGMCallTypes as n, getDTSManagerConstructor as o, REMOTE_ALIAS_IDENTIFIER as p, generateTypes as r, isDebugMode as s, exposeRpc as t, validateOptions as u, retrieveOriginalOutDir as v, __exportAll as x, createHttpServer as y };
const require_Action = require('./Action-CzhPMw2i.js');
const require_Broker = require('./Broker-CaenCqdn.js');
let fs = require("fs");
fs = require_Action.__toESM(fs);
let url = require("url");
let path = require("path");
path = require_Action.__toESM(path);
let fs_promises = require("fs/promises");
let _module_federation_managers = require("@module-federation/managers");
let typescript = require("typescript");
typescript = require_Action.__toESM(typescript);
let _module_federation_sdk = require("@module-federation/sdk");
let ansi_colors = require("ansi-colors");
ansi_colors = require_Action.__toESM(ansi_colors);
let undici = require("undici");
let _module_federation_third_party_dts_extractor = require("@module-federation/third-party-dts-extractor");
let adm_zip = require("adm-zip");
adm_zip = require_Action.__toESM(adm_zip);
let crypto = require("crypto");
crypto = require_Action.__toESM(crypto);
let _module_federation_error_codes = require("@module-federation/error-codes");
let _module_federation_error_codes_node = require("@module-federation/error-codes/node");
let child_process = require("child_process");
let util = require("util");
util = require_Action.__toESM(util);
let isomorphic_ws = require("isomorphic-ws");
isomorphic_ws = require_Action.__toESM(isomorphic_ws);
let http = require("http");
http = require_Action.__toESM(http);
let process$1 = require("process");
process$1 = require_Action.__toESM(process$1);
//#region src/server/message/Action/AddPublisher.ts
var AddPublisherAction = class extends require_Action.Action {
constructor(payload) {
super({ payload }, require_Action.ActionKind.ADD_PUBLISHER);
}
};
//#endregion
//#region src/server/message/Action/AddSubscriber.ts
var AddSubscriberAction = class extends require_Action.Action {
constructor(payload) {
super({ payload }, require_Action.ActionKind.ADD_SUBSCRIBER);
}
};
//#endregion
//#region src/server/message/Action/ExitSubscriber.ts
var ExitSubscriberAction = class extends require_Action.Action {
constructor(payload) {
super({ payload }, require_Action.ActionKind.EXIT_SUBSCRIBER);
}
};
//#endregion
//#region src/server/message/Action/ExitPublisher.ts
var ExitPublisherAction = class extends require_Action.Action {
constructor(payload) {
super({ payload }, require_Action.ActionKind.EXIT_PUBLISHER);
}
};
//#endregion
//#region src/server/message/Action/NotifyWebClient.ts
var NotifyWebClientAction = class extends require_Action.Action {
constructor(payload) {
super({ payload }, require_Action.ActionKind.NOTIFY_WEB_CLIENT);
}
};
//#endregion
//#region src/server/message/Action/UpdatePublisher.ts
var UpdatePublisherAction = class extends require_Action.Action {
constructor(payload) {
super({ payload }, require_Action.ActionKind.UPDATE_PUBLISHER);
}
};
//#endregion
//#region src/server/broker/createBroker.ts
const __filename$1 = (0, url.fileURLToPath)(require("url").pathToFileURL(__filename).href);
const __dirname$1 = path.default.dirname(__filename$1);
function createBroker() {
const sub = (0, child_process.fork)(path.default.resolve(__dirname$1, "./start-broker.js"), [], {
detached: true,
stdio: "ignore",
env: process.env
});
sub.send("start");
sub.unref();
return sub;
}
//#endregion
//#region src/server/DevServer.ts
var ModuleFederationDevServer = class {
constructor(ctx) {
this._publishWebSocket = null;
this._subscriberWebsocketMap = {};
this._reconnect = true;
this._reconnectTimes = 0;
this._isConnected = false;
this._isReconnecting = false;
this._updateCallback = () => Promise.resolve(void 0);
const { name, remotes, remoteTypeTarPath, updateCallback } = ctx;
this._ip = require_Broker.getIPV4();
this._name = name;
this._remotes = remotes;
this._remoteTypeTarPath = remoteTypeTarPath;
this._updateCallback = updateCallback;
this._stopWhenSIGTERMOrSIGINT();
this._handleUnexpectedExit();
this._connectPublishToServer();
}
_connectPublishToServer() {
if (!this._reconnect) return;
require_Broker.fileLog(`Publisher:${this._name} Trying to connect to ws://${this._ip}:${require_Broker.Broker.DEFAULT_WEB_SOCKET_PORT}...`, require_Action.MF_SERVER_IDENTIFIER, "info");
this._publishWebSocket = new isomorphic_ws.default(`ws://${this._ip}:${require_Broker.Broker.DEFAULT_WEB_SOCKET_PORT}?WEB_SOCKET_CONNECT_MAGIC_ID=${require_Broker.Broker.WEB_SOCKET_CONNECT_MAGIC_ID}`);
this._publishWebSocket.on("open", () => {
require_Broker.fileLog(`Current pid: ${process.pid}, publisher:${this._name} connected to ws://${this._ip}:${require_Broker.Broker.DEFAULT_WEB_SOCKET_PORT}, starting service...`, require_Action.MF_SERVER_IDENTIFIER, "info");
this._isConnected = true;
const addPublisherAction = new AddPublisherAction({
name: this._name,
ip: this._ip,
remoteTypeTarPath: this._remoteTypeTarPath
});
this._publishWebSocket?.send(JSON.stringify(addPublisherAction));
this._connectSubscribers();
});
this._publishWebSocket.on("message", async (message) => {
try {
const parsedMessage = JSON.parse(message.toString());
if (parsedMessage.type === "Log") {
if (parsedMessage.kind === require_Broker.LogKind.BrokerExitLog) {
require_Broker.fileLog(`Receive broker exit signal, ${this._name} service will exit...`, require_Action.MF_SERVER_IDENTIFIER, "warn");
this._exit();
}
}
if (parsedMessage.type === "API") {
if (parsedMessage.kind === require_Broker.APIKind.FETCH_TYPES) {
const { payload: { remoteInfo } } = parsedMessage;
require_Broker.fileLog(`${this._name} Receive broker FETCH_TYPES, payload as follows: ${JSON.stringify(remoteInfo, null, 2)}.`, require_Action.MF_SERVER_IDENTIFIER, "info");
await this.fetchDynamicRemoteTypes({ remoteInfo });
}
}
} catch (err) {
console.error(err);
const exitPublisher = new ExitPublisherAction({
name: this._name,
ip: this._ip
});
const exitSubscriber = new ExitSubscriberAction({
name: this._name,
ip: this._ip,
publishers: this._remotes.map((remote) => ({
name: remote.name,
ip: remote.ip
}))
});
this._publishWebSocket?.send(JSON.stringify(exitPublisher));
this._publishWebSocket?.send(JSON.stringify(exitSubscriber));
require_Broker.fileLog("Parse messages error, ModuleFederationDevServer will exit...", require_Action.MF_SERVER_IDENTIFIER, "fatal");
this._exit();
}
});
this._publishWebSocket.on("close", (code) => {
require_Broker.fileLog(`Connection closed with code ${code}.`, require_Action.MF_SERVER_IDENTIFIER, "warn");
this._publishWebSocket && this._publishWebSocket.close();
this._publishWebSocket = null;
if (!this._reconnect) return;
const reconnectTime = require_Broker.fib(++this._reconnectTimes);
require_Broker.fileLog(`start reconnecting to server after ${reconnectTime}s.`, require_Action.MF_SERVER_IDENTIFIER, "info");
setTimeout(() => this._connectPublishToServer(), reconnectTime * 1e3);
});
this._publishWebSocket.on("error", this._tryCreateBackgroundBroker.bind(this));
}
_connectSubscriberToServer(remote) {
const { name, ip } = remote;
require_Broker.fileLog(`remote module:${name} trying to connect to ws://${ip}:${require_Broker.Broker.DEFAULT_WEB_SOCKET_PORT}...`, require_Action.MF_SERVER_IDENTIFIER, "info");
const identifier = require_Broker.getIdentifier({
name,
ip
});
this._subscriberWebsocketMap[identifier] = new isomorphic_ws.default(`ws://${ip}:${require_Broker.Broker.DEFAULT_WEB_SOCKET_PORT}?WEB_SOCKET_CONNECT_MAGIC_ID=${require_Broker.Broker.WEB_SOCKET_CONNECT_MAGIC_ID}`);
this._subscriberWebsocketMap[identifier].on("open", () => {
require_Broker.fileLog(`Current pid: ${process.pid} remote module: ${name} connected to ws://${ip}:${require_Broker.Broker.DEFAULT_WEB_SOCKET_PORT}, starting service...`, require_Action.MF_SERVER_IDENTIFIER, "info");
const addSubscriber = new AddSubscriberAction({
name: this._name,
ip: this._ip,
publishers: [{
name,
ip
}]
});
this._subscriberWebsocketMap[identifier].send(JSON.stringify(addSubscriber));
});
this._subscriberWebsocketMap[identifier].on("message", async (message) => {
try {
const parsedMessage = JSON.parse(message.toString());
if (parsedMessage.type === "Log") {
if (parsedMessage.kind === require_Broker.LogKind.BrokerExitLog) {
require_Broker.fileLog(`${identifier}'s Server exit, thus ${identifier} will no longer has reload ability.`, require_Action.MF_SERVER_IDENTIFIER, "warn");
this._exit();
}
}
if (parsedMessage.type === "API") {
if (parsedMessage.kind === require_Broker.APIKind.UPDATE_SUBSCRIBER) {
const { payload: { updateKind, updateSourcePaths, name: subscribeName, remoteTypeTarPath, updateMode } } = parsedMessage;
await this._updateSubscriber({
remoteTypeTarPath,
name: subscribeName,
updateKind,
updateMode,
updateSourcePaths
});
}
}
} catch (err) {
console.error(err);
const exitSubscriber = new ExitSubscriberAction({
name: this._name,
ip: this._ip,
publishers: [{
name,
ip
}]
});
this._subscriberWebsocketMap[identifier].send(JSON.stringify(exitSubscriber));
require_Broker.fileLog(`${identifier} exit,
error: ${err instanceof Error ? err.toString() : JSON.stringify(err)}
`, require_Action.MF_SERVER_IDENTIFIER, "warn");
}
});
this._subscriberWebsocketMap[identifier].on("close", (code) => {
require_Broker.fileLog(`Connection closed with code ${code}.`, require_Action.MF_SERVER_IDENTIFIER, "warn");
this._subscriberWebsocketMap[identifier]?.close();
delete this._subscriberWebsocketMap[identifier];
});
this._subscriberWebsocketMap[identifier].on("error", (err) => {
if ("code" in err && err.code === "ETIMEDOUT") require_Broker.fileLog(`Can not connect ${JSON.stringify(remote)}, please make sure this remote is started locally.`, require_Action.MF_SERVER_IDENTIFIER, "warn");
else console.error(err);
this._subscriberWebsocketMap[identifier]?.close();
delete this._subscriberWebsocketMap[identifier];
});
}
_connectSubscribers() {
this._remotes.forEach((remote) => {
this._connectSubscriberToServer(remote);
});
}
async _updateSubscriber(options) {
const { updateMode, updateKind, updateSourcePaths, name, remoteTypeTarPath, remoteInfo } = options;
require_Broker.fileLog(`[_updateSubscriber] run, options: ${JSON.stringify(options, null, 2)}`, require_Action.MF_SERVER_IDENTIFIER, "warn");
if (updateMode === require_Action.UpdateMode.PASSIVE && updateSourcePaths.includes(this._name)) {
require_Broker.fileLog(`[_updateSubscriber] run, updateSourcePaths:${updateSourcePaths} includes ${this._name}, update ignore!`, require_Action.MF_SERVER_IDENTIFIER, "warn");
return;
}
if (updateSourcePaths.slice(-1)[0] === this._name) {
require_Broker.fileLog(`[_updateSubscriber] run, updateSourcePaths:${updateSourcePaths} ends is ${this._name}, update ignore!`, require_Action.MF_SERVER_IDENTIFIER, "warn");
return;
}
require_Broker.fileLog(`[_updateSubscriber] run, updateSourcePaths:${updateSourcePaths}, current module:${this._name}, update start...`, require_Action.MF_SERVER_IDENTIFIER, "info");
await this._updateCallback({
name,
updateMode,
updateKind,
updateSourcePaths,
remoteTypeTarPath,
remoteInfo
});
const newUpdateSourcePaths = updateSourcePaths.concat(this._name);
const updatePublisher = new UpdatePublisherAction({
name: this._name,
ip: this._ip,
updateMode: require_Action.UpdateMode.PASSIVE,
updateKind,
updateSourcePaths: newUpdateSourcePaths,
remoteTypeTarPath: this._remoteTypeTarPath
});
require_Broker.fileLog(`[_updateSubscriber] run, updateSourcePaths:${newUpdateSourcePaths}, update publisher ${this._name} start...`, require_Action.MF_SERVER_IDENTIFIER, "info");
this._publishWebSocket?.send(JSON.stringify(updatePublisher));
}
_tryCreateBackgroundBroker(err) {
if (!((err?.code === "ECONNREFUSED" || err?.code === "ETIMEDOUT") && err.port === require_Broker.Broker.DEFAULT_WEB_SOCKET_PORT)) {
require_Broker.fileLog(`websocket error: ${err.stack}`, require_Action.MF_SERVER_IDENTIFIER, "fatal");
return;
}
require_Broker.fileLog(`Failed to connect to ws://${this._ip}:${require_Broker.Broker.DEFAULT_WEB_SOCKET_PORT}...`, require_Action.MF_SERVER_IDENTIFIER, "fatal");
this._isReconnecting = true;
setTimeout(() => {
this._isReconnecting = false;
if (this._reconnect === false) return;
require_Broker.fileLog("Creating new background broker...", require_Action.MF_SERVER_IDENTIFIER, "warn");
createBroker().on("message", (message) => {
if (message === "ready") {
require_Broker.fileLog("background broker started.", require_Action.MF_SERVER_IDENTIFIER, "info");
this._reconnectTimes = 1;
if (process.send) process.send("ready");
}
});
}, Math.ceil(100 * Math.random()));
}
_stopWhenSIGTERMOrSIGINT() {
process.on("SIGTERM", () => {
require_Broker.fileLog(`Process(${process.pid}) SIGTERM, ModuleFederationDevServer will exit...`, require_Action.MF_SERVER_IDENTIFIER, "warn");
this._exit();
});
process.on("SIGINT", () => {
require_Broker.fileLog(`Process(${process.pid}) SIGINT, ModuleFederationDevServer will exit...`, require_Action.MF_SERVER_IDENTIFIER, "warn");
this._exit();
});
}
_handleUnexpectedExit() {
process.on("unhandledRejection", (error) => {
if (this._isReconnecting) return;
console.error("Unhandled Rejection Error: ", error);
require_Broker.fileLog(`Process(${process.pid}) unhandledRejection, garfishModuleServer will exit...`, require_Action.MF_SERVER_IDENTIFIER, "error");
this._exit();
});
process.on("uncaughtException", (error) => {
if (this._isReconnecting) return;
console.error("Unhandled Exception Error: ", error);
require_Broker.fileLog(`Process(${process.pid}) uncaughtException, garfishModuleServer will exit...`, require_Action.MF_SERVER_IDENTIFIER, "error");
this._exit();
});
}
_exit() {
this._reconnect = false;
if (this._publishWebSocket) {
const exitPublisher = new ExitPublisherAction({
name: this._name,
ip: this._ip
});
this._publishWebSocket.send(JSON.stringify(exitPublisher));
this._publishWebSocket.on("message", (message) => {
const parsedMessage = JSON.parse(message.toString());
require_Broker.fileLog(`[${parsedMessage.kind}]: ${JSON.stringify(parsedMessage)}`, require_Action.MF_SERVER_IDENTIFIER, "info");
});
}
if (this._publishWebSocket) {
this._publishWebSocket.close();
this._publishWebSocket = null;
}
process.exit(0);
}
exit() {
this._exit();
}
update(options) {
if (!this._publishWebSocket || !this._isConnected) return;
const { updateKind, updateMode, updateSourcePaths, clientName } = options;
require_Broker.fileLog(`update run, ${this._name} module update, updateKind: ${updateKind}, updateMode: ${updateMode}, updateSourcePaths: ${updateSourcePaths}`, require_Action.MF_SERVER_IDENTIFIER, "info");
if (updateKind === require_Broker.UpdateKind.RELOAD_PAGE) {
const notifyWebClient = new NotifyWebClientAction({
name: clientName || this._name,
updateMode
});
this._publishWebSocket.send(JSON.stringify(notifyWebClient));
return;
}
const updatePublisher = new UpdatePublisherAction({
name: this._name,
ip: this._ip,
updateMode,
updateKind,
updateSourcePaths: [this._name],
remoteTypeTarPath: this._remoteTypeTarPath
});
this._publishWebSocket.send(JSON.stringify(updatePublisher));
}
async fetchDynamicRemoteTypes(options) {
const { remoteInfo, once } = options;
const updateMode = require_Action.UpdateMode.PASSIVE;
const updateKind = require_Broker.UpdateKind.UPDATE_TYPE;
require_Broker.fileLog(`fetchDynamicRemoteTypes: remoteInfo: ${JSON.stringify(remoteInfo)}`, require_Action.MF_SERVER_IDENTIFIER, "info");
await this._updateCallback({
name: this._name,
updateMode,
updateKind,
updateSourcePaths: [],
remoteTypeTarPath: "",
remoteInfo,
once
});
const updatePublisher = new UpdatePublisherAction({
name: this._name,
ip: this._ip,
updateMode,
updateKind,
updateSourcePaths: [this._name],
remoteTypeTarPath: this._remoteTypeTarPath
});
this._publishWebSocket.send(JSON.stringify(updatePublisher));
}
};
//#endregion
//#region src/server/createHttpServer.ts
async function createHttpServer(options) {
const { typeTarPath } = options;
const freeport = await require_Broker.getFreePort();
const server = http.default.createServer((req, res) => {
if ((req.url?.split("?")[0] ?? "/") === `/${require_Action.DEFAULT_TAR_NAME}`) {
res.statusCode = 200;
res.setHeader("Content-Type", "application/x-gzip");
if (req.method === "HEAD") {
res.end();
return;
}
const stream = fs.default.createReadStream(typeTarPath);
stream.on("error", () => {
if (!res.headersSent) res.statusCode = 500;
res.end();
});
res.on("close", () => {
stream.destroy();
});
stream.pipe(res);
return;
}
res.statusCode = 404;
res.end();
});
server.listen(freeport);
return {
server,
serverAddress: `http://${require_Broker.getIPV4()}:${freeport}`
};
}
//#endregion
//#region src/core/lib/typeScriptCompiler.ts
const STARTS_WITH_SLASH = /^\//;
const DEFINITION_FILE_EXTENSION = ".d.ts";
const retrieveMfTypesPath = (tsConfig, remoteOptions) => (0, path.normalize)(tsConfig.compilerOptions.outDir.replace(remoteOptions.compiledTypesFolder, ""));
const retrieveOriginalOutDir = (tsConfig, remoteOptions) => (0, path.normalize)(tsConfig.compilerOptions.outDir.replace(remoteOptions.compiledTypesFolder, "").replace(remoteOptions.typesFolder, ""));
const retrieveMfAPITypesPath = (tsConfig, remoteOptions) => (0, path.join)(retrieveOriginalOutDir(tsConfig, remoteOptions), `${remoteOptions.typesFolder}.d.ts`);
function writeTempTsConfig(tsConfig, context, name, cwd) {
const createHash = (contents) => {
return crypto.default.createHash("md5").update(contents).digest("hex");
};
const hash = createHash(`${JSON.stringify(tsConfig)}${name}${Date.now()}`);
const tempTsConfigJsonPath = (0, path.resolve)(cwd ?? context, "node_modules", _module_federation_sdk.TEMP_DIR, `tsconfig.${hash}.json`);
(0, fs.mkdirSync)((0, path.dirname)(tempTsConfigJsonPath), { recursive: true });
(0, fs.writeFileSync)(tempTsConfigJsonPath, JSON.stringify(tsConfig, null, 2));
return tempTsConfigJsonPath;
}
const removeExt = (f) => {
const vueExt = ".vue";
const ext = (0, path.extname)(f);
if (ext === vueExt) return f;
const regexPattern = new RegExp(`\\${ext}$`);
return f.replace(regexPattern, "");
};
function getExposeKey(options) {
const { filePath, rootDir, outDir, mapExposeToEntry } = options;
return mapExposeToEntry[(0, path.relative)(outDir, filePath.replace(new RegExp(`\\.d.ts$`), ""))];
}
const processTypesFile = async (options) => {
const { outDir, filePath, rootDir, cb, mapExposeToEntry, mfTypePath } = options;
if (!(0, fs.existsSync)(filePath)) return;
if ((await (0, fs_promises.stat)(filePath)).isDirectory()) {
const files = await (0, fs_promises.readdir)(filePath);
await Promise.all(files.map((file) => processTypesFile({
...options,
filePath: (0, path.join)(filePath, file)
})));
} else if (filePath.endsWith(".d.ts")) {
const exposeKey = getExposeKey({
filePath,
rootDir,
outDir,
mapExposeToEntry
});
if (exposeKey) {
const mfeTypeEntry = (0, path.join)(mfTypePath, `${exposeKey === "." ? "index" : exposeKey}${DEFINITION_FILE_EXTENSION}`);
const mfeTypeEntryDirectory = (0, path.dirname)(mfeTypeEntry);
const relativePathToOutput = (0, path.relative)(mfeTypeEntryDirectory, filePath).replace(DEFINITION_FILE_EXTENSION, "").replace(STARTS_WITH_SLASH, "").split(path.sep).join("/");
(0, fs.mkdirSync)(mfeTypeEntryDirectory, { recursive: true });
await (0, fs_promises.writeFile)(mfeTypeEntry, `export * from './${relativePathToOutput}';\nexport { default } from './${relativePathToOutput}';`);
}
cb(await (0, fs_promises.readFile)(filePath, "utf8"));
}
};
const getPMFromUserAgent = () => {
const userAgent = process.env["npm_config_user_agent"];
if (userAgent == null) return "null";
return userAgent.split("/")[0];
};
const resolvePackageManagerExecutable = () => {
switch (getPMFromUserAgent()) {
case "yarn": return "yarn";
default: return "npx";
}
};
const splitCommandArgs = (value) => {
const args = [];
let current = "";
let quote = null;
let escaped = false;
for (const char of value) {
if (escaped) {
current += char;
escaped = false;
continue;
}
if (char === "\\") {
escaped = true;
continue;
}
if (quote) {
if (char === quote) quote = null;
else current += char;
continue;
}
if (char === "\"" || char === "'") {
quote = char;
continue;
}
if (char.trim() === "") {
if (current) {
args.push(current);
current = "";
}
continue;
}
current += char;
}
if (current) args.push(current);
return args;
};
const formatCommandForDisplay = (executable, args) => {
const formatArg = (arg) => {
if (/[\s'"]/.test(arg)) return JSON.stringify(arg);
return arg;
};
return [executable, ...args].map(formatArg).join(" ");
};
const compileTs = async (mapComponentsToExpose, tsConfig, remoteOptions) => {
if (!Object.keys(mapComponentsToExpose).length) return;
const { compilerOptions } = tsConfig;
const tempTsConfigJsonPath = writeTempTsConfig(tsConfig, remoteOptions.context, remoteOptions.moduleFederationConfig.name || "mf", typeof remoteOptions.moduleFederationConfig.dts !== "boolean" ? remoteOptions.moduleFederationConfig.dts?.cwd ?? void 0 : void 0);
require_Broker.logger.debug(`tempTsConfigJsonPath: ${tempTsConfigJsonPath}`);
try {
const mfTypePath = retrieveMfTypesPath(tsConfig, remoteOptions);
const thirdPartyExtractor = new _module_federation_third_party_dts_extractor.ThirdPartyExtractor({
destDir: (0, path.resolve)(mfTypePath, "node_modules"),
context: remoteOptions.context,
exclude: typeof remoteOptions.extractThirdParty === "object" ? remoteOptions.extractThirdParty.exclude : void 0
});
const execPromise = util.default.promisify(child_process.execFile);
const pmExecutable = resolvePackageManagerExecutable();
const compilerArgs = splitCommandArgs(remoteOptions.compilerInstance);
const cmdArgs = [
...compilerArgs.length > 0 ? compilerArgs : [remoteOptions.compilerInstance],
"--project",
tempTsConfigJsonPath
];
const cmd = formatCommandForDisplay(pmExecutable, cmdArgs);
try {
await execPromise(pmExecutable, cmdArgs, {
cwd: typeof remoteOptions.moduleFederationConfig.dts !== "boolean" ? remoteOptions.moduleFederationConfig.dts?.cwd ?? void 0 : void 0,
shell: process.platform === "win32"
});
} catch (err) {
if (compilerOptions.tsBuildInfoFile) try {
await (0, fs_promises.rm)(compilerOptions.tsBuildInfoFile);
} catch (e) {}
(0, _module_federation_error_codes_node.logAndReport)(_module_federation_error_codes.TYPE_001, _module_federation_error_codes.typeDescMap, { cmd }, (msg) => {
throw new Error(msg);
}, void 0);
}
const mapExposeToEntry = Object.fromEntries(Object.entries(mapComponentsToExpose).map(([exposed, filename]) => {
const normalizedFileName = (0, path.normalize)(filename);
let relativeFileName = "";
if ((0, path.isAbsolute)(normalizedFileName)) relativeFileName = (0, path.relative)(tsConfig.compilerOptions.rootDir, normalizedFileName);
else relativeFileName = (0, path.relative)(tsConfig.compilerOptions.rootDir, (0, path.resolve)(remoteOptions.context, normalizedFileName));
return [removeExt(relativeFileName), exposed];
}));
const cb = remoteOptions.extractThirdParty ? thirdPartyExtractor.collectPkgs.bind(thirdPartyExtractor) : () => void 0;
await processTypesFile({
outDir: compilerOptions.outDir,
filePath: compilerOptions.outDir,
rootDir: compilerOptions.rootDir,
mfTypePath,
cb,
mapExposeToEntry
});
if (remoteOptions.extractThirdParty) await thirdPartyExtractor.copyDts();
if (remoteOptions.deleteTsConfig) await (0, fs_promises.rm)(tempTsConfigJsonPath);
} catch (err) {
throw err;
}
};
//#endregion
//#region src/core/lib/archiveHandler.ts
const retrieveTypesZipPath = (mfTypesPath, remoteOptions) => (0, path.join)(mfTypesPath.replace(remoteOptions.typesFolder, ""), `${remoteOptions.typesFolder}.zip`);
const createTypesArchive = async (tsConfig, remoteOptions) => {
const mfTypesPath = retrieveMfTypesPath(tsConfig, remoteOptions);
const zip = new adm_zip.default();
zip.addLocalFolder(mfTypesPath);
return zip.writeZipPromise(retrieveTypesZipPath(mfTypesPath, remoteOptions));
};
const downloadErrorLogger = (destinationFolder, fileToDownload) => (reason) => {
throw {
...reason,
message: `Network error: Unable to download federated mocks for '${destinationFolder}' from '${fileToDownload}' because '${reason.message}'`
};
};
const retrieveTypesArchiveDestinationPath = (hostOptions, destinationFolder) => {
return (0, path.resolve)(hostOptions.context, hostOptions.typesFolder, destinationFolder);
};
const downloadTypesArchive = (hostOptions) => {
let retries = 0;
return async ([destinationFolder, fileToDownload]) => {
const destinationPath = retrieveTypesArchiveDestinationPath(hostOptions, destinationFolder);
while (retries++ < hostOptions.maxRetries) try {
const url = new URL(fileToDownload).href;
const response = await nativeFetch(url, {
responseType: "arraybuffer",
timeout: hostOptions.timeout,
family: hostOptions.family
}).catch(downloadErrorLogger(destinationFolder, url));
if (typeof response.headers?.["content-type"] === "string" && response.headers["content-type"].includes("text/html")) throw new Error(`${url} receives invalid content-type: ${response.headers["content-type"]}`);
try {
if (hostOptions.deleteTypesFolder) await (0, fs_promises.rm)(destinationPath, {
recursive: true,
force: true
});
} catch (error) {
require_Broker.fileLog(`Unable to remove types folder, ${error}`, "downloadTypesArchive", "error");
}
new adm_zip.default(Buffer.from(response.data)).extractAllTo(destinationPath, true);
require_Broker.fileLog(`zip.extractAllTo success destinationPath: ${destinationPath}; url: ${url}`, "downloadTypesArchive", "info");
return [destinationFolder, destinationPath];
} catch (error) {
require_Broker.fileLog(`Error during types archive download: ${error?.message || "unknown error"}`, "downloadTypesArchive", "error");
if (retries >= hostOptions.maxRetries) {
require_Broker.logger.error(`Failed to download types archive from "${fileToDownload}". Set FEDERATION_DEBUG=true for details.`);
if (hostOptions.abortOnError !== false) throw error;
return;
}
}
};
};
//#endregion
//#region src/core/configurations/hostPlugin.ts
const defaultOptions$1 = {
typesFolder: "@mf-types",
remoteTypesFolder: "@mf-types",
deleteTypesFolder: true,
maxRetries: 3,
implementation: "",
context: process.cwd(),
abortOnError: true,
consumeAPITypes: false,
runtimePkgs: [],
remoteTypeUrls: {},
timeout: 6e4,
typesOnBuild: false,
family: 0
};
const buildZipUrl = (hostOptions, url) => {
const remoteUrl = new URL(url, "file:");
remoteUrl.pathname = `${remoteUrl.pathname.split("/").slice(0, -1).join("/")}/${hostOptions.remoteTypesFolder}.zip`;
return remoteUrl.protocol === "file:" ? remoteUrl.pathname : remoteUrl.href;
};
const buildApiTypeUrl = (zipUrl) => {
if (!zipUrl) return;
return zipUrl.replace(".zip", ".d.ts");
};
const retrieveRemoteInfo = (options) => {
const { hostOptions, remoteAlias, remote } = options;
const { remoteTypeUrls } = hostOptions;
let decodedRemote = remote;
if (decodedRemote.startsWith(_module_federation_sdk.ENCODE_NAME_PREFIX)) decodedRemote = (0, _module_federation_sdk.decodeName)(decodedRemote, _module_federation_sdk.ENCODE_NAME_PREFIX);
const parsedInfo = (0, _module_federation_sdk.parseEntry)(decodedRemote, void 0, "@");
const url = "entry" in parsedInfo ? parsedInfo.entry : parsedInfo.name === decodedRemote ? decodedRemote : "";
let zipUrl = "";
let apiTypeUrl = "";
const name = parsedInfo.name || remoteAlias;
const remoteTypeUrl = typeof remoteTypeUrls === "object" && remoteTypeUrls[name];
if (remoteTypeUrl) {
zipUrl = remoteTypeUrl.zip;
apiTypeUrl = remoteTypeUrl.api;
}
const shouldResolveTypeUrlsByConvention = Boolean(url && !url.includes(_module_federation_sdk.MANIFEST_EXT));
if (!zipUrl && shouldResolveTypeUrlsByConvention) zipUrl = buildZipUrl(hostOptions, url);
if (!apiTypeUrl && zipUrl && (remoteTypeUrl || shouldResolveTypeUrlsByConvention)) apiTypeUrl = buildApiTypeUrl(zipUrl);
return {
name,
url,
zipUrl,
apiTypeUrl,
alias: remoteAlias
};
};
const resolveRemotes = (hostOptions) => {
const parsedOptions = _module_federation_managers.utils.parseOptions(hostOptions.moduleFederationConfig.remotes || {}, (item, key) => ({
remote: Array.isArray(item) ? item[0] : item,
key
}), (item, key) => ({
remote: Array.isArray(item.external) ? item.external[0] : item.external,
key
}));
const remoteTypeUrls = hostOptions.remoteTypeUrls ?? {};
if (typeof remoteTypeUrls !== "object") throw new Error("remoteTypeUrls must be consumed before resolveRemotes");
const remoteInfos = Object.keys(remoteTypeUrls).reduce((sum, remoteName) => {
const { zip, api, alias } = remoteTypeUrls[remoteName];
sum[alias] = {
name: remoteName,
url: "",
zipUrl: zip,
apiTypeUrl: api,
alias: alias || remoteName
};
return sum;
}, {});
return parsedOptions.reduce((accumulator, item) => {
const { key, remote } = item[1];
const res = retrieveRemoteInfo({
hostOptions,
remoteAlias: key,
remote
});
if (accumulator[key]) {
accumulator[key] = {
...accumulator[key],
url: res.url,
apiTypeUrl: accumulator[key].apiTypeUrl || res.apiTypeUrl
};
return accumulator;
}
accumulator[key] = res;
return accumulator;
}, remoteInfos);
};
const retrieveHostConfig = (options) => {
validateOptions(options);
const hostOptions = {
...defaultOptions$1,
...options
};
return {
hostOptions,
mapRemotesToDownload: resolveRemotes(hostOptions)
};
};
//#endregion
//#region src/core/constant.ts
const REMOTE_ALIAS_IDENTIFIER = "REMOTE_ALIAS_IDENTIFIER";
const REMOTE_API_TYPES_FILE_NAME = "apis.d.ts";
const HOST_API_TYPES_FILE_NAME = "index.d.ts";
//#endregion
//#region src/core/lib/DTSManager.ts
var DTSManager = class {
constructor(options) {
this.options = cloneDeepOptions(options);
this.runtimePkgs = [
"@module-federation/runtime",
"@module-federation/enhanced/runtime",
"@module-federation/runtime-tools"
];
this.loadedRemoteAPIAlias = /* @__PURE__ */ new Set();
this.remoteAliasMap = {};
this.extraOptions = options?.extraOptions || {};
this.updatedRemoteInfos = {};
}
generateAPITypes(mapComponentsToExpose) {
const exposePaths = /* @__PURE__ */ new Set();
const packageType = Object.keys(mapComponentsToExpose).reduce((sum, exposeKey) => {
const exposePath = path.default.join(REMOTE_ALIAS_IDENTIFIER, exposeKey).split(path.default.sep).join("/");
exposePaths.add(`'${exposePath}'`);
sum = `T extends '${exposePath}' ? typeof import('${exposePath}') :` + sum;
return sum;
}, "any;");
return `
export type RemoteKeys = ${[...exposePaths].join(" | ")};
type PackageType<T> = ${packageType}`;
}
async extractRemoteTypes(options) {
const { remoteOptions, tsConfig } = options;
if (!remoteOptions.extractRemoteTypes) return;
let hasRemotes = false;
const remotes = remoteOptions.moduleFederationConfig.remotes;
if (remotes) {
if (Array.isArray(remotes)) hasRemotes = Boolean(remotes.length);
else if (typeof remotes === "object") hasRemotes = Boolean(Object.keys(remotes).length);
}
const mfTypesPath = retrieveMfTypesPath(tsConfig, remoteOptions);
if (hasRemotes && this.options.host) try {
const { hostOptions } = retrieveHostConfig(this.options.host);
const remoteTypesFolder = path.default.resolve(hostOptions.context, hostOptions.typesFolder);
const targetDir = path.default.join(mfTypesPath, "node_modules");
if (fs.default.existsSync(remoteTypesFolder)) {
const targetFolder = path.default.resolve(remoteOptions.context, targetDir);
await (0, fs_promises.mkdir)(targetFolder, { recursive: true });
await (0, fs_promises.cp)(remoteTypesFolder, targetFolder, {
recursive: true,
force: true
});
}
} catch (err) {
if (this.options.host?.abortOnError === false) require_Broker.fileLog(`Unable to copy remote types, ${err}`, "extractRemoteTypes", "error");
else throw err;
}
}
async generateTypes() {
try {
const { options } = this;
if (!options.remote) throw new Error("options.remote is required if you want to generateTypes");
const { remoteOptions, tsConfig, mapComponentsToExpose } = retrieveRemoteConfig(options.remote);
if (!Object.keys(mapComponentsToExpose).length) return;
if (!tsConfig.files?.length) {
require_Broker.logger.info("No type files to compile, skip");
return;
}
if (tsConfig.compilerOptions.tsBuildInfoFile) try {
const tsBuildInfoFile = path.default.resolve(remoteOptions.context, tsConfig.compilerOptions.tsBuildInfoFile);
const mfTypesPath = retrieveMfTypesPath(tsConfig, remoteOptions);
if (!fs.default.existsSync(mfTypesPath)) fs.default.rmSync(tsBuildInfoFile, { force: true });
} catch (e) {}
await this.extractRemoteTypes({
remoteOptions,
tsConfig,
mapComponentsToExpose
});
await compileTs(mapComponentsToExpose, tsConfig, remoteOptions);
await createTypesArchive(tsConfig, remoteOptions);
let apiTypesPath = "";
if (remoteOptions.generateAPITypes) {
const apiTypes = this.generateAPITypes(mapComponentsToExpose);
apiTypesPath = retrieveMfAPITypesPath(tsConfig, remoteOptions);
fs.default.writeFileSync(apiTypesPath, apiTypes);
}
try {
if (remoteOptions.deleteTypesFolder) await (0, fs_promises.rm)(retrieveMfTypesPath(tsConfig, remoteOptions), {
recursive: true,
force: true
});
} catch (err) {
if (isDebugMode()) console.error(err);
}
require_Broker.logger.success("Federated types created correctly");
} catch (error) {
if (this.options.remote?.abortOnError === false) {
if (this.options.displayErrorInTerminal) require_Broker.logger.error(error);
} else throw error;
}
}
async requestRemoteManifest(remoteInfo, hostOptions) {
try {
if (!remoteInfo.url.includes(_module_federation_sdk.MANIFEST_EXT)) return remoteInfo;
if (remoteInfo.zipUrl) return remoteInfo;
const url = remoteInfo.url;
const manifestJson = (await nativeFetch(url, {
timeout: hostOptions.timeout,
family: hostOptions.family
})).data;
if (!manifestJson.metaData.types.zip) throw new Error(`Can not get ${remoteInfo.name}'s types archive url!`);
const addProtocol = (u) => {
if (u.startsWith("//")) return `https:${u}`;
return u;
};
let publicPath;
if ("publicPath" in manifestJson.metaData) publicPath = manifestJson.metaData.publicPath;
else {
const getPublicPath = new Function(manifestJson.metaData.getPublicPath);
if (manifestJson.metaData.getPublicPath.startsWith("function")) publicPath = getPublicPath()();
else publicPath = getPublicPath();
}
if (publicPath === "auto") publicPath = (0, _module_federation_sdk.inferAutoPublicPath)(remoteInfo.url);
const normalizedPublicPath = addProtocol(publicPath).endsWith("/") ? addProtocol(publicPath) : `${addProtocol(publicPath)}/`;
remoteInfo.zipUrl = new URL(manifestJson.metaData.types.zip, normalizedPublicPath).href;
if (!manifestJson.metaData.types.api) {
console.warn(`Can not get ${remoteInfo.name}'s api types url!`);
remoteInfo.apiTypeUrl = "";
return remoteInfo;
}
remoteInfo.apiTypeUrl = new URL(manifestJson.metaData.types.api, normalizedPublicPath).href;
return remoteInfo;
} catch (_err) {
require_Broker.fileLog(`fetch manifest failed, ${_err}, ${remoteInfo.name} will be ignored`, "requestRemoteManifest", "error");
return remoteInfo;
}
}
async consumeTargetRemotes(hostOptions, remoteInfo) {
if (!remoteInfo.zipUrl) throw new Error(`Can not get ${remoteInfo.name}'s types archive url!`);
return downloadTypesArchive(hostOptions)([remoteInfo.alias, remoteInfo.zipUrl]);
}
async downloadAPITypes(remoteInfo, destinationPath, hostOptions) {
const { apiTypeUrl } = remoteInfo;
if (!apiTypeUrl) return;
try {
let apiTypeFile = (await nativeFetch(apiTypeUrl, {
timeout: hostOptions.timeout,
family: hostOptions.family
})).data;
apiTypeFile = apiTypeFile.replaceAll(REMOTE_ALIAS_IDENTIFIER, remoteInfo.alias);
const filePath = path.default.join(destinationPath, REMOTE_API_TYPES_FILE_NAME);
fs.default.writeFileSync(filePath, apiTypeFile);
const existed = this.loadedRemoteAPIAlias.has(remoteInfo.alias);
this.loadedRemoteAPIAlias.add(remoteInfo.alias);
require_Broker.fileLog(`success`, "downloadAPITypes", "info");
return existed;
} catch (err) {
require_Broker.fileLog(`Unable to download "${remoteInfo.name}" api types, ${err}`, "downloadAPITypes", "error");
}
}
consumeAPITypes(hostOptions) {
const apiTypeFileName = path.default.join(hostOptions.context, hostOptions.typesFolder, HOST_API_TYPES_FILE_NAME);
try {
const existedFile = fs.default.readFileSync(apiTypeFileName, "utf-8");
new _module_federation_third_party_dts_extractor.ThirdPartyExtractor({ destDir: "" }).collectTypeImports(existedFile).forEach((existedImport) => {
const alias = existedImport.split("./").slice(1).join("./").replace("/apis.d.ts", "");
this.loadedRemoteAPIAlias.add(alias);
});
} catch (err) {}
if (!this.loadedRemoteAPIAlias.size) return;
const packageTypes = [];
const remoteKeys = [];
const importTypeStr = [...this.loadedRemoteAPIAlias].sort().map((alias, index) => {
const remoteKey = `RemoteKeys_${index}`;
const packageType = `PackageType_${index}`;
packageTypes.push(`T extends ${remoteKey} ? ${packageType}<T>`);
remoteKeys.push(remoteKey);
return `import type { PackageType as ${packageType},RemoteKeys as ${remoteKey} } from './${alias}/apis.d.ts';`;
}).join("\n");
const remoteKeysStr = `type RemoteKeys = ${remoteKeys.join(" | ")};`;
const packageTypesStr = `type PackageType<T, Y=any> = ${[...packageTypes, "Y"].join(" :\n")} ;`;
const runtimePkgs = /* @__PURE__ */ new Set();
[...this.runtimePkgs, ...hostOptions.runtimePkgs].forEach((pkg) => {
runtimePkgs.add(pkg);
});
const fileStr = `${importTypeStr}
${[...runtimePkgs].map((pkg) => {
return `declare module "${pkg}" {
${remoteKeysStr}
${packageTypesStr}
export function loadRemote<T extends RemoteKeys,Y>(packageName: T): Promise<PackageType<T, Y>>;
export function loadRemote<T extends string,Y>(packageName: T): Promise<PackageType<T, Y>>;
}`;
}).join("\n")}
`;
fs.default.writeFileSync(path.default.join(hostOptions.context, hostOptions.typesFolder, HOST_API_TYPES_FILE_NAME), fileStr);
}
async consumeArchiveTypes(options) {
const { hostOptions, mapRemotesToDownload } = retrieveHostConfig(options);
const downloadPromises = Object.entries(mapRemotesToDownload).map(async (item) => {
const remoteInfo = item[1];
if (!this.remoteAliasMap[remoteInfo.alias]) {
const requiredRemoteInfo = await this.requestRemoteManifest(remoteInfo, hostOptions);
this.remoteAliasMap[remoteInfo.alias] = requiredRemoteInfo;
}
return this.consumeTargetRemotes(hostOptions, this.remoteAliasMap[remoteInfo.alias]);
});
return {
hostOptions,
downloadPromisesResult: await Promise.allSettled(downloadPromises)
};
}
async consumeTypes() {
try {
const { options } = this;
if (!options.host) throw new Error("options.host is required if you want to consumeTypes");
const { mapRemotesToDownload } = retrieveHostConfig(options.host);
if (!Object.keys(mapRemotesToDownload).length) return;
const { downloadPromisesResult, hostOptions } = await this.consumeArchiveTypes(options.host);
if (hostOptions.consumeAPITypes) {
await Promise.all(downloadPromisesResult.map(async (item) => {
if (item.status === "rejected" || !item.value) return;
const [alias, destinationPath] = item.value;
const remoteInfo = this.remoteAliasMap[alias];
if (!remoteInfo) return;
await this.downloadAPITypes(remoteInfo, destinationPath, hostOptions);
}));
this.consumeAPITypes(hostOptions);
}
require_Broker.logger.success("Federated types extraction completed");
} catch (err) {
if (this.options.host?.abortOnError === false) require_Broker.fileLog(`Unable to consume federated types, ${err}`, "consumeTypes", "error");
else throw err;
}
}
async updateTypes(options) {
try {
const { remoteName, updateMode, remoteTarPath, remoteInfo: updatedRemoteInfo, once } = options;
const hostName = this.options?.host?.moduleFederationConfig?.name;
require_Broker.fileLog(`options: ${JSON.stringify(options, null, 2)};\nhostName: ${hostName}`, "updateTypes", "info");
if (updateMode === require_Action.UpdateMode.POSITIVE && remoteName === hostName) {
if (!this.options.remote) return;
await this.generateTypes();
} else {
const { remoteAliasMap } = this;
if (!this.options.host) return;
const { hostOptions, mapRemotesToDownload } = retrieveHostConfig(this.options.host);
const loadedRemoteInfo = Object.values(remoteAliasMap).find((i) => i.name === remoteName);
const consumeTypes = async (requiredRemoteInfo) => {
require_Broker.fileLog(`consumeTypes start`, "updateTypes", "info");
if (!requiredRemoteInfo.zipUrl) throw new Error(`Can not get ${requiredRemoteInfo.name}'s types archive url!`);
const [_alias, destinationPath] = await this.consumeTargetRemotes(hostOptions, {
...requiredRemoteInfo,
zipUrl: remoteTarPath || requiredRemoteInfo.zipUrl
});
if (await this.downloadAPITypes(requiredRemoteInfo, destinationPath, hostOptions)) this.consumeAPITypes(hostOptions);
require_Broker.fileLog(`consumeTypes end`, "updateTypes", "info");
};
require_Broker.fileLog(`loadedRemoteInfo: ${JSON.stringify(loadedRemoteInfo, null, 2)}`, "updateTypes", "info");
if (!loadedRemoteInfo) {
const remoteInfo = Object.values(mapRemotesToDownload).find((item) => {
return item.name === remoteName;
});
require_Broker.fileLog(`remoteInfo: ${JSON.stringify(remoteInfo, null, 2)}`, "updateTypes", "info");
if (remoteInfo) {
if (!this.remoteAliasMap[remoteInfo.alias]) {
const requiredRemoteInfo = await this.requestRemoteManifest(remoteInfo, hostOptions);
this.remoteAliasMap[remoteInfo.alias] = requiredRemoteInfo;
}
await consumeTypes(this.remoteAliasMap[remoteInfo.alias]);
} else if (updatedRemoteInfo) {
const consumeDynamicRemoteTypes = async () => {
await consumeTypes(this.updatedRemoteInfos[updatedRemoteInfo.name]);
};
if (!this.updatedRemoteInfos[updatedRemoteInfo.name]) {
const parsedRemoteInfo = retrieveRemoteInfo({
hostOptions,
remoteAlias: updatedRemoteInfo.alias || updatedRemoteInfo.name,
remote: updatedRemoteInfo.url
});
require_Broker.fileLog(`start request manifest`, "consumeTypes", "info");
this.updatedRemoteInfos[updatedRemoteInfo.name] = await this.requestRemoteManifest(parsedRemoteInfo, hostOptions);
require_Broker.fileLog(`end request manifest, this.updatedRemoteInfos[updatedRemoteInfo.name]: ${JSON.stringify(this.updatedRemoteInfos[updatedRemoteInfo.name], null, 2)}`, "updateTypes", "info");
await consumeDynamicRemoteTypes();
}
if (!once && this.updatedRemoteInfos[updatedRemoteInfo.name]) await consumeDynamicRemoteTypes();
}
} else await consumeTypes(loadedRemoteInfo);
}
} catch (err) {
require_Broker.fileLog(`updateTypes fail, ${err}`, "updateTypes", "error");
}
}
};
//#endregion
//#region src/core/lib/utils.ts
const dispatcherCache = /* @__PURE__ */ new Map();
function getDTSManagerConstructor(implementation) {
if (implementation) {
const NewConstructor = require(implementation);
return NewConstructor.default ? NewConstructor.default : NewConstructor;
}
return DTSManager;
}
const validateOptions = (options) => {
if (!options.moduleFederationConfig) throw new Error("moduleFederationConfig is required");
};
function retrieveTypesAssetsInfo(options) {
let apiTypesPath = "";
let zipTypesPath = "";
try {
const { tsConfig, remoteOptions, mapComponentsToExpose } = retrieveRemoteConfig(options);
if (!Object.keys(mapComponentsToExpose).length || !tsConfig.files.length) return {
apiTypesPath,
zipTypesPath,
zipName: "",
apiFileName: ""
};
zipTypesPath = retrieveTypesZipPath(retrieveMfTypesPath(tsConfig, remoteOptions), remoteOptions);
if (remoteOptions.generateAPITypes) apiTypesPath = retrieveMfAPITypesPath(tsConfig, remoteOptions);
return {
apiTypesPath,
zipTypesPath,
zipName: path.default.basename(zipTypesPath),
apiFileName: path.default.basename(apiTypesPath)
};
} catch (err) {
console.error(ansi_colors.default.red(`Unable to compile federated types, ${err}`));
return {
apiTypesPath: "",
zipTypesPath: "",
zipName: "",
apiFileName: ""
};
}
}
function isDebugMode() {
return Boolean(process.env["FEDERATION_DEBUG"]) || process.env["NODE_ENV"] === "test";
}
const isTSProject = (dtsOptions, context = process.cwd()) => {
if (dtsOptions === false) return false;
try {
let filepath = "";
if (typeof dtsOptions === "object" && dtsOptions.tsConfigPath) filepath = dtsOptions.tsConfigPath;
else filepath = path.default.resolve(context, "./tsconfig.json");
if (!path.default.isAbsolute(filepath)) filepath = path.default.resolve(context, filepath);
return fs.default.existsSync(filepath);
} catch (err) {
return false;
}
};
function cloneDeepOptions(options) {
const excludeKeys = new Set(["manifest", "async"]);
const cache = /* @__PURE__ */ new WeakMap();
function sanitize(val, key) {
if (key !== void 0 && excludeKeys.has(key) || typeof val === "function") return false;
if (key === "extractThirdParty" && Array.isArray(val)) return val.map(String);
if (Array.isArray(val)) {
if (cache.has(val)) return cache.get(val);
const out = [];
cache.set(val, out);
val.forEach((v, i) => out.push(sanitize(v, String(i))));
return out;
}
if (val !== null && typeof val === "object" && Object.getPrototypeOf(val) === Object.prototype) {
const obj = val;
if (cache.has(obj)) return cache.get(obj);
const out = {};
cache.set(obj, out);
for (const [k, v] of Object.entries(obj)) out[k] = sanitize(v, k);
return out;
}
return val;
}
return structuredClone(sanitize(options));
}
const getEnvHeaders = () => {
const headersStr = (0, _module_federation_sdk.getProcessEnv)()["MF_ENV_HEADERS"];
if (!headersStr || headersStr === "undefined") return {};
try {
return { ...JSON.parse(headersStr) };
} catch {
return {};
}
};
const createDispatcherFromFamily = (family) => {
if (!family) return void 0;
if (dispatcherCache.has(family)) return dispatcherCache.get(family);
try {
const dispatcher = new undici.Agent({ connect: { family } });
dispatcherCache.set(family, dispatcher);
return dispatcher;
} catch {}
};
const toHeaderRecord = (headers) => {
const out = {};
headers.forEach((value, key) => {
out[key.toLowerCase()] = value;
});
return out;
};
async function nativeFetch(url, config) {
const controller = new AbortController();
const timeoutMs = config?.timeout ?? 6e4;
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
const headers = {
...getEnvHeaders(),
...config?.headers ?? {}
};
const dispatcher = config?.dispatcher ?? createDispatcherFromFamily(config?.family);
try {
const resp = await fetch(url, {
headers,
signal: controller.signal,
...dispatcher ? { dispatcher } : {},
...config?.agent ? { agent: config.agent } : {}
});
const headerRecord = toHeaderRecord(resp.headers);
if (!resp.ok) throw new Error(`Request failed with status ${resp.status}`);
if (config?.responseType === "arraybuffer") return {
data: await resp.arrayBuffer(),
headers: headerRecord,
status: resp.status
};
return {
data: (resp.headers.get("content-type") || "").includes("application/json") || url.endsWith(".json") ? await resp.json() : await resp.text(),
headers: headerRecord,
status: resp.status
};
} finally {
clearTimeout(timeoutId);
}
}
//#endregion
//#region src/core/configurations/remotePlugin.ts
const defaultOptions = {
tsConfigPath: "./tsconfig.json",
typesFolder: "@mf-types",
compiledTypesFolder: "compiled-types",
hostRemoteTypesFolder: "@mf-types",
deleteTypesFolder: true,
additionalFilesToCompile: [],
compilerInstance: "tsc",
compileInChildProcess: false,
implementation: "",
generateAPITypes: false,
context: process.cwd(),
abortOnError: true,
extractRemoteTypes: false,
extractThirdParty: false,
outputDir: "",
deleteTsConfig: true
};
function getEffectiveRootDir(parsedCommandLine) {
const compilerOptions = parsedCommandLine.options;
if (compilerOptions.rootDir) return compilerOptions.rootDir;
const files = parsedCommandLine.fileNames;
if (files.length > 0) return files.map((file) => (0, path.dirname)(file)).reduce((commonPath, fileDir) => {
while (!fileDir.startsWith(commonPath)) commonPath = (0, path.dirname)(commonPath);
return commonPath;
}, files[0]);
if (parsedCommandLine.projectReferences.length) {
const relativeReferences = parsedCommandLine.projectReferences.filter((reference) => !(0, path.isAbsolute)(reference.originalPath ?? reference.path));
const referencesForRoot = relativeReferences.length ? relativeReferences : parsedCommandLine.projectReferences;
return referencesForRoot.map((reference) => (0, path.dirname)(reference.path)).reduce((commonPath, filePath) => {
while (!filePath.startsWith(commonPath)) commonPath = (0, path.dirname)(commonPath);
return commonPath;
}, (0, path.dirname)(referencesForRoot[0].path));
}
throw new Error("Can not get effective rootDir, please set compilerOptions.rootDir !");
}
const getDependentFiles = (rootFiles, configContent, rootDir) => {
const dependentFiles = typescript.default.createProgram(rootFiles, configContent.options).getSourceFiles().map((file) => file.fileName).filter((file) => !file.endsWith(".d.ts") && file.startsWith(rootDir + "/"));
return dependentFiles.length ? dependentFiles : rootFiles;
};
const readTsConfig = ({ tsConfigPath, typesFolder, compiledTypesFolder, context, additionalFilesToCompile, outputDir }, mapComponentsToExpose) => {
const resolvedTsConfigPath = (0, path.resolve)(context, tsConfigPath);
const readResult = typescript.default.readConfigFile(resolvedTsConfigPath, typescript.default.sys.readFile);
if (readResult.error) throw new Error(readResult.error.messageText.toString());
const rawTsConfigJson = readResult.config;
const configContent = typescript.default.parseJsonConfigFileContent(rawTsConfigJson, typescript.default.sys, (0, path.dirname)(resolvedTsConfigPath));
const rootDir = getEffectiveRootDir(configContent);
const outDir = (0, path.resolve)(context, outputDir || configContent.options.outDir || "dist", typesFolder, compiledTypesFolder);
const defaultCompilerOptions = {
rootDir,
emitDeclarationOnly: true,
noEmit: false,
declaration: true,
outDir
};
rawTsConfigJson.compilerOptions = rawTsConfigJson.compilerOptions || {};
rawTsConfigJson.compilerOptions = {
incremental: true,
tsBuildInfoFile: (0, path.resolve)(context, "node_modules/.cache/mf-types/.tsbuildinfo"),
...rawTsConfigJson.compilerOptions,
...defaultCompilerOptions
};
const { paths, baseUrl, ...restCompilerOptions } = rawTsConfigJson.compilerOptions || {};
rawTsConfigJson.compilerOptions = restCompilerOptions;
const outDirWithoutTypesFolder = (0, path.resolve)(context, outputDir || configContent.options.outDir || "dist");
const excludeExtensions = [".mdx", ".md"];
const filesToCompile = [...getDependentFiles([...Object.values(mapComponentsToExpose), ...additionalFilesToCompile].filter((filename) => !excludeExtensions.some((ext) => filename.endsWith(ext))), configContent, rootDir), ...configContent.fileNames.filter((filename) => filename.endsWith(".d.ts") && !filename.startsWith(outDirWithoutTypesFolder))];
rawTsConfigJson.include = [];
rawTsConfigJson.files = [...new Set(filesToCompile)];
rawTsConfigJson.exclude = [];
"references" in rawTsConfigJson && delete rawTsConfigJson.references;
rawTsConfigJson.extends = resolvedTsConfigPath;
rawTsConfigJson.compilerOptions.declarationDir = outDir;
return rawTsConfigJson;
};
const TS_EXTENSIONS = [
".ts",
".tsx",
".vue",
".svelte",
".js",
".jsx"
];
const resolveWithExtension = (exposedPath, context) => {
const explicitExtension = (0, path.extname)(exposedPath);
if (TS_EXTENSIONS.includes(explicitExtension)) return (0, path.resolve)(context, exposedPath);
for (const extension of TS_EXTENSIONS) {
const exposedPathWithExtension = (0, path.resolve)(context, `${exposedPath}${extension}`);
if ((0, fs.existsSync)(exposedPathWithExtension)) return exposedPathWithExtension;
}
};
const resolveExposes = (remoteOptions) => {
return _module_federation_managers.utils.parseOptions(remoteOptions.moduleFederationConfig.exposes || {}, (item, key) => ({
exposePath: Array.isArray(item) ? item[0] : item,
key
}), (item, key) => ({
exposePath: Array.isArray(item.import) ? item.import[0] : item.import[0],
key
})).reduce((accumulator, item) => {
const { exposePath, key } = item[1];
accumulator[key] = resolveWithExtension(exposePath, remoteOptions.context) || resolveWithExtension((0, path.join)(exposePath, "index"), remoteOptions.context) || exposePath;
return accumulator;
}, {});
};
const retrieveRemoteConfig = (options) => {
validateOptions(options);
const remoteOptions = {
...defaultOptions,
...options
};
const mapComponentsToExpose = resolveExposes(remoteOptions);
const tsConfig = readTsConfig(remoteOptions, mapComponentsToExpose);
if (tsConfig.compilerOptions.incremental && tsConfig.compilerOptions.tsBuildInfoFile && options.deleteTypesFolder !== true) remoteOptions.deleteTypesFolder = false;
return {
tsConfig,
mapComponentsToExpose,
remoteOptions
};
};
//#endregion
//#region src/core/lib/generateTypes.ts
async function generateTypes(options) {
return new (getDTSManagerConstructor(options.remote?.implementation))(options).generateTypes();
}
//#endregion
//#region src/core/rpc/types.ts
let RpcGMCallTypes = /* @__PURE__ */ function(RpcGMCallTypes) {
RpcGMCallTypes["CALL"] = "mf_call";
RpcGMCallTypes["RESOLVE"] = "mf_resolve";
RpcGMCallTypes["REJECT"] = "mf_reject";
RpcGMCallTypes["EXIT"] = "mf_exit";
return RpcGMCallTypes;
}({});
//#endregion
//#region src/core/rpc/expose-rpc.ts
function exposeRpc(fn) {
const sendMessage = (message) => new Promise((resolve, reject) => {
if (!process$1.default.send) reject(/* @__PURE__ */ new Error(`Process ${process$1.default.pid} doesn't have IPC channels`));
else if (!process$1.default.connected) reject(/* @__PURE__ */ new Error(`Process ${process$1.default.pid} doesn't have open IPC channels`));
else process$1.default.send(message, void 0, void 0, (error) => {
if (error) reject(error);
else resolve(void 0);
});
});
const handleMessage = async (message) => {
if (message.type === RpcGMCallTypes.CALL) {
if (!process$1.default.send) return;
let value, error;
try {
value = await fn(...message.args);
} catch (fnError) {
error = fnError;
}
try {
if (error) await sendMessage({
type: RpcGMCallTypes.REJECT,
id: message.id,
error
});
else await sendMessage({
type: RpcGMCallTypes.RESOLVE,
id: message.id,
value
});
} catch (sendError) {
if (error) {
if (error instanceof Error) console.error(error);
}
console.error(sendError);
}
}
};
process$1.default.on("message", handleMessage);
}
//#endregion
Object.defineProperty(exports, 'DTSManager', {
enumerable: true,
get: function () {
return DTSManager;
}
});
Object.defineProperty(exports, 'HOST_API_TYPES_FILE_NAME', {
enumerable: true,
get: function () {
return HOST_API_TYPES_FILE_NAME;
}
});
Object.defineProperty(exports, 'ModuleFederationDevServer', {
enumerable: true,
get: function () {
return ModuleFederationDevServer;
}
});
Object.defineProperty(exports, 'REMOTE_ALIAS_IDENTIFIER', {
enumerable: true,
get: function () {
return REMOTE_ALIAS_IDENTIFIER;
}
});
Object.defineProperty(exports, 'REMOTE_API_TYPES_FILE_NAME', {
enumerable: true,
get: function () {
return REMOTE_API_TYPES_FILE_NAME;
}
});
Object.defineProperty(exports, 'RpcGMCallTypes', {
enumerable: true,
get: function () {
return RpcGMCallTypes;
}
});
Object.defineProperty(exports, 'cloneDeepOptions', {
enumerable: true,
get: function () {
return cloneDeepOptions;
}
});
Object.defineProperty(exports, 'createHttpServer', {
enumerable: true,
get: function () {
return createHttpServer;
}
});
Object.defineProperty(exports, 'exposeRpc', {
enumerable: true,
get: function () {
return exposeRpc;
}
});
Object.defineProperty(exports, 'generateTypes', {
enumerable: true,
get: function () {
return generateTypes;
}
});
Object.defineProperty(exports, 'getDTSManagerConstructor', {
enumerable: true,
get: function () {
return getDTSManagerConstructor;
}
});
Object.defineProperty(exports, 'isDebugMode', {
enumerable: true,
get: function () {
return isDebugMode;
}
});
Object.defineProperty(exports, 'isTSProject', {
enumerable: true,
get: function () {
return isTSProject;
}
});
Object.defineProperty(exports, 'retrieveHostConfig', {
enumerable: true,
get: function () {
return retrieveHostConfig;
}
});
Object.defineProperty(exports, 'retrieveMfTypesPath', {
enumerable: true,
get: function () {
return retrieveMfTypesPath;
}
});
Object.defineProperty(exports, 'retrieveOriginalOutDir', {
enumerable: true,
get: function () {
return retrieveOriginalOutDir;
}
});
Object.defineProperty(exports, 'retrieveRemoteConfig', {
enumerable: true,
get: function () {
return retrieveRemoteConfig;
}
});
Object.defineProperty(exports, 'retrieveTypesAssetsInfo', {
enumerable: true,
get: function () {
return retrieveTypesAssetsInfo;
}
});
Object.defineProperty(exports, 'retrieveTypesZipPath', {
enumerable: true,
get: function () {
return retrieveTypesZipPath;
}
});
Object.defineProperty(exports, 'validateOptions', {
enumerable: true,
get: function () {
return validateOptions;
}
});
+10
-0
# @module-federation/dts-plugin
## 2.6.0
### Patch Changes
- 2a7f724: Fix dts-plugin expose resolution for extensionless multi-dot paths like `foo.generated` so they correctly infer supported source files such as `.ts`, `.tsx`, `.vue`, `.js`, and `.jsx`, while preserving explicit extensions and directory `index` fallback behavior.
- @module-federation/sdk@2.6.0
- @module-federation/managers@2.6.0
- @module-federation/third-party-dts-extractor@2.6.0
- @module-federation/error-codes@2.6.0
## 2.5.1

@@ -4,0 +14,0 @@

+2
-2
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
require('./Broker-CaenCqdn.js');
const require_expose_rpc = require('./expose-rpc-XLxmAXIW.js');
const require_consumeTypes = require('./consumeTypes-FbF0o6AJ.js');
const require_expose_rpc = require('./expose-rpc-BNzQqY2X.js');
const require_consumeTypes = require('./consumeTypes-DuDkcp8N.js');

@@ -6,0 +6,0 @@ exports.DTSManager = require_expose_rpc.DTSManager;

@@ -1,5 +0,5 @@

import { _ as retrieveMfTypesPath, c as isTSProject, d as DTSManager, f as HOST_API_TYPES_FILE_NAME, g as retrieveTypesZipPath, h as retrieveHostConfig, i as retrieveRemoteConfig, l as retrieveTypesAssetsInfo, m as REMOTE_API_TYPES_FILE_NAME, o as getDTSManagerConstructor, p as REMOTE_ALIAS_IDENTIFIER, r as generateTypes, u as validateOptions, v as retrieveOriginalOutDir } from "./expose-rpc-ZWQxpg0y.mjs";
import { _ as retrieveMfTypesPath, c as isTSProject, d as DTSManager, f as HOST_API_TYPES_FILE_NAME, g as retrieveTypesZipPath, h as retrieveHostConfig, i as retrieveRemoteConfig, l as retrieveTypesAssetsInfo, m as REMOTE_API_TYPES_FILE_NAME, o as getDTSManagerConstructor, p as REMOTE_ALIAS_IDENTIFIER, r as generateTypes, u as validateOptions, v as retrieveOriginalOutDir } from "./expose-rpc-BiwGpqZ3.mjs";
import "./Broker-Cmbh_XVO.mjs";
import { i as rpc_exports, n as generateTypesInChildProcess, r as DtsWorker, t as consumeTypes } from "./consumeTypes-COIltElB.mjs";
import { i as rpc_exports, n as generateTypesInChildProcess, r as DtsWorker, t as consumeTypes } from "./consumeTypes-CUv-A3UY.mjs";
export { DTSManager, DtsWorker, HOST_API_TYPES_FILE_NAME, REMOTE_ALIAS_IDENTIFIER, REMOTE_API_TYPES_FILE_NAME, consumeTypes, generateTypes, generateTypesInChildProcess, getDTSManagerConstructor, isTSProject, retrieveHostConfig, retrieveMfTypesPath, retrieveOriginalOutDir, retrieveRemoteConfig, retrieveTypesAssetsInfo, retrieveTypesZipPath, rpc_exports as rpc, validateOptions };

@@ -1,5 +0,5 @@

import { _ as retrieveMfTypesPath, b as ModuleFederationDevServer, g as retrieveTypesZipPath, h as retrieveHostConfig, i as retrieveRemoteConfig, n as RpcGMCallTypes, o as getDTSManagerConstructor, t as exposeRpc, y as createHttpServer } from "./expose-rpc-ZWQxpg0y.mjs";
import { _ as retrieveMfTypesPath, b as ModuleFederationDevServer, g as retrieveTypesZipPath, h as retrieveHostConfig, i as retrieveRemoteConfig, n as RpcGMCallTypes, o as getDTSManagerConstructor, t as exposeRpc, y as createHttpServer } from "./expose-rpc-BiwGpqZ3.mjs";
import { o as UpdateMode, r as DEFAULT_TAR_NAME } from "./Action-DNNg2YDh.mjs";
import { n as UpdateKind, o as getIPV4, s as fileLog } from "./Broker-Cmbh_XVO.mjs";
import "./consumeTypes-COIltElB.mjs";
import "./consumeTypes-CUv-A3UY.mjs";
import "./core.mjs";

@@ -6,0 +6,0 @@ import { t as getIpFromEntry } from "./utils-CkPvDGOy.mjs";

@@ -1,2 +0,2 @@

import { n as RpcGMCallTypes, r as generateTypes, t as exposeRpc } from "./expose-rpc-ZWQxpg0y.mjs";
import { n as RpcGMCallTypes, r as generateTypes, t as exposeRpc } from "./expose-rpc-BiwGpqZ3.mjs";
import "./Broker-Cmbh_XVO.mjs";

@@ -3,0 +3,0 @@

@@ -1,5 +0,5 @@

import { a as cloneDeepOptions, c as isTSProject, l as retrieveTypesAssetsInfo, n as RpcGMCallTypes, r as generateTypes, u as validateOptions } from "./expose-rpc-ZWQxpg0y.mjs";
import { a as cloneDeepOptions, c as isTSProject, l as retrieveTypesAssetsInfo, n as RpcGMCallTypes, r as generateTypes, u as validateOptions } from "./expose-rpc-BiwGpqZ3.mjs";
import { s as WEB_CLIENT_OPTIONS_IDENTIFIER } from "./Action-DNNg2YDh.mjs";
import { c as logger$1, o as getIPV4 } from "./Broker-Cmbh_XVO.mjs";
import { a as createRpcWorker, n as generateTypesInChildProcess, t as consumeTypes } from "./consumeTypes-COIltElB.mjs";
import { a as createRpcWorker, n as generateTypesInChildProcess, t as consumeTypes } from "./consumeTypes-CUv-A3UY.mjs";
import "./core.mjs";

@@ -6,0 +6,0 @@ import fs from "fs";

Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const require_Action = require('./Action-CzhPMw2i.js');
const require_Broker = require('./Broker-CaenCqdn.js');
const require_expose_rpc = require('./expose-rpc-XLxmAXIW.js');
require('./consumeTypes-FbF0o6AJ.js');
const require_expose_rpc = require('./expose-rpc-BNzQqY2X.js');
require('./consumeTypes-DuDkcp8N.js');
require('./core.js');

@@ -7,0 +7,0 @@ const require_utils = require('./utils-7KqCZHbb.js');

Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
require('./Broker-CaenCqdn.js');
const require_expose_rpc = require('./expose-rpc-XLxmAXIW.js');
const require_expose_rpc = require('./expose-rpc-BNzQqY2X.js');

@@ -5,0 +5,0 @@ //#region src/core/lib/forkGenerateDts.ts

Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const require_Action = require('./Action-CzhPMw2i.js');
const require_Broker = require('./Broker-CaenCqdn.js');
const require_expose_rpc = require('./expose-rpc-XLxmAXIW.js');
const require_consumeTypes = require('./consumeTypes-FbF0o6AJ.js');
const require_expose_rpc = require('./expose-rpc-BNzQqY2X.js');
const require_consumeTypes = require('./consumeTypes-DuDkcp8N.js');
require('./core.js');

@@ -7,0 +7,0 @@ let fs = require("fs");

{
"name": "@module-federation/dts-plugin",
"version": "2.5.1",
"version": "2.6.0",
"author": "hanric <hanric.zhang@gmail.com>",

@@ -71,9 +71,9 @@ "main": "./dist/index.js",

"isomorphic-ws": "5.0.0",
"undici": "7.24.7",
"undici": "7.28.0",
"node-schedule": "2.1.1",
"ws": "8.21.0",
"@module-federation/error-codes": "2.5.1",
"@module-federation/managers": "2.5.1",
"@module-federation/sdk": "2.5.1",
"@module-federation/third-party-dts-extractor": "2.5.1"
"@module-federation/error-codes": "2.6.0",
"@module-federation/sdk": "2.6.0",
"@module-federation/managers": "2.6.0",
"@module-federation/third-party-dts-extractor": "2.6.0"
},

@@ -90,3 +90,3 @@ "devDependencies": {

"webpack": "^5.104.1",
"@module-federation/runtime": "2.5.1"
"@module-federation/runtime": "2.6.0"
},

@@ -93,0 +93,0 @@ "peerDependencies": {

const require_Action = require('./Action-CzhPMw2i.js');
const require_expose_rpc = require('./expose-rpc-XLxmAXIW.js');
let url = require("url");
let path = require("path");
path = require_Action.__toESM(path);
let crypto = require("crypto");
let child_process = require("child_process");
child_process = require_Action.__toESM(child_process);
let process$1 = require("process");
process$1 = require_Action.__toESM(process$1);
//#region src/core/rpc/rpc-error.ts
var RpcExitError = class extends Error {
constructor(message, code, signal) {
super(message);
this.code = code;
this.signal = signal;
this.name = "RpcExitError";
}
};
//#endregion
//#region src/core/rpc/wrap-rpc.ts
function createControlledPromise() {
let resolve = () => void 0;
let reject = () => void 0;
return {
promise: new Promise((aResolve, aReject) => {
resolve = aResolve;
reject = aReject;
}),
resolve,
reject
};
}
function wrapRpc(childProcess, options) {
return (async (...args) => {
if (!childProcess.send) throw new Error(`Process ${childProcess.pid} doesn't have IPC channels`);
else if (!childProcess.connected) throw new Error(`Process ${childProcess.pid} doesn't have open IPC channels`);
const { id, once } = options;
const { promise: resultPromise, resolve: resolveResult, reject: rejectResult } = createControlledPromise();
const { promise: sendPromise, resolve: resolveSend, reject: rejectSend } = createControlledPromise();
const handleMessage = (message) => {
if (message?.id === id) {
if (message.type === require_expose_rpc.RpcGMCallTypes.RESOLVE) resolveResult(message.value);
else if (message.type === require_expose_rpc.RpcGMCallTypes.REJECT) rejectResult(message.error);
}
if (once && childProcess?.kill) childProcess.kill("SIGTERM");
};
const handleClose = (code, signal) => {
rejectResult(new RpcExitError(code ? `Process ${childProcess.pid} exited with code ${code}${signal ? ` [${signal}]` : ""}` : `Process ${childProcess.pid} exited${signal ? ` [${signal}]` : ""}`, code, signal));
removeHandlers();
};
const removeHandlers = () => {
childProcess.off("message", handleMessage);
childProcess.off("close", handleClose);
};
if (once) childProcess.once("message", handleMessage);
else childProcess.on("message", handleMessage);
childProcess.on("close", handleClose);
childProcess.send({
type: require_expose_rpc.RpcGMCallTypes.CALL,
id,
args
}, (error) => {
if (error) {
rejectSend(error);
removeHandlers();
} else resolveSend(void 0);
});
return sendPromise.then(() => resultPromise);
});
}
//#endregion
//#region src/core/rpc/rpc-worker.ts
const FEDERATION_WORKER_DATA_ENV_KEY = "VMOK_WORKER_DATA_ENV";
function createRpcWorker(modulePath, data, memoryLimit, once) {
const options = {
env: {
...process$1.env,
[FEDERATION_WORKER_DATA_ENV_KEY]: JSON.stringify(data || {})
},
stdio: [
"inherit",
"inherit",
"inherit",
"ipc"
],
serialization: "advanced"
};
if (memoryLimit) options.execArgv = [`--max-old-space-size=${memoryLimit}`];
let childProcess, remoteMethod;
const id = (0, crypto.randomUUID)();
return {
connect(...args) {
if (childProcess && !childProcess.connected) {
childProcess.send({
type: require_expose_rpc.RpcGMCallTypes.EXIT,
id
});
childProcess = void 0;
remoteMethod = void 0;
}
if (!childProcess?.connected) {
childProcess = child_process.fork(modulePath, options);
remoteMethod = wrapRpc(childProcess, {
id,
once
});
}
if (!remoteMethod) return Promise.reject(/* @__PURE__ */ new Error("Worker is not connected - cannot perform RPC."));
return remoteMethod(...args);
},
terminate() {
try {
if (childProcess.connected) childProcess.send({
type: require_expose_rpc.RpcGMCallTypes.EXIT,
id
}, (err) => {
if (err) console.error("Error sending message:", err);
});
} catch (error) {
if (error.code === "EPIPE") console.error("Pipe closed before message could be sent:", error);
else console.error("Unexpected error:", error);
}
childProcess = void 0;
remoteMethod = void 0;
},
get connected() {
return Boolean(childProcess?.connected);
},
get process() {
return childProcess;
},
get id() {
return id;
}
};
}
function getRpcWorkerData() {
return JSON.parse(process$1.env[FEDERATION_WORKER_DATA_ENV_KEY] || "{}");
}
//#endregion
//#region src/core/rpc/index.ts
var rpc_exports = /* @__PURE__ */ require_Action.__exportAll({
RpcExitError: () => RpcExitError,
RpcGMCallTypes: () => require_expose_rpc.RpcGMCallTypes,
createRpcWorker: () => createRpcWorker,
exposeRpc: () => require_expose_rpc.exposeRpc,
getRpcWorkerData: () => getRpcWorkerData,
wrapRpc: () => wrapRpc
});
//#endregion
//#region src/core/lib/DtsWorker.ts
const __filename$1 = (0, url.fileURLToPath)(require("url").pathToFileURL(__filename).href);
const __dirname$1 = path.default.dirname(__filename$1);
const __extname = path.default.extname(__filename$1);
var DtsWorker = class {
constructor(options) {
this._options = require_expose_rpc.cloneDeepOptions(options);
this.removeUnSerializationOptions();
this.rpcWorker = createRpcWorker(path.default.resolve(__dirname$1, `./fork-generate-dts${__extname}`), {}, void 0, true);
this._res = this.rpcWorker.connect(this._options);
}
removeUnSerializationOptions() {
if (this._options.remote?.moduleFederationConfig?.manifest) delete this._options.remote?.moduleFederationConfig?.manifest;
if (this._options.host?.moduleFederationConfig?.manifest) delete this._options.host?.moduleFederationConfig?.manifest;
}
get controlledPromise() {
const ensureChildProcessExit = () => {
try {
const pid = this.rpcWorker.process?.pid;
const rootPid = process.pid;
if (pid && rootPid !== pid) process.kill(pid, 0);
} catch (error) {
if (require_expose_rpc.isDebugMode()) console.error(error);
}
};
return Promise.resolve(this._res).then(() => {
this.exit();
ensureChildProcessExit();
}).catch((err) => {
if (require_expose_rpc.isDebugMode()) console.error(err);
ensureChildProcessExit();
});
}
exit() {
try {
this.rpcWorker?.terminate();
} catch (err) {
if (require_expose_rpc.isDebugMode()) console.error(err);
}
}
};
//#endregion
//#region src/core/lib/generateTypesInChildProcess.ts
async function generateTypesInChildProcess(options) {
return new DtsWorker(options).controlledPromise;
}
//#endregion
//#region src/core/lib/consumeTypes.ts
async function consumeTypes(options) {
await new (require_expose_rpc.getDTSManagerConstructor(options.host?.implementation))(options).consumeTypes();
}
//#endregion
Object.defineProperty(exports, 'DtsWorker', {
enumerable: true,
get: function () {
return DtsWorker;
}
});
Object.defineProperty(exports, 'consumeTypes', {
enumerable: true,
get: function () {
return consumeTypes;
}
});
Object.defineProperty(exports, 'createRpcWorker', {
enumerable: true,
get: function () {
return createRpcWorker;
}
});
Object.defineProperty(exports, 'generateTypesInChildProcess', {
enumerable: true,
get: function () {
return generateTypesInChildProcess;
}
});
Object.defineProperty(exports, 'rpc_exports', {
enumerable: true,
get: function () {
return rpc_exports;
}
});
import { a as cloneDeepOptions, n as RpcGMCallTypes, o as getDTSManagerConstructor, s as isDebugMode, t as exposeRpc, x as __exportAll } from "./expose-rpc-ZWQxpg0y.mjs";
import { fileURLToPath } from "url";
import path from "path";
import { randomUUID } from "crypto";
import * as child_process from "child_process";
import * as process$2 from "process";
//#region src/core/rpc/rpc-error.ts
var RpcExitError = class extends Error {
constructor(message, code, signal) {
super(message);
this.code = code;
this.signal = signal;
this.name = "RpcExitError";
}
};
//#endregion
//#region src/core/rpc/wrap-rpc.ts
function createControlledPromise() {
let resolve = () => void 0;
let reject = () => void 0;
return {
promise: new Promise((aResolve, aReject) => {
resolve = aResolve;
reject = aReject;
}),
resolve,
reject
};
}
function wrapRpc(childProcess, options) {
return (async (...args) => {
if (!childProcess.send) throw new Error(`Process ${childProcess.pid} doesn't have IPC channels`);
else if (!childProcess.connected) throw new Error(`Process ${childProcess.pid} doesn't have open IPC channels`);
const { id, once } = options;
const { promise: resultPromise, resolve: resolveResult, reject: rejectResult } = createControlledPromise();
const { promise: sendPromise, resolve: resolveSend, reject: rejectSend } = createControlledPromise();
const handleMessage = (message) => {
if (message?.id === id) {
if (message.type === RpcGMCallTypes.RESOLVE) resolveResult(message.value);
else if (message.type === RpcGMCallTypes.REJECT) rejectResult(message.error);
}
if (once && childProcess?.kill) childProcess.kill("SIGTERM");
};
const handleClose = (code, signal) => {
rejectResult(new RpcExitError(code ? `Process ${childProcess.pid} exited with code ${code}${signal ? ` [${signal}]` : ""}` : `Process ${childProcess.pid} exited${signal ? ` [${signal}]` : ""}`, code, signal));
removeHandlers();
};
const removeHandlers = () => {
childProcess.off("message", handleMessage);
childProcess.off("close", handleClose);
};
if (once) childProcess.once("message", handleMessage);
else childProcess.on("message", handleMessage);
childProcess.on("close", handleClose);
childProcess.send({
type: RpcGMCallTypes.CALL,
id,
args
}, (error) => {
if (error) {
rejectSend(error);
removeHandlers();
} else resolveSend(void 0);
});
return sendPromise.then(() => resultPromise);
});
}
//#endregion
//#region src/core/rpc/rpc-worker.ts
const FEDERATION_WORKER_DATA_ENV_KEY = "VMOK_WORKER_DATA_ENV";
function createRpcWorker(modulePath, data, memoryLimit, once) {
const options = {
env: {
...process$2.env,
[FEDERATION_WORKER_DATA_ENV_KEY]: JSON.stringify(data || {})
},
stdio: [
"inherit",
"inherit",
"inherit",
"ipc"
],
serialization: "advanced"
};
if (memoryLimit) options.execArgv = [`--max-old-space-size=${memoryLimit}`];
let childProcess, remoteMethod;
const id = randomUUID();
return {
connect(...args) {
if (childProcess && !childProcess.connected) {
childProcess.send({
type: RpcGMCallTypes.EXIT,
id
});
childProcess = void 0;
remoteMethod = void 0;
}
if (!childProcess?.connected) {
childProcess = child_process.fork(modulePath, options);
remoteMethod = wrapRpc(childProcess, {
id,
once
});
}
if (!remoteMethod) return Promise.reject(/* @__PURE__ */ new Error("Worker is not connected - cannot perform RPC."));
return remoteMethod(...args);
},
terminate() {
try {
if (childProcess.connected) childProcess.send({
type: RpcGMCallTypes.EXIT,
id
}, (err) => {
if (err) console.error("Error sending message:", err);
});
} catch (error) {
if (error.code === "EPIPE") console.error("Pipe closed before message could be sent:", error);
else console.error("Unexpected error:", error);
}
childProcess = void 0;
remoteMethod = void 0;
},
get connected() {
return Boolean(childProcess?.connected);
},
get process() {
return childProcess;
},
get id() {
return id;
}
};
}
function getRpcWorkerData() {
return JSON.parse(process$2.env[FEDERATION_WORKER_DATA_ENV_KEY] || "{}");
}
//#endregion
//#region src/core/rpc/index.ts
var rpc_exports = /* @__PURE__ */ __exportAll({
RpcExitError: () => RpcExitError,
RpcGMCallTypes: () => RpcGMCallTypes,
createRpcWorker: () => createRpcWorker,
exposeRpc: () => exposeRpc,
getRpcWorkerData: () => getRpcWorkerData,
wrapRpc: () => wrapRpc
});
//#endregion
//#region src/core/lib/DtsWorker.ts
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const __extname = path.extname(__filename);
var DtsWorker = class {
constructor(options) {
this._options = cloneDeepOptions(options);
this.removeUnSerializationOptions();
this.rpcWorker = createRpcWorker(path.resolve(__dirname, `./fork-generate-dts${__extname}`), {}, void 0, true);
this._res = this.rpcWorker.connect(this._options);
}
removeUnSerializationOptions() {
if (this._options.remote?.moduleFederationConfig?.manifest) delete this._options.remote?.moduleFederationConfig?.manifest;
if (this._options.host?.moduleFederationConfig?.manifest) delete this._options.host?.moduleFederationConfig?.manifest;
}
get controlledPromise() {
const ensureChildProcessExit = () => {
try {
const pid = this.rpcWorker.process?.pid;
const rootPid = process.pid;
if (pid && rootPid !== pid) process.kill(pid, 0);
} catch (error) {
if (isDebugMode()) console.error(error);
}
};
return Promise.resolve(this._res).then(() => {
this.exit();
ensureChildProcessExit();
}).catch((err) => {
if (isDebugMode()) console.error(err);
ensureChildProcessExit();
});
}
exit() {
try {
this.rpcWorker?.terminate();
} catch (err) {
if (isDebugMode()) console.error(err);
}
}
};
//#endregion
//#region src/core/lib/generateTypesInChildProcess.ts
async function generateTypesInChildProcess(options) {
return new DtsWorker(options).controlledPromise;
}
//#endregion
//#region src/core/lib/consumeTypes.ts
async function consumeTypes(options) {
await new (getDTSManagerConstructor(options.host?.implementation))(options).consumeTypes();
}
//#endregion
export { createRpcWorker as a, rpc_exports as i, generateTypesInChildProcess as n, DtsWorker as r, consumeTypes as t };
import { a as MF_SERVER_IDENTIFIER, n as ActionKind, o as UpdateMode, r as DEFAULT_TAR_NAME, t as Action } from "./Action-DNNg2YDh.mjs";
import { a as getIdentifier, c as logger$1, i as getFreePort, l as LogKind, n as UpdateKind, o as getIPV4, r as fib, s as fileLog, t as Broker, u as APIKind } from "./Broker-Cmbh_XVO.mjs";
import { createRequire } from "node:module";
import fs, { existsSync, mkdirSync, writeFileSync } from "fs";
import { fileURLToPath } from "url";
import path, { dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from "path";
import { cp, mkdir, readFile, readdir, rm, stat, writeFile } from "fs/promises";
import { utils } from "@module-federation/managers";
import typescript from "typescript";
import { ENCODE_NAME_PREFIX, MANIFEST_EXT, TEMP_DIR, decodeName, getProcessEnv, inferAutoPublicPath, parseEntry } from "@module-federation/sdk";
import ansiColors from "ansi-colors";
import { Agent } from "undici";
import { ThirdPartyExtractor } from "@module-federation/third-party-dts-extractor";
import AdmZip from "adm-zip";
import crypto from "crypto";
import { TYPE_001, typeDescMap } from "@module-federation/error-codes";
import { logAndReport } from "@module-federation/error-codes/node";
import { execFile, fork } from "child_process";
import util from "util";
import WebSocket from "isomorphic-ws";
import http from "http";
import process$1 from "process";
//#region \0rolldown/runtime.js
var __defProp = Object.defineProperty;
var __exportAll = (all, no_symbols) => {
let target = {};
for (var name in all) {
__defProp(target, name, {
get: all[name],
enumerable: true
});
}
if (!no_symbols) {
__defProp(target, Symbol.toStringTag, { value: "Module" });
}
return target;
};
var __require = /* @__PURE__ */ createRequire(import.meta.url);
//#endregion
//#region src/server/message/Action/AddPublisher.ts
var AddPublisherAction = class extends Action {
constructor(payload) {
super({ payload }, ActionKind.ADD_PUBLISHER);
}
};
//#endregion
//#region src/server/message/Action/AddSubscriber.ts
var AddSubscriberAction = class extends Action {
constructor(payload) {
super({ payload }, ActionKind.ADD_SUBSCRIBER);
}
};
//#endregion
//#region src/server/message/Action/ExitSubscriber.ts
var ExitSubscriberAction = class extends Action {
constructor(payload) {
super({ payload }, ActionKind.EXIT_SUBSCRIBER);
}
};
//#endregion
//#region src/server/message/Action/ExitPublisher.ts
var ExitPublisherAction = class extends Action {
constructor(payload) {
super({ payload }, ActionKind.EXIT_PUBLISHER);
}
};
//#endregion
//#region src/server/message/Action/NotifyWebClient.ts
var NotifyWebClientAction = class extends Action {
constructor(payload) {
super({ payload }, ActionKind.NOTIFY_WEB_CLIENT);
}
};
//#endregion
//#region src/server/message/Action/UpdatePublisher.ts
var UpdatePublisherAction = class extends Action {
constructor(payload) {
super({ payload }, ActionKind.UPDATE_PUBLISHER);
}
};
//#endregion
//#region src/server/broker/createBroker.ts
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
function createBroker() {
const sub = fork(path.resolve(__dirname, "./start-broker.js"), [], {
detached: true,
stdio: "ignore",
env: process.env
});
sub.send("start");
sub.unref();
return sub;
}
//#endregion
//#region src/server/DevServer.ts
var ModuleFederationDevServer = class {
constructor(ctx) {
this._publishWebSocket = null;
this._subscriberWebsocketMap = {};
this._reconnect = true;
this._reconnectTimes = 0;
this._isConnected = false;
this._isReconnecting = false;
this._updateCallback = () => Promise.resolve(void 0);
const { name, remotes, remoteTypeTarPath, updateCallback } = ctx;
this._ip = getIPV4();
this._name = name;
this._remotes = remotes;
this._remoteTypeTarPath = remoteTypeTarPath;
this._updateCallback = updateCallback;
this._stopWhenSIGTERMOrSIGINT();
this._handleUnexpectedExit();
this._connectPublishToServer();
}
_connectPublishToServer() {
if (!this._reconnect) return;
fileLog(`Publisher:${this._name} Trying to connect to ws://${this._ip}:${Broker.DEFAULT_WEB_SOCKET_PORT}...`, MF_SERVER_IDENTIFIER, "info");
this._publishWebSocket = new WebSocket(`ws://${this._ip}:${Broker.DEFAULT_WEB_SOCKET_PORT}?WEB_SOCKET_CONNECT_MAGIC_ID=${Broker.WEB_SOCKET_CONNECT_MAGIC_ID}`);
this._publishWebSocket.on("open", () => {
fileLog(`Current pid: ${process.pid}, publisher:${this._name} connected to ws://${this._ip}:${Broker.DEFAULT_WEB_SOCKET_PORT}, starting service...`, MF_SERVER_IDENTIFIER, "info");
this._isConnected = true;
const addPublisherAction = new AddPublisherAction({
name: this._name,
ip: this._ip,
remoteTypeTarPath: this._remoteTypeTarPath
});
this._publishWebSocket?.send(JSON.stringify(addPublisherAction));
this._connectSubscribers();
});
this._publishWebSocket.on("message", async (message) => {
try {
const parsedMessage = JSON.parse(message.toString());
if (parsedMessage.type === "Log") {
if (parsedMessage.kind === LogKind.BrokerExitLog) {
fileLog(`Receive broker exit signal, ${this._name} service will exit...`, MF_SERVER_IDENTIFIER, "warn");
this._exit();
}
}
if (parsedMessage.type === "API") {
if (parsedMessage.kind === APIKind.FETCH_TYPES) {
const { payload: { remoteInfo } } = parsedMessage;
fileLog(`${this._name} Receive broker FETCH_TYPES, payload as follows: ${JSON.stringify(remoteInfo, null, 2)}.`, MF_SERVER_IDENTIFIER, "info");
await this.fetchDynamicRemoteTypes({ remoteInfo });
}
}
} catch (err) {
console.error(err);
const exitPublisher = new ExitPublisherAction({
name: this._name,
ip: this._ip
});
const exitSubscriber = new ExitSubscriberAction({
name: this._name,
ip: this._ip,
publishers: this._remotes.map((remote) => ({
name: remote.name,
ip: remote.ip
}))
});
this._publishWebSocket?.send(JSON.stringify(exitPublisher));
this._publishWebSocket?.send(JSON.stringify(exitSubscriber));
fileLog("Parse messages error, ModuleFederationDevServer will exit...", MF_SERVER_IDENTIFIER, "fatal");
this._exit();
}
});
this._publishWebSocket.on("close", (code) => {
fileLog(`Connection closed with code ${code}.`, MF_SERVER_IDENTIFIER, "warn");
this._publishWebSocket && this._publishWebSocket.close();
this._publishWebSocket = null;
if (!this._reconnect) return;
const reconnectTime = fib(++this._reconnectTimes);
fileLog(`start reconnecting to server after ${reconnectTime}s.`, MF_SERVER_IDENTIFIER, "info");
setTimeout(() => this._connectPublishToServer(), reconnectTime * 1e3);
});
this._publishWebSocket.on("error", this._tryCreateBackgroundBroker.bind(this));
}
_connectSubscriberToServer(remote) {
const { name, ip } = remote;
fileLog(`remote module:${name} trying to connect to ws://${ip}:${Broker.DEFAULT_WEB_SOCKET_PORT}...`, MF_SERVER_IDENTIFIER, "info");
const identifier = getIdentifier({
name,
ip
});
this._subscriberWebsocketMap[identifier] = new WebSocket(`ws://${ip}:${Broker.DEFAULT_WEB_SOCKET_PORT}?WEB_SOCKET_CONNECT_MAGIC_ID=${Broker.WEB_SOCKET_CONNECT_MAGIC_ID}`);
this._subscriberWebsocketMap[identifier].on("open", () => {
fileLog(`Current pid: ${process.pid} remote module: ${name} connected to ws://${ip}:${Broker.DEFAULT_WEB_SOCKET_PORT}, starting service...`, MF_SERVER_IDENTIFIER, "info");
const addSubscriber = new AddSubscriberAction({
name: this._name,
ip: this._ip,
publishers: [{
name,
ip
}]
});
this._subscriberWebsocketMap[identifier].send(JSON.stringify(addSubscriber));
});
this._subscriberWebsocketMap[identifier].on("message", async (message) => {
try {
const parsedMessage = JSON.parse(message.toString());
if (parsedMessage.type === "Log") {
if (parsedMessage.kind === LogKind.BrokerExitLog) {
fileLog(`${identifier}'s Server exit, thus ${identifier} will no longer has reload ability.`, MF_SERVER_IDENTIFIER, "warn");
this._exit();
}
}
if (parsedMessage.type === "API") {
if (parsedMessage.kind === APIKind.UPDATE_SUBSCRIBER) {
const { payload: { updateKind, updateSourcePaths, name: subscribeName, remoteTypeTarPath, updateMode } } = parsedMessage;
await this._updateSubscriber({
remoteTypeTarPath,
name: subscribeName,
updateKind,
updateMode,
updateSourcePaths
});
}
}
} catch (err) {
console.error(err);
const exitSubscriber = new ExitSubscriberAction({
name: this._name,
ip: this._ip,
publishers: [{
name,
ip
}]
});
this._subscriberWebsocketMap[identifier].send(JSON.stringify(exitSubscriber));
fileLog(`${identifier} exit,
error: ${err instanceof Error ? err.toString() : JSON.stringify(err)}
`, MF_SERVER_IDENTIFIER, "warn");
}
});
this._subscriberWebsocketMap[identifier].on("close", (code) => {
fileLog(`Connection closed with code ${code}.`, MF_SERVER_IDENTIFIER, "warn");
this._subscriberWebsocketMap[identifier]?.close();
delete this._subscriberWebsocketMap[identifier];
});
this._subscriberWebsocketMap[identifier].on("error", (err) => {
if ("code" in err && err.code === "ETIMEDOUT") fileLog(`Can not connect ${JSON.stringify(remote)}, please make sure this remote is started locally.`, MF_SERVER_IDENTIFIER, "warn");
else console.error(err);
this._subscriberWebsocketMap[identifier]?.close();
delete this._subscriberWebsocketMap[identifier];
});
}
_connectSubscribers() {
this._remotes.forEach((remote) => {
this._connectSubscriberToServer(remote);
});
}
async _updateSubscriber(options) {
const { updateMode, updateKind, updateSourcePaths, name, remoteTypeTarPath, remoteInfo } = options;
fileLog(`[_updateSubscriber] run, options: ${JSON.stringify(options, null, 2)}`, MF_SERVER_IDENTIFIER, "warn");
if (updateMode === UpdateMode.PASSIVE && updateSourcePaths.includes(this._name)) {
fileLog(`[_updateSubscriber] run, updateSourcePaths:${updateSourcePaths} includes ${this._name}, update ignore!`, MF_SERVER_IDENTIFIER, "warn");
return;
}
if (updateSourcePaths.slice(-1)[0] === this._name) {
fileLog(`[_updateSubscriber] run, updateSourcePaths:${updateSourcePaths} ends is ${this._name}, update ignore!`, MF_SERVER_IDENTIFIER, "warn");
return;
}
fileLog(`[_updateSubscriber] run, updateSourcePaths:${updateSourcePaths}, current module:${this._name}, update start...`, MF_SERVER_IDENTIFIER, "info");
await this._updateCallback({
name,
updateMode,
updateKind,
updateSourcePaths,
remoteTypeTarPath,
remoteInfo
});
const newUpdateSourcePaths = updateSourcePaths.concat(this._name);
const updatePublisher = new UpdatePublisherAction({
name: this._name,
ip: this._ip,
updateMode: UpdateMode.PASSIVE,
updateKind,
updateSourcePaths: newUpdateSourcePaths,
remoteTypeTarPath: this._remoteTypeTarPath
});
fileLog(`[_updateSubscriber] run, updateSourcePaths:${newUpdateSourcePaths}, update publisher ${this._name} start...`, MF_SERVER_IDENTIFIER, "info");
this._publishWebSocket?.send(JSON.stringify(updatePublisher));
}
_tryCreateBackgroundBroker(err) {
if (!((err?.code === "ECONNREFUSED" || err?.code === "ETIMEDOUT") && err.port === Broker.DEFAULT_WEB_SOCKET_PORT)) {
fileLog(`websocket error: ${err.stack}`, MF_SERVER_IDENTIFIER, "fatal");
return;
}
fileLog(`Failed to connect to ws://${this._ip}:${Broker.DEFAULT_WEB_SOCKET_PORT}...`, MF_SERVER_IDENTIFIER, "fatal");
this._isReconnecting = true;
setTimeout(() => {
this._isReconnecting = false;
if (this._reconnect === false) return;
fileLog("Creating new background broker...", MF_SERVER_IDENTIFIER, "warn");
createBroker().on("message", (message) => {
if (message === "ready") {
fileLog("background broker started.", MF_SERVER_IDENTIFIER, "info");
this._reconnectTimes = 1;
if (process.send) process.send("ready");
}
});
}, Math.ceil(100 * Math.random()));
}
_stopWhenSIGTERMOrSIGINT() {
process.on("SIGTERM", () => {
fileLog(`Process(${process.pid}) SIGTERM, ModuleFederationDevServer will exit...`, MF_SERVER_IDENTIFIER, "warn");
this._exit();
});
process.on("SIGINT", () => {
fileLog(`Process(${process.pid}) SIGINT, ModuleFederationDevServer will exit...`, MF_SERVER_IDENTIFIER, "warn");
this._exit();
});
}
_handleUnexpectedExit() {
process.on("unhandledRejection", (error) => {
if (this._isReconnecting) return;
console.error("Unhandled Rejection Error: ", error);
fileLog(`Process(${process.pid}) unhandledRejection, garfishModuleServer will exit...`, MF_SERVER_IDENTIFIER, "error");
this._exit();
});
process.on("uncaughtException", (error) => {
if (this._isReconnecting) return;
console.error("Unhandled Exception Error: ", error);
fileLog(`Process(${process.pid}) uncaughtException, garfishModuleServer will exit...`, MF_SERVER_IDENTIFIER, "error");
this._exit();
});
}
_exit() {
this._reconnect = false;
if (this._publishWebSocket) {
const exitPublisher = new ExitPublisherAction({
name: this._name,
ip: this._ip
});
this._publishWebSocket.send(JSON.stringify(exitPublisher));
this._publishWebSocket.on("message", (message) => {
const parsedMessage = JSON.parse(message.toString());
fileLog(`[${parsedMessage.kind}]: ${JSON.stringify(parsedMessage)}`, MF_SERVER_IDENTIFIER, "info");
});
}
if (this._publishWebSocket) {
this._publishWebSocket.close();
this._publishWebSocket = null;
}
process.exit(0);
}
exit() {
this._exit();
}
update(options) {
if (!this._publishWebSocket || !this._isConnected) return;
const { updateKind, updateMode, updateSourcePaths, clientName } = options;
fileLog(`update run, ${this._name} module update, updateKind: ${updateKind}, updateMode: ${updateMode}, updateSourcePaths: ${updateSourcePaths}`, MF_SERVER_IDENTIFIER, "info");
if (updateKind === UpdateKind.RELOAD_PAGE) {
const notifyWebClient = new NotifyWebClientAction({
name: clientName || this._name,
updateMode
});
this._publishWebSocket.send(JSON.stringify(notifyWebClient));
return;
}
const updatePublisher = new UpdatePublisherAction({
name: this._name,
ip: this._ip,
updateMode,
updateKind,
updateSourcePaths: [this._name],
remoteTypeTarPath: this._remoteTypeTarPath
});
this._publishWebSocket.send(JSON.stringify(updatePublisher));
}
async fetchDynamicRemoteTypes(options) {
const { remoteInfo, once } = options;
const updateMode = UpdateMode.PASSIVE;
const updateKind = UpdateKind.UPDATE_TYPE;
fileLog(`fetchDynamicRemoteTypes: remoteInfo: ${JSON.stringify(remoteInfo)}`, MF_SERVER_IDENTIFIER, "info");
await this._updateCallback({
name: this._name,
updateMode,
updateKind,
updateSourcePaths: [],
remoteTypeTarPath: "",
remoteInfo,
once
});
const updatePublisher = new UpdatePublisherAction({
name: this._name,
ip: this._ip,
updateMode,
updateKind,
updateSourcePaths: [this._name],
remoteTypeTarPath: this._remoteTypeTarPath
});
this._publishWebSocket.send(JSON.stringify(updatePublisher));
}
};
//#endregion
//#region src/server/createHttpServer.ts
async function createHttpServer(options) {
const { typeTarPath } = options;
const freeport = await getFreePort();
const server = http.createServer((req, res) => {
if ((req.url?.split("?")[0] ?? "/") === `/${DEFAULT_TAR_NAME}`) {
res.statusCode = 200;
res.setHeader("Content-Type", "application/x-gzip");
if (req.method === "HEAD") {
res.end();
return;
}
const stream = fs.createReadStream(typeTarPath);
stream.on("error", () => {
if (!res.headersSent) res.statusCode = 500;
res.end();
});
res.on("close", () => {
stream.destroy();
});
stream.pipe(res);
return;
}
res.statusCode = 404;
res.end();
});
server.listen(freeport);
return {
server,
serverAddress: `http://${getIPV4()}:${freeport}`
};
}
//#endregion
//#region src/core/lib/typeScriptCompiler.ts
const STARTS_WITH_SLASH = /^\//;
const DEFINITION_FILE_EXTENSION = ".d.ts";
const retrieveMfTypesPath = (tsConfig, remoteOptions) => normalize(tsConfig.compilerOptions.outDir.replace(remoteOptions.compiledTypesFolder, ""));
const retrieveOriginalOutDir = (tsConfig, remoteOptions) => normalize(tsConfig.compilerOptions.outDir.replace(remoteOptions.compiledTypesFolder, "").replace(remoteOptions.typesFolder, ""));
const retrieveMfAPITypesPath = (tsConfig, remoteOptions) => join(retrieveOriginalOutDir(tsConfig, remoteOptions), `${remoteOptions.typesFolder}.d.ts`);
function writeTempTsConfig(tsConfig, context, name, cwd) {
const createHash = (contents) => {
return crypto.createHash("md5").update(contents).digest("hex");
};
const hash = createHash(`${JSON.stringify(tsConfig)}${name}${Date.now()}`);
const tempTsConfigJsonPath = resolve(cwd ?? context, "node_modules", TEMP_DIR, `tsconfig.${hash}.json`);
mkdirSync(dirname(tempTsConfigJsonPath), { recursive: true });
writeFileSync(tempTsConfigJsonPath, JSON.stringify(tsConfig, null, 2));
return tempTsConfigJsonPath;
}
const removeExt = (f) => {
const vueExt = ".vue";
const ext = extname(f);
if (ext === vueExt) return f;
const regexPattern = new RegExp(`\\${ext}$`);
return f.replace(regexPattern, "");
};
function getExposeKey(options) {
const { filePath, rootDir, outDir, mapExposeToEntry } = options;
return mapExposeToEntry[relative(outDir, filePath.replace(new RegExp(`\\.d.ts$`), ""))];
}
const processTypesFile = async (options) => {
const { outDir, filePath, rootDir, cb, mapExposeToEntry, mfTypePath } = options;
if (!existsSync(filePath)) return;
if ((await stat(filePath)).isDirectory()) {
const files = await readdir(filePath);
await Promise.all(files.map((file) => processTypesFile({
...options,
filePath: join(filePath, file)
})));
} else if (filePath.endsWith(".d.ts")) {
const exposeKey = getExposeKey({
filePath,
rootDir,
outDir,
mapExposeToEntry
});
if (exposeKey) {
const mfeTypeEntry = join(mfTypePath, `${exposeKey === "." ? "index" : exposeKey}${DEFINITION_FILE_EXTENSION}`);
const mfeTypeEntryDirectory = dirname(mfeTypeEntry);
const relativePathToOutput = relative(mfeTypeEntryDirectory, filePath).replace(DEFINITION_FILE_EXTENSION, "").replace(STARTS_WITH_SLASH, "").split(sep).join("/");
mkdirSync(mfeTypeEntryDirectory, { recursive: true });
await writeFile(mfeTypeEntry, `export * from './${relativePathToOutput}';\nexport { default } from './${relativePathToOutput}';`);
}
cb(await readFile(filePath, "utf8"));
}
};
const getPMFromUserAgent = () => {
const userAgent = process.env["npm_config_user_agent"];
if (userAgent == null) return "null";
return userAgent.split("/")[0];
};
const resolvePackageManagerExecutable = () => {
switch (getPMFromUserAgent()) {
case "yarn": return "yarn";
default: return "npx";
}
};
const splitCommandArgs = (value) => {
const args = [];
let current = "";
let quote = null;
let escaped = false;
for (const char of value) {
if (escaped) {
current += char;
escaped = false;
continue;
}
if (char === "\\") {
escaped = true;
continue;
}
if (quote) {
if (char === quote) quote = null;
else current += char;
continue;
}
if (char === "\"" || char === "'") {
quote = char;
continue;
}
if (char.trim() === "") {
if (current) {
args.push(current);
current = "";
}
continue;
}
current += char;
}
if (current) args.push(current);
return args;
};
const formatCommandForDisplay = (executable, args) => {
const formatArg = (arg) => {
if (/[\s'"]/.test(arg)) return JSON.stringify(arg);
return arg;
};
return [executable, ...args].map(formatArg).join(" ");
};
const compileTs = async (mapComponentsToExpose, tsConfig, remoteOptions) => {
if (!Object.keys(mapComponentsToExpose).length) return;
const { compilerOptions } = tsConfig;
const tempTsConfigJsonPath = writeTempTsConfig(tsConfig, remoteOptions.context, remoteOptions.moduleFederationConfig.name || "mf", typeof remoteOptions.moduleFederationConfig.dts !== "boolean" ? remoteOptions.moduleFederationConfig.dts?.cwd ?? void 0 : void 0);
logger$1.debug(`tempTsConfigJsonPath: ${tempTsConfigJsonPath}`);
try {
const mfTypePath = retrieveMfTypesPath(tsConfig, remoteOptions);
const thirdPartyExtractor = new ThirdPartyExtractor({
destDir: resolve(mfTypePath, "node_modules"),
context: remoteOptions.context,
exclude: typeof remoteOptions.extractThirdParty === "object" ? remoteOptions.extractThirdParty.exclude : void 0
});
const execPromise = util.promisify(execFile);
const pmExecutable = resolvePackageManagerExecutable();
const compilerArgs = splitCommandArgs(remoteOptions.compilerInstance);
const cmdArgs = [
...compilerArgs.length > 0 ? compilerArgs : [remoteOptions.compilerInstance],
"--project",
tempTsConfigJsonPath
];
const cmd = formatCommandForDisplay(pmExecutable, cmdArgs);
try {
await execPromise(pmExecutable, cmdArgs, {
cwd: typeof remoteOptions.moduleFederationConfig.dts !== "boolean" ? remoteOptions.moduleFederationConfig.dts?.cwd ?? void 0 : void 0,
shell: process.platform === "win32"
});
} catch (err) {
if (compilerOptions.tsBuildInfoFile) try {
await rm(compilerOptions.tsBuildInfoFile);
} catch (e) {}
logAndReport(TYPE_001, typeDescMap, { cmd }, (msg) => {
throw new Error(msg);
}, void 0);
}
const mapExposeToEntry = Object.fromEntries(Object.entries(mapComponentsToExpose).map(([exposed, filename]) => {
const normalizedFileName = normalize(filename);
let relativeFileName = "";
if (isAbsolute(normalizedFileName)) relativeFileName = relative(tsConfig.compilerOptions.rootDir, normalizedFileName);
else relativeFileName = relative(tsConfig.compilerOptions.rootDir, resolve(remoteOptions.context, normalizedFileName));
return [removeExt(relativeFileName), exposed];
}));
const cb = remoteOptions.extractThirdParty ? thirdPartyExtractor.collectPkgs.bind(thirdPartyExtractor) : () => void 0;
await processTypesFile({
outDir: compilerOptions.outDir,
filePath: compilerOptions.outDir,
rootDir: compilerOptions.rootDir,
mfTypePath,
cb,
mapExposeToEntry
});
if (remoteOptions.extractThirdParty) await thirdPartyExtractor.copyDts();
if (remoteOptions.deleteTsConfig) await rm(tempTsConfigJsonPath);
} catch (err) {
throw err;
}
};
//#endregion
//#region src/core/lib/archiveHandler.ts
const retrieveTypesZipPath = (mfTypesPath, remoteOptions) => join(mfTypesPath.replace(remoteOptions.typesFolder, ""), `${remoteOptions.typesFolder}.zip`);
const createTypesArchive = async (tsConfig, remoteOptions) => {
const mfTypesPath = retrieveMfTypesPath(tsConfig, remoteOptions);
const zip = new AdmZip();
zip.addLocalFolder(mfTypesPath);
return zip.writeZipPromise(retrieveTypesZipPath(mfTypesPath, remoteOptions));
};
const downloadErrorLogger = (destinationFolder, fileToDownload) => (reason) => {
throw {
...reason,
message: `Network error: Unable to download federated mocks for '${destinationFolder}' from '${fileToDownload}' because '${reason.message}'`
};
};
const retrieveTypesArchiveDestinationPath = (hostOptions, destinationFolder) => {
return resolve(hostOptions.context, hostOptions.typesFolder, destinationFolder);
};
const downloadTypesArchive = (hostOptions) => {
let retries = 0;
return async ([destinationFolder, fileToDownload]) => {
const destinationPath = retrieveTypesArchiveDestinationPath(hostOptions, destinationFolder);
while (retries++ < hostOptions.maxRetries) try {
const url = new URL(fileToDownload).href;
const response = await nativeFetch(url, {
responseType: "arraybuffer",
timeout: hostOptions.timeout,
family: hostOptions.family
}).catch(downloadErrorLogger(destinationFolder, url));
if (typeof response.headers?.["content-type"] === "string" && response.headers["content-type"].includes("text/html")) throw new Error(`${url} receives invalid content-type: ${response.headers["content-type"]}`);
try {
if (hostOptions.deleteTypesFolder) await rm(destinationPath, {
recursive: true,
force: true
});
} catch (error) {
fileLog(`Unable to remove types folder, ${error}`, "downloadTypesArchive", "error");
}
new AdmZip(Buffer.from(response.data)).extractAllTo(destinationPath, true);
fileLog(`zip.extractAllTo success destinationPath: ${destinationPath}; url: ${url}`, "downloadTypesArchive", "info");
return [destinationFolder, destinationPath];
} catch (error) {
fileLog(`Error during types archive download: ${error?.message || "unknown error"}`, "downloadTypesArchive", "error");
if (retries >= hostOptions.maxRetries) {
logger$1.error(`Failed to download types archive from "${fileToDownload}". Set FEDERATION_DEBUG=true for details.`);
if (hostOptions.abortOnError !== false) throw error;
return;
}
}
};
};
//#endregion
//#region src/core/configurations/hostPlugin.ts
const defaultOptions$1 = {
typesFolder: "@mf-types",
remoteTypesFolder: "@mf-types",
deleteTypesFolder: true,
maxRetries: 3,
implementation: "",
context: process.cwd(),
abortOnError: true,
consumeAPITypes: false,
runtimePkgs: [],
remoteTypeUrls: {},
timeout: 6e4,
typesOnBuild: false,
family: 0
};
const buildZipUrl = (hostOptions, url) => {
const remoteUrl = new URL(url, "file:");
remoteUrl.pathname = `${remoteUrl.pathname.split("/").slice(0, -1).join("/")}/${hostOptions.remoteTypesFolder}.zip`;
return remoteUrl.protocol === "file:" ? remoteUrl.pathname : remoteUrl.href;
};
const buildApiTypeUrl = (zipUrl) => {
if (!zipUrl) return;
return zipUrl.replace(".zip", ".d.ts");
};
const retrieveRemoteInfo = (options) => {
const { hostOptions, remoteAlias, remote } = options;
const { remoteTypeUrls } = hostOptions;
let decodedRemote = remote;
if (decodedRemote.startsWith(ENCODE_NAME_PREFIX)) decodedRemote = decodeName(decodedRemote, ENCODE_NAME_PREFIX);
const parsedInfo = parseEntry(decodedRemote, void 0, "@");
const url = "entry" in parsedInfo ? parsedInfo.entry : parsedInfo.name === decodedRemote ? decodedRemote : "";
let zipUrl = "";
let apiTypeUrl = "";
const name = parsedInfo.name || remoteAlias;
const remoteTypeUrl = typeof remoteTypeUrls === "object" && remoteTypeUrls[name];
if (remoteTypeUrl) {
zipUrl = remoteTypeUrl.zip;
apiTypeUrl = remoteTypeUrl.api;
}
const shouldResolveTypeUrlsByConvention = Boolean(url && !url.includes(MANIFEST_EXT));
if (!zipUrl && shouldResolveTypeUrlsByConvention) zipUrl = buildZipUrl(hostOptions, url);
if (!apiTypeUrl && zipUrl && (remoteTypeUrl || shouldResolveTypeUrlsByConvention)) apiTypeUrl = buildApiTypeUrl(zipUrl);
return {
name,
url,
zipUrl,
apiTypeUrl,
alias: remoteAlias
};
};
const resolveRemotes = (hostOptions) => {
const parsedOptions = utils.parseOptions(hostOptions.moduleFederationConfig.remotes || {}, (item, key) => ({
remote: Array.isArray(item) ? item[0] : item,
key
}), (item, key) => ({
remote: Array.isArray(item.external) ? item.external[0] : item.external,
key
}));
const remoteTypeUrls = hostOptions.remoteTypeUrls ?? {};
if (typeof remoteTypeUrls !== "object") throw new Error("remoteTypeUrls must be consumed before resolveRemotes");
const remoteInfos = Object.keys(remoteTypeUrls).reduce((sum, remoteName) => {
const { zip, api, alias } = remoteTypeUrls[remoteName];
sum[alias] = {
name: remoteName,
url: "",
zipUrl: zip,
apiTypeUrl: api,
alias: alias || remoteName
};
return sum;
}, {});
return parsedOptions.reduce((accumulator, item) => {
const { key, remote } = item[1];
const res = retrieveRemoteInfo({
hostOptions,
remoteAlias: key,
remote
});
if (accumulator[key]) {
accumulator[key] = {
...accumulator[key],
url: res.url,
apiTypeUrl: accumulator[key].apiTypeUrl || res.apiTypeUrl
};
return accumulator;
}
accumulator[key] = res;
return accumulator;
}, remoteInfos);
};
const retrieveHostConfig = (options) => {
validateOptions(options);
const hostOptions = {
...defaultOptions$1,
...options
};
return {
hostOptions,
mapRemotesToDownload: resolveRemotes(hostOptions)
};
};
//#endregion
//#region src/core/constant.ts
const REMOTE_ALIAS_IDENTIFIER = "REMOTE_ALIAS_IDENTIFIER";
const REMOTE_API_TYPES_FILE_NAME = "apis.d.ts";
const HOST_API_TYPES_FILE_NAME = "index.d.ts";
//#endregion
//#region src/core/lib/DTSManager.ts
var DTSManager = class {
constructor(options) {
this.options = cloneDeepOptions(options);
this.runtimePkgs = [
"@module-federation/runtime",
"@module-federation/enhanced/runtime",
"@module-federation/runtime-tools"
];
this.loadedRemoteAPIAlias = /* @__PURE__ */ new Set();
this.remoteAliasMap = {};
this.extraOptions = options?.extraOptions || {};
this.updatedRemoteInfos = {};
}
generateAPITypes(mapComponentsToExpose) {
const exposePaths = /* @__PURE__ */ new Set();
const packageType = Object.keys(mapComponentsToExpose).reduce((sum, exposeKey) => {
const exposePath = path.join(REMOTE_ALIAS_IDENTIFIER, exposeKey).split(path.sep).join("/");
exposePaths.add(`'${exposePath}'`);
sum = `T extends '${exposePath}' ? typeof import('${exposePath}') :` + sum;
return sum;
}, "any;");
return `
export type RemoteKeys = ${[...exposePaths].join(" | ")};
type PackageType<T> = ${packageType}`;
}
async extractRemoteTypes(options) {
const { remoteOptions, tsConfig } = options;
if (!remoteOptions.extractRemoteTypes) return;
let hasRemotes = false;
const remotes = remoteOptions.moduleFederationConfig.remotes;
if (remotes) {
if (Array.isArray(remotes)) hasRemotes = Boolean(remotes.length);
else if (typeof remotes === "object") hasRemotes = Boolean(Object.keys(remotes).length);
}
const mfTypesPath = retrieveMfTypesPath(tsConfig, remoteOptions);
if (hasRemotes && this.options.host) try {
const { hostOptions } = retrieveHostConfig(this.options.host);
const remoteTypesFolder = path.resolve(hostOptions.context, hostOptions.typesFolder);
const targetDir = path.join(mfTypesPath, "node_modules");
if (fs.existsSync(remoteTypesFolder)) {
const targetFolder = path.resolve(remoteOptions.context, targetDir);
await mkdir(targetFolder, { recursive: true });
await cp(remoteTypesFolder, targetFolder, {
recursive: true,
force: true
});
}
} catch (err) {
if (this.options.host?.abortOnError === false) fileLog(`Unable to copy remote types, ${err}`, "extractRemoteTypes", "error");
else throw err;
}
}
async generateTypes() {
try {
const { options } = this;
if (!options.remote) throw new Error("options.remote is required if you want to generateTypes");
const { remoteOptions, tsConfig, mapComponentsToExpose } = retrieveRemoteConfig(options.remote);
if (!Object.keys(mapComponentsToExpose).length) return;
if (!tsConfig.files?.length) {
logger$1.info("No type files to compile, skip");
return;
}
if (tsConfig.compilerOptions.tsBuildInfoFile) try {
const tsBuildInfoFile = path.resolve(remoteOptions.context, tsConfig.compilerOptions.tsBuildInfoFile);
const mfTypesPath = retrieveMfTypesPath(tsConfig, remoteOptions);
if (!fs.existsSync(mfTypesPath)) fs.rmSync(tsBuildInfoFile, { force: true });
} catch (e) {}
await this.extractRemoteTypes({
remoteOptions,
tsConfig,
mapComponentsToExpose
});
await compileTs(mapComponentsToExpose, tsConfig, remoteOptions);
await createTypesArchive(tsConfig, remoteOptions);
let apiTypesPath = "";
if (remoteOptions.generateAPITypes) {
const apiTypes = this.generateAPITypes(mapComponentsToExpose);
apiTypesPath = retrieveMfAPITypesPath(tsConfig, remoteOptions);
fs.writeFileSync(apiTypesPath, apiTypes);
}
try {
if (remoteOptions.deleteTypesFolder) await rm(retrieveMfTypesPath(tsConfig, remoteOptions), {
recursive: true,
force: true
});
} catch (err) {
if (isDebugMode()) console.error(err);
}
logger$1.success("Federated types created correctly");
} catch (error) {
if (this.options.remote?.abortOnError === false) {
if (this.options.displayErrorInTerminal) logger$1.error(error);
} else throw error;
}
}
async requestRemoteManifest(remoteInfo, hostOptions) {
try {
if (!remoteInfo.url.includes(MANIFEST_EXT)) return remoteInfo;
if (remoteInfo.zipUrl) return remoteInfo;
const url = remoteInfo.url;
const manifestJson = (await nativeFetch(url, {
timeout: hostOptions.timeout,
family: hostOptions.family
})).data;
if (!manifestJson.metaData.types.zip) throw new Error(`Can not get ${remoteInfo.name}'s types archive url!`);
const addProtocol = (u) => {
if (u.startsWith("//")) return `https:${u}`;
return u;
};
let publicPath;
if ("publicPath" in manifestJson.metaData) publicPath = manifestJson.metaData.publicPath;
else {
const getPublicPath = new Function(manifestJson.metaData.getPublicPath);
if (manifestJson.metaData.getPublicPath.startsWith("function")) publicPath = getPublicPath()();
else publicPath = getPublicPath();
}
if (publicPath === "auto") publicPath = inferAutoPublicPath(remoteInfo.url);
const normalizedPublicPath = addProtocol(publicPath).endsWith("/") ? addProtocol(publicPath) : `${addProtocol(publicPath)}/`;
remoteInfo.zipUrl = new URL(manifestJson.metaData.types.zip, normalizedPublicPath).href;
if (!manifestJson.metaData.types.api) {
console.warn(`Can not get ${remoteInfo.name}'s api types url!`);
remoteInfo.apiTypeUrl = "";
return remoteInfo;
}
remoteInfo.apiTypeUrl = new URL(manifestJson.metaData.types.api, normalizedPublicPath).href;
return remoteInfo;
} catch (_err) {
fileLog(`fetch manifest failed, ${_err}, ${remoteInfo.name} will be ignored`, "requestRemoteManifest", "error");
return remoteInfo;
}
}
async consumeTargetRemotes(hostOptions, remoteInfo) {
if (!remoteInfo.zipUrl) throw new Error(`Can not get ${remoteInfo.name}'s types archive url!`);
return downloadTypesArchive(hostOptions)([remoteInfo.alias, remoteInfo.zipUrl]);
}
async downloadAPITypes(remoteInfo, destinationPath, hostOptions) {
const { apiTypeUrl } = remoteInfo;
if (!apiTypeUrl) return;
try {
let apiTypeFile = (await nativeFetch(apiTypeUrl, {
timeout: hostOptions.timeout,
family: hostOptions.family
})).data;
apiTypeFile = apiTypeFile.replaceAll(REMOTE_ALIAS_IDENTIFIER, remoteInfo.alias);
const filePath = path.join(destinationPath, REMOTE_API_TYPES_FILE_NAME);
fs.writeFileSync(filePath, apiTypeFile);
const existed = this.loadedRemoteAPIAlias.has(remoteInfo.alias);
this.loadedRemoteAPIAlias.add(remoteInfo.alias);
fileLog(`success`, "downloadAPITypes", "info");
return existed;
} catch (err) {
fileLog(`Unable to download "${remoteInfo.name}" api types, ${err}`, "downloadAPITypes", "error");
}
}
consumeAPITypes(hostOptions) {
const apiTypeFileName = path.join(hostOptions.context, hostOptions.typesFolder, HOST_API_TYPES_FILE_NAME);
try {
const existedFile = fs.readFileSync(apiTypeFileName, "utf-8");
new ThirdPartyExtractor({ destDir: "" }).collectTypeImports(existedFile).forEach((existedImport) => {
const alias = existedImport.split("./").slice(1).join("./").replace("/apis.d.ts", "");
this.loadedRemoteAPIAlias.add(alias);
});
} catch (err) {}
if (!this.loadedRemoteAPIAlias.size) return;
const packageTypes = [];
const remoteKeys = [];
const importTypeStr = [...this.loadedRemoteAPIAlias].sort().map((alias, index) => {
const remoteKey = `RemoteKeys_${index}`;
const packageType = `PackageType_${index}`;
packageTypes.push(`T extends ${remoteKey} ? ${packageType}<T>`);
remoteKeys.push(remoteKey);
return `import type { PackageType as ${packageType},RemoteKeys as ${remoteKey} } from './${alias}/apis.d.ts';`;
}).join("\n");
const remoteKeysStr = `type RemoteKeys = ${remoteKeys.join(" | ")};`;
const packageTypesStr = `type PackageType<T, Y=any> = ${[...packageTypes, "Y"].join(" :\n")} ;`;
const runtimePkgs = /* @__PURE__ */ new Set();
[...this.runtimePkgs, ...hostOptions.runtimePkgs].forEach((pkg) => {
runtimePkgs.add(pkg);
});
const fileStr = `${importTypeStr}
${[...runtimePkgs].map((pkg) => {
return `declare module "${pkg}" {
${remoteKeysStr}
${packageTypesStr}
export function loadRemote<T extends RemoteKeys,Y>(packageName: T): Promise<PackageType<T, Y>>;
export function loadRemote<T extends string,Y>(packageName: T): Promise<PackageType<T, Y>>;
}`;
}).join("\n")}
`;
fs.writeFileSync(path.join(hostOptions.context, hostOptions.typesFolder, HOST_API_TYPES_FILE_NAME), fileStr);
}
async consumeArchiveTypes(options) {
const { hostOptions, mapRemotesToDownload } = retrieveHostConfig(options);
const downloadPromises = Object.entries(mapRemotesToDownload).map(async (item) => {
const remoteInfo = item[1];
if (!this.remoteAliasMap[remoteInfo.alias]) {
const requiredRemoteInfo = await this.requestRemoteManifest(remoteInfo, hostOptions);
this.remoteAliasMap[remoteInfo.alias] = requiredRemoteInfo;
}
return this.consumeTargetRemotes(hostOptions, this.remoteAliasMap[remoteInfo.alias]);
});
return {
hostOptions,
downloadPromisesResult: await Promise.allSettled(downloadPromises)
};
}
async consumeTypes() {
try {
const { options } = this;
if (!options.host) throw new Error("options.host is required if you want to consumeTypes");
const { mapRemotesToDownload } = retrieveHostConfig(options.host);
if (!Object.keys(mapRemotesToDownload).length) return;
const { downloadPromisesResult, hostOptions } = await this.consumeArchiveTypes(options.host);
if (hostOptions.consumeAPITypes) {
await Promise.all(downloadPromisesResult.map(async (item) => {
if (item.status === "rejected" || !item.value) return;
const [alias, destinationPath] = item.value;
const remoteInfo = this.remoteAliasMap[alias];
if (!remoteInfo) return;
await this.downloadAPITypes(remoteInfo, destinationPath, hostOptions);
}));
this.consumeAPITypes(hostOptions);
}
logger$1.success("Federated types extraction completed");
} catch (err) {
if (this.options.host?.abortOnError === false) fileLog(`Unable to consume federated types, ${err}`, "consumeTypes", "error");
else throw err;
}
}
async updateTypes(options) {
try {
const { remoteName, updateMode, remoteTarPath, remoteInfo: updatedRemoteInfo, once } = options;
const hostName = this.options?.host?.moduleFederationConfig?.name;
fileLog(`options: ${JSON.stringify(options, null, 2)};\nhostName: ${hostName}`, "updateTypes", "info");
if (updateMode === UpdateMode.POSITIVE && remoteName === hostName) {
if (!this.options.remote) return;
await this.generateTypes();
} else {
const { remoteAliasMap } = this;
if (!this.options.host) return;
const { hostOptions, mapRemotesToDownload } = retrieveHostConfig(this.options.host);
const loadedRemoteInfo = Object.values(remoteAliasMap).find((i) => i.name === remoteName);
const consumeTypes = async (requiredRemoteInfo) => {
fileLog(`consumeTypes start`, "updateTypes", "info");
if (!requiredRemoteInfo.zipUrl) throw new Error(`Can not get ${requiredRemoteInfo.name}'s types archive url!`);
const [_alias, destinationPath] = await this.consumeTargetRemotes(hostOptions, {
...requiredRemoteInfo,
zipUrl: remoteTarPath || requiredRemoteInfo.zipUrl
});
if (await this.downloadAPITypes(requiredRemoteInfo, destinationPath, hostOptions)) this.consumeAPITypes(hostOptions);
fileLog(`consumeTypes end`, "updateTypes", "info");
};
fileLog(`loadedRemoteInfo: ${JSON.stringify(loadedRemoteInfo, null, 2)}`, "updateTypes", "info");
if (!loadedRemoteInfo) {
const remoteInfo = Object.values(mapRemotesToDownload).find((item) => {
return item.name === remoteName;
});
fileLog(`remoteInfo: ${JSON.stringify(remoteInfo, null, 2)}`, "updateTypes", "info");
if (remoteInfo) {
if (!this.remoteAliasMap[remoteInfo.alias]) {
const requiredRemoteInfo = await this.requestRemoteManifest(remoteInfo, hostOptions);
this.remoteAliasMap[remoteInfo.alias] = requiredRemoteInfo;
}
await consumeTypes(this.remoteAliasMap[remoteInfo.alias]);
} else if (updatedRemoteInfo) {
const consumeDynamicRemoteTypes = async () => {
await consumeTypes(this.updatedRemoteInfos[updatedRemoteInfo.name]);
};
if (!this.updatedRemoteInfos[updatedRemoteInfo.name]) {
const parsedRemoteInfo = retrieveRemoteInfo({
hostOptions,
remoteAlias: updatedRemoteInfo.alias || updatedRemoteInfo.name,
remote: updatedRemoteInfo.url
});
fileLog(`start request manifest`, "consumeTypes", "info");
this.updatedRemoteInfos[updatedRemoteInfo.name] = await this.requestRemoteManifest(parsedRemoteInfo, hostOptions);
fileLog(`end request manifest, this.updatedRemoteInfos[updatedRemoteInfo.name]: ${JSON.stringify(this.updatedRemoteInfos[updatedRemoteInfo.name], null, 2)}`, "updateTypes", "info");
await consumeDynamicRemoteTypes();
}
if (!once && this.updatedRemoteInfos[updatedRemoteInfo.name]) await consumeDynamicRemoteTypes();
}
} else await consumeTypes(loadedRemoteInfo);
}
} catch (err) {
fileLog(`updateTypes fail, ${err}`, "updateTypes", "error");
}
}
};
//#endregion
//#region src/core/lib/utils.ts
const dispatcherCache = /* @__PURE__ */ new Map();
function getDTSManagerConstructor(implementation) {
if (implementation) {
const NewConstructor = __require(implementation);
return NewConstructor.default ? NewConstructor.default : NewConstructor;
}
return DTSManager;
}
const validateOptions = (options) => {
if (!options.moduleFederationConfig) throw new Error("moduleFederationConfig is required");
};
function retrieveTypesAssetsInfo(options) {
let apiTypesPath = "";
let zipTypesPath = "";
try {
const { tsConfig, remoteOptions, mapComponentsToExpose } = retrieveRemoteConfig(options);
if (!Object.keys(mapComponentsToExpose).length || !tsConfig.files.length) return {
apiTypesPath,
zipTypesPath,
zipName: "",
apiFileName: ""
};
zipTypesPath = retrieveTypesZipPath(retrieveMfTypesPath(tsConfig, remoteOptions), remoteOptions);
if (remoteOptions.generateAPITypes) apiTypesPath = retrieveMfAPITypesPath(tsConfig, remoteOptions);
return {
apiTypesPath,
zipTypesPath,
zipName: path.basename(zipTypesPath),
apiFileName: path.basename(apiTypesPath)
};
} catch (err) {
console.error(ansiColors.red(`Unable to compile federated types, ${err}`));
return {
apiTypesPath: "",
zipTypesPath: "",
zipName: "",
apiFileName: ""
};
}
}
function isDebugMode() {
return Boolean(process.env["FEDERATION_DEBUG"]) || process.env["NODE_ENV"] === "test";
}
const isTSProject = (dtsOptions, context = process.cwd()) => {
if (dtsOptions === false) return false;
try {
let filepath = "";
if (typeof dtsOptions === "object" && dtsOptions.tsConfigPath) filepath = dtsOptions.tsConfigPath;
else filepath = path.resolve(context, "./tsconfig.json");
if (!path.isAbsolute(filepath)) filepath = path.resolve(context, filepath);
return fs.existsSync(filepath);
} catch (err) {
return false;
}
};
function cloneDeepOptions(options) {
const excludeKeys = new Set(["manifest", "async"]);
const cache = /* @__PURE__ */ new WeakMap();
function sanitize(val, key) {
if (key !== void 0 && excludeKeys.has(key) || typeof val === "function") return false;
if (key === "extractThirdParty" && Array.isArray(val)) return val.map(String);
if (Array.isArray(val)) {
if (cache.has(val)) return cache.get(val);
const out = [];
cache.set(val, out);
val.forEach((v, i) => out.push(sanitize(v, String(i))));
return out;
}
if (val !== null && typeof val === "object" && Object.getPrototypeOf(val) === Object.prototype) {
const obj = val;
if (cache.has(obj)) return cache.get(obj);
const out = {};
cache.set(obj, out);
for (const [k, v] of Object.entries(obj)) out[k] = sanitize(v, k);
return out;
}
return val;
}
return structuredClone(sanitize(options));
}
const getEnvHeaders = () => {
const headersStr = getProcessEnv()["MF_ENV_HEADERS"];
if (!headersStr || headersStr === "undefined") return {};
try {
return { ...JSON.parse(headersStr) };
} catch {
return {};
}
};
const createDispatcherFromFamily = (family) => {
if (!family) return void 0;
if (dispatcherCache.has(family)) return dispatcherCache.get(family);
try {
const dispatcher = new Agent({ connect: { family } });
dispatcherCache.set(family, dispatcher);
return dispatcher;
} catch {}
};
const toHeaderRecord = (headers) => {
const out = {};
headers.forEach((value, key) => {
out[key.toLowerCase()] = value;
});
return out;
};
async function nativeFetch(url, config) {
const controller = new AbortController();
const timeoutMs = config?.timeout ?? 6e4;
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
const headers = {
...getEnvHeaders(),
...config?.headers ?? {}
};
const dispatcher = config?.dispatcher ?? createDispatcherFromFamily(config?.family);
try {
const resp = await fetch(url, {
headers,
signal: controller.signal,
...dispatcher ? { dispatcher } : {},
...config?.agent ? { agent: config.agent } : {}
});
const headerRecord = toHeaderRecord(resp.headers);
if (!resp.ok) throw new Error(`Request failed with status ${resp.status}`);
if (config?.responseType === "arraybuffer") return {
data: await resp.arrayBuffer(),
headers: headerRecord,
status: resp.status
};
return {
data: (resp.headers.get("content-type") || "").includes("application/json") || url.endsWith(".json") ? await resp.json() : await resp.text(),
headers: headerRecord,
status: resp.status
};
} finally {
clearTimeout(timeoutId);
}
}
//#endregion
//#region src/core/configurations/remotePlugin.ts
const defaultOptions = {
tsConfigPath: "./tsconfig.json",
typesFolder: "@mf-types",
compiledTypesFolder: "compiled-types",
hostRemoteTypesFolder: "@mf-types",
deleteTypesFolder: true,
additionalFilesToCompile: [],
compilerInstance: "tsc",
compileInChildProcess: false,
implementation: "",
generateAPITypes: false,
context: process.cwd(),
abortOnError: true,
extractRemoteTypes: false,
extractThirdParty: false,
outputDir: "",
deleteTsConfig: true
};
function getEffectiveRootDir(parsedCommandLine) {
const compilerOptions = parsedCommandLine.options;
if (compilerOptions.rootDir) return compilerOptions.rootDir;
const files = parsedCommandLine.fileNames;
if (files.length > 0) return files.map((file) => dirname(file)).reduce((commonPath, fileDir) => {
while (!fileDir.startsWith(commonPath)) commonPath = dirname(commonPath);
return commonPath;
}, files[0]);
if (parsedCommandLine.projectReferences.length) {
const relativeReferences = parsedCommandLine.projectReferences.filter((reference) => !isAbsolute(reference.originalPath ?? reference.path));
const referencesForRoot = relativeReferences.length ? relativeReferences : parsedCommandLine.projectReferences;
return referencesForRoot.map((reference) => dirname(reference.path)).reduce((commonPath, filePath) => {
while (!filePath.startsWith(commonPath)) commonPath = dirname(commonPath);
return commonPath;
}, dirname(referencesForRoot[0].path));
}
throw new Error("Can not get effective rootDir, please set compilerOptions.rootDir !");
}
const getDependentFiles = (rootFiles, configContent, rootDir) => {
const dependentFiles = typescript.createProgram(rootFiles, configContent.options).getSourceFiles().map((file) => file.fileName).filter((file) => !file.endsWith(".d.ts") && file.startsWith(rootDir));
return dependentFiles.length ? dependentFiles : rootFiles;
};
const readTsConfig = ({ tsConfigPath, typesFolder, compiledTypesFolder, context, additionalFilesToCompile, outputDir }, mapComponentsToExpose) => {
const resolvedTsConfigPath = resolve(context, tsConfigPath);
const readResult = typescript.readConfigFile(resolvedTsConfigPath, typescript.sys.readFile);
if (readResult.error) throw new Error(readResult.error.messageText.toString());
const rawTsConfigJson = readResult.config;
const configContent = typescript.parseJsonConfigFileContent(rawTsConfigJson, typescript.sys, dirname(resolvedTsConfigPath));
const rootDir = getEffectiveRootDir(configContent);
const outDir = resolve(context, outputDir || configContent.options.outDir || "dist", typesFolder, compiledTypesFolder);
const defaultCompilerOptions = {
rootDir,
emitDeclarationOnly: true,
noEmit: false,
declaration: true,
outDir
};
rawTsConfigJson.compilerOptions = rawTsConfigJson.compilerOptions || {};
rawTsConfigJson.compilerOptions = {
incremental: true,
tsBuildInfoFile: resolve(context, "node_modules/.cache/mf-types/.tsbuildinfo"),
...rawTsConfigJson.compilerOptions,
...defaultCompilerOptions
};
const { paths, baseUrl, ...restCompilerOptions } = rawTsConfigJson.compilerOptions || {};
rawTsConfigJson.compilerOptions = restCompilerOptions;
const outDirWithoutTypesFolder = resolve(context, outputDir || configContent.options.outDir || "dist");
const excludeExtensions = [".mdx", ".md"];
const filesToCompile = [...getDependentFiles([...Object.values(mapComponentsToExpose), ...additionalFilesToCompile].filter((filename) => !excludeExtensions.some((ext) => filename.endsWith(ext))), configContent, rootDir), ...configContent.fileNames.filter((filename) => filename.endsWith(".d.ts") && !filename.startsWith(outDirWithoutTypesFolder))];
rawTsConfigJson.include = [];
rawTsConfigJson.files = [...new Set(filesToCompile)];
rawTsConfigJson.exclude = [];
"references" in rawTsConfigJson && delete rawTsConfigJson.references;
rawTsConfigJson.extends = resolvedTsConfigPath;
rawTsConfigJson.compilerOptions.declarationDir = outDir;
return rawTsConfigJson;
};
const TS_EXTENSIONS = [
"ts",
"tsx",
"vue",
"svelte"
];
const resolveWithExtension = (exposedPath, context) => {
if (extname(exposedPath)) return resolve(context, exposedPath);
for (const extension of TS_EXTENSIONS) {
const exposedPathWithExtension = resolve(context, `${exposedPath}.${extension}`);
if (existsSync(exposedPathWithExtension)) return exposedPathWithExtension;
}
};
const resolveExposes = (remoteOptions) => {
return utils.parseOptions(remoteOptions.moduleFederationConfig.exposes || {}, (item, key) => ({
exposePath: Array.isArray(item) ? item[0] : item,
key
}), (item, key) => ({
exposePath: Array.isArray(item.import) ? item.import[0] : item.import[0],
key
})).reduce((accumulator, item) => {
const { exposePath, key } = item[1];
accumulator[key] = resolveWithExtension(exposePath, remoteOptions.context) || resolveWithExtension(join(exposePath, "index"), remoteOptions.context) || exposePath;
return accumulator;
}, {});
};
const retrieveRemoteConfig = (options) => {
validateOptions(options);
const remoteOptions = {
...defaultOptions,
...options
};
const mapComponentsToExpose = resolveExposes(remoteOptions);
const tsConfig = readTsConfig(remoteOptions, mapComponentsToExpose);
if (tsConfig.compilerOptions.incremental && tsConfig.compilerOptions.tsBuildInfoFile && options.deleteTypesFolder !== true) remoteOptions.deleteTypesFolder = false;
return {
tsConfig,
mapComponentsToExpose,
remoteOptions
};
};
//#endregion
//#region src/core/lib/generateTypes.ts
async function generateTypes(options) {
return new (getDTSManagerConstructor(options.remote?.implementation))(options).generateTypes();
}
//#endregion
//#region src/core/rpc/types.ts
let RpcGMCallTypes = /* @__PURE__ */ function(RpcGMCallTypes) {
RpcGMCallTypes["CALL"] = "mf_call";
RpcGMCallTypes["RESOLVE"] = "mf_resolve";
RpcGMCallTypes["REJECT"] = "mf_reject";
RpcGMCallTypes["EXIT"] = "mf_exit";
return RpcGMCallTypes;
}({});
//#endregion
//#region src/core/rpc/expose-rpc.ts
function exposeRpc(fn) {
const sendMessage = (message) => new Promise((resolve, reject) => {
if (!process$1.send) reject(/* @__PURE__ */ new Error(`Process ${process$1.pid} doesn't have IPC channels`));
else if (!process$1.connected) reject(/* @__PURE__ */ new Error(`Process ${process$1.pid} doesn't have open IPC channels`));
else process$1.send(message, void 0, void 0, (error) => {
if (error) reject(error);
else resolve(void 0);
});
});
const handleMessage = async (message) => {
if (message.type === RpcGMCallTypes.CALL) {
if (!process$1.send) return;
let value, error;
try {
value = await fn(...message.args);
} catch (fnError) {
error = fnError;
}
try {
if (error) await sendMessage({
type: RpcGMCallTypes.REJECT,
id: message.id,
error
});
else await sendMessage({
type: RpcGMCallTypes.RESOLVE,
id: message.id,
value
});
} catch (sendError) {
if (error) {
if (error instanceof Error) console.error(error);
}
console.error(sendError);
}
}
};
process$1.on("message", handleMessage);
}
//#endregion
export { retrieveMfTypesPath as _, cloneDeepOptions as a, ModuleFederationDevServer as b, isTSProject as c, DTSManager as d, HOST_API_TYPES_FILE_NAME as f, retrieveTypesZipPath as g, retrieveHostConfig as h, retrieveRemoteConfig as i, retrieveTypesAssetsInfo as l, REMOTE_API_TYPES_FILE_NAME as m, RpcGMCallTypes as n, getDTSManagerConstructor as o, REMOTE_ALIAS_IDENTIFIER as p, generateTypes as r, isDebugMode as s, exposeRpc as t, validateOptions as u, retrieveOriginalOutDir as v, __exportAll as x, createHttpServer as y };
const require_Action = require('./Action-CzhPMw2i.js');
const require_Broker = require('./Broker-CaenCqdn.js');
let fs = require("fs");
fs = require_Action.__toESM(fs);
let url = require("url");
let path = require("path");
path = require_Action.__toESM(path);
let fs_promises = require("fs/promises");
let _module_federation_managers = require("@module-federation/managers");
let typescript = require("typescript");
typescript = require_Action.__toESM(typescript);
let _module_federation_sdk = require("@module-federation/sdk");
let ansi_colors = require("ansi-colors");
ansi_colors = require_Action.__toESM(ansi_colors);
let undici = require("undici");
let _module_federation_third_party_dts_extractor = require("@module-federation/third-party-dts-extractor");
let adm_zip = require("adm-zip");
adm_zip = require_Action.__toESM(adm_zip);
let crypto = require("crypto");
crypto = require_Action.__toESM(crypto);
let _module_federation_error_codes = require("@module-federation/error-codes");
let _module_federation_error_codes_node = require("@module-federation/error-codes/node");
let child_process = require("child_process");
let util = require("util");
util = require_Action.__toESM(util);
let isomorphic_ws = require("isomorphic-ws");
isomorphic_ws = require_Action.__toESM(isomorphic_ws);
let http = require("http");
http = require_Action.__toESM(http);
let process$1 = require("process");
process$1 = require_Action.__toESM(process$1);
//#region src/server/message/Action/AddPublisher.ts
var AddPublisherAction = class extends require_Action.Action {
constructor(payload) {
super({ payload }, require_Action.ActionKind.ADD_PUBLISHER);
}
};
//#endregion
//#region src/server/message/Action/AddSubscriber.ts
var AddSubscriberAction = class extends require_Action.Action {
constructor(payload) {
super({ payload }, require_Action.ActionKind.ADD_SUBSCRIBER);
}
};
//#endregion
//#region src/server/message/Action/ExitSubscriber.ts
var ExitSubscriberAction = class extends require_Action.Action {
constructor(payload) {
super({ payload }, require_Action.ActionKind.EXIT_SUBSCRIBER);
}
};
//#endregion
//#region src/server/message/Action/ExitPublisher.ts
var ExitPublisherAction = class extends require_Action.Action {
constructor(payload) {
super({ payload }, require_Action.ActionKind.EXIT_PUBLISHER);
}
};
//#endregion
//#region src/server/message/Action/NotifyWebClient.ts
var NotifyWebClientAction = class extends require_Action.Action {
constructor(payload) {
super({ payload }, require_Action.ActionKind.NOTIFY_WEB_CLIENT);
}
};
//#endregion
//#region src/server/message/Action/UpdatePublisher.ts
var UpdatePublisherAction = class extends require_Action.Action {
constructor(payload) {
super({ payload }, require_Action.ActionKind.UPDATE_PUBLISHER);
}
};
//#endregion
//#region src/server/broker/createBroker.ts
const __filename$1 = (0, url.fileURLToPath)(require("url").pathToFileURL(__filename).href);
const __dirname$1 = path.default.dirname(__filename$1);
function createBroker() {
const sub = (0, child_process.fork)(path.default.resolve(__dirname$1, "./start-broker.js"), [], {
detached: true,
stdio: "ignore",
env: process.env
});
sub.send("start");
sub.unref();
return sub;
}
//#endregion
//#region src/server/DevServer.ts
var ModuleFederationDevServer = class {
constructor(ctx) {
this._publishWebSocket = null;
this._subscriberWebsocketMap = {};
this._reconnect = true;
this._reconnectTimes = 0;
this._isConnected = false;
this._isReconnecting = false;
this._updateCallback = () => Promise.resolve(void 0);
const { name, remotes, remoteTypeTarPath, updateCallback } = ctx;
this._ip = require_Broker.getIPV4();
this._name = name;
this._remotes = remotes;
this._remoteTypeTarPath = remoteTypeTarPath;
this._updateCallback = updateCallback;
this._stopWhenSIGTERMOrSIGINT();
this._handleUnexpectedExit();
this._connectPublishToServer();
}
_connectPublishToServer() {
if (!this._reconnect) return;
require_Broker.fileLog(`Publisher:${this._name} Trying to connect to ws://${this._ip}:${require_Broker.Broker.DEFAULT_WEB_SOCKET_PORT}...`, require_Action.MF_SERVER_IDENTIFIER, "info");
this._publishWebSocket = new isomorphic_ws.default(`ws://${this._ip}:${require_Broker.Broker.DEFAULT_WEB_SOCKET_PORT}?WEB_SOCKET_CONNECT_MAGIC_ID=${require_Broker.Broker.WEB_SOCKET_CONNECT_MAGIC_ID}`);
this._publishWebSocket.on("open", () => {
require_Broker.fileLog(`Current pid: ${process.pid}, publisher:${this._name} connected to ws://${this._ip}:${require_Broker.Broker.DEFAULT_WEB_SOCKET_PORT}, starting service...`, require_Action.MF_SERVER_IDENTIFIER, "info");
this._isConnected = true;
const addPublisherAction = new AddPublisherAction({
name: this._name,
ip: this._ip,
remoteTypeTarPath: this._remoteTypeTarPath
});
this._publishWebSocket?.send(JSON.stringify(addPublisherAction));
this._connectSubscribers();
});
this._publishWebSocket.on("message", async (message) => {
try {
const parsedMessage = JSON.parse(message.toString());
if (parsedMessage.type === "Log") {
if (parsedMessage.kind === require_Broker.LogKind.BrokerExitLog) {
require_Broker.fileLog(`Receive broker exit signal, ${this._name} service will exit...`, require_Action.MF_SERVER_IDENTIFIER, "warn");
this._exit();
}
}
if (parsedMessage.type === "API") {
if (parsedMessage.kind === require_Broker.APIKind.FETCH_TYPES) {
const { payload: { remoteInfo } } = parsedMessage;
require_Broker.fileLog(`${this._name} Receive broker FETCH_TYPES, payload as follows: ${JSON.stringify(remoteInfo, null, 2)}.`, require_Action.MF_SERVER_IDENTIFIER, "info");
await this.fetchDynamicRemoteTypes({ remoteInfo });
}
}
} catch (err) {
console.error(err);
const exitPublisher = new ExitPublisherAction({
name: this._name,
ip: this._ip
});
const exitSubscriber = new ExitSubscriberAction({
name: this._name,
ip: this._ip,
publishers: this._remotes.map((remote) => ({
name: remote.name,
ip: remote.ip
}))
});
this._publishWebSocket?.send(JSON.stringify(exitPublisher));
this._publishWebSocket?.send(JSON.stringify(exitSubscriber));
require_Broker.fileLog("Parse messages error, ModuleFederationDevServer will exit...", require_Action.MF_SERVER_IDENTIFIER, "fatal");
this._exit();
}
});
this._publishWebSocket.on("close", (code) => {
require_Broker.fileLog(`Connection closed with code ${code}.`, require_Action.MF_SERVER_IDENTIFIER, "warn");
this._publishWebSocket && this._publishWebSocket.close();
this._publishWebSocket = null;
if (!this._reconnect) return;
const reconnectTime = require_Broker.fib(++this._reconnectTimes);
require_Broker.fileLog(`start reconnecting to server after ${reconnectTime}s.`, require_Action.MF_SERVER_IDENTIFIER, "info");
setTimeout(() => this._connectPublishToServer(), reconnectTime * 1e3);
});
this._publishWebSocket.on("error", this._tryCreateBackgroundBroker.bind(this));
}
_connectSubscriberToServer(remote) {
const { name, ip } = remote;
require_Broker.fileLog(`remote module:${name} trying to connect to ws://${ip}:${require_Broker.Broker.DEFAULT_WEB_SOCKET_PORT}...`, require_Action.MF_SERVER_IDENTIFIER, "info");
const identifier = require_Broker.getIdentifier({
name,
ip
});
this._subscriberWebsocketMap[identifier] = new isomorphic_ws.default(`ws://${ip}:${require_Broker.Broker.DEFAULT_WEB_SOCKET_PORT}?WEB_SOCKET_CONNECT_MAGIC_ID=${require_Broker.Broker.WEB_SOCKET_CONNECT_MAGIC_ID}`);
this._subscriberWebsocketMap[identifier].on("open", () => {
require_Broker.fileLog(`Current pid: ${process.pid} remote module: ${name} connected to ws://${ip}:${require_Broker.Broker.DEFAULT_WEB_SOCKET_PORT}, starting service...`, require_Action.MF_SERVER_IDENTIFIER, "info");
const addSubscriber = new AddSubscriberAction({
name: this._name,
ip: this._ip,
publishers: [{
name,
ip
}]
});
this._subscriberWebsocketMap[identifier].send(JSON.stringify(addSubscriber));
});
this._subscriberWebsocketMap[identifier].on("message", async (message) => {
try {
const parsedMessage = JSON.parse(message.toString());
if (parsedMessage.type === "Log") {
if (parsedMessage.kind === require_Broker.LogKind.BrokerExitLog) {
require_Broker.fileLog(`${identifier}'s Server exit, thus ${identifier} will no longer has reload ability.`, require_Action.MF_SERVER_IDENTIFIER, "warn");
this._exit();
}
}
if (parsedMessage.type === "API") {
if (parsedMessage.kind === require_Broker.APIKind.UPDATE_SUBSCRIBER) {
const { payload: { updateKind, updateSourcePaths, name: subscribeName, remoteTypeTarPath, updateMode } } = parsedMessage;
await this._updateSubscriber({
remoteTypeTarPath,
name: subscribeName,
updateKind,
updateMode,
updateSourcePaths
});
}
}
} catch (err) {
console.error(err);
const exitSubscriber = new ExitSubscriberAction({
name: this._name,
ip: this._ip,
publishers: [{
name,
ip
}]
});
this._subscriberWebsocketMap[identifier].send(JSON.stringify(exitSubscriber));
require_Broker.fileLog(`${identifier} exit,
error: ${err instanceof Error ? err.toString() : JSON.stringify(err)}
`, require_Action.MF_SERVER_IDENTIFIER, "warn");
}
});
this._subscriberWebsocketMap[identifier].on("close", (code) => {
require_Broker.fileLog(`Connection closed with code ${code}.`, require_Action.MF_SERVER_IDENTIFIER, "warn");
this._subscriberWebsocketMap[identifier]?.close();
delete this._subscriberWebsocketMap[identifier];
});
this._subscriberWebsocketMap[identifier].on("error", (err) => {
if ("code" in err && err.code === "ETIMEDOUT") require_Broker.fileLog(`Can not connect ${JSON.stringify(remote)}, please make sure this remote is started locally.`, require_Action.MF_SERVER_IDENTIFIER, "warn");
else console.error(err);
this._subscriberWebsocketMap[identifier]?.close();
delete this._subscriberWebsocketMap[identifier];
});
}
_connectSubscribers() {
this._remotes.forEach((remote) => {
this._connectSubscriberToServer(remote);
});
}
async _updateSubscriber(options) {
const { updateMode, updateKind, updateSourcePaths, name, remoteTypeTarPath, remoteInfo } = options;
require_Broker.fileLog(`[_updateSubscriber] run, options: ${JSON.stringify(options, null, 2)}`, require_Action.MF_SERVER_IDENTIFIER, "warn");
if (updateMode === require_Action.UpdateMode.PASSIVE && updateSourcePaths.includes(this._name)) {
require_Broker.fileLog(`[_updateSubscriber] run, updateSourcePaths:${updateSourcePaths} includes ${this._name}, update ignore!`, require_Action.MF_SERVER_IDENTIFIER, "warn");
return;
}
if (updateSourcePaths.slice(-1)[0] === this._name) {
require_Broker.fileLog(`[_updateSubscriber] run, updateSourcePaths:${updateSourcePaths} ends is ${this._name}, update ignore!`, require_Action.MF_SERVER_IDENTIFIER, "warn");
return;
}
require_Broker.fileLog(`[_updateSubscriber] run, updateSourcePaths:${updateSourcePaths}, current module:${this._name}, update start...`, require_Action.MF_SERVER_IDENTIFIER, "info");
await this._updateCallback({
name,
updateMode,
updateKind,
updateSourcePaths,
remoteTypeTarPath,
remoteInfo
});
const newUpdateSourcePaths = updateSourcePaths.concat(this._name);
const updatePublisher = new UpdatePublisherAction({
name: this._name,
ip: this._ip,
updateMode: require_Action.UpdateMode.PASSIVE,
updateKind,
updateSourcePaths: newUpdateSourcePaths,
remoteTypeTarPath: this._remoteTypeTarPath
});
require_Broker.fileLog(`[_updateSubscriber] run, updateSourcePaths:${newUpdateSourcePaths}, update publisher ${this._name} start...`, require_Action.MF_SERVER_IDENTIFIER, "info");
this._publishWebSocket?.send(JSON.stringify(updatePublisher));
}
_tryCreateBackgroundBroker(err) {
if (!((err?.code === "ECONNREFUSED" || err?.code === "ETIMEDOUT") && err.port === require_Broker.Broker.DEFAULT_WEB_SOCKET_PORT)) {
require_Broker.fileLog(`websocket error: ${err.stack}`, require_Action.MF_SERVER_IDENTIFIER, "fatal");
return;
}
require_Broker.fileLog(`Failed to connect to ws://${this._ip}:${require_Broker.Broker.DEFAULT_WEB_SOCKET_PORT}...`, require_Action.MF_SERVER_IDENTIFIER, "fatal");
this._isReconnecting = true;
setTimeout(() => {
this._isReconnecting = false;
if (this._reconnect === false) return;
require_Broker.fileLog("Creating new background broker...", require_Action.MF_SERVER_IDENTIFIER, "warn");
createBroker().on("message", (message) => {
if (message === "ready") {
require_Broker.fileLog("background broker started.", require_Action.MF_SERVER_IDENTIFIER, "info");
this._reconnectTimes = 1;
if (process.send) process.send("ready");
}
});
}, Math.ceil(100 * Math.random()));
}
_stopWhenSIGTERMOrSIGINT() {
process.on("SIGTERM", () => {
require_Broker.fileLog(`Process(${process.pid}) SIGTERM, ModuleFederationDevServer will exit...`, require_Action.MF_SERVER_IDENTIFIER, "warn");
this._exit();
});
process.on("SIGINT", () => {
require_Broker.fileLog(`Process(${process.pid}) SIGINT, ModuleFederationDevServer will exit...`, require_Action.MF_SERVER_IDENTIFIER, "warn");
this._exit();
});
}
_handleUnexpectedExit() {
process.on("unhandledRejection", (error) => {
if (this._isReconnecting) return;
console.error("Unhandled Rejection Error: ", error);
require_Broker.fileLog(`Process(${process.pid}) unhandledRejection, garfishModuleServer will exit...`, require_Action.MF_SERVER_IDENTIFIER, "error");
this._exit();
});
process.on("uncaughtException", (error) => {
if (this._isReconnecting) return;
console.error("Unhandled Exception Error: ", error);
require_Broker.fileLog(`Process(${process.pid}) uncaughtException, garfishModuleServer will exit...`, require_Action.MF_SERVER_IDENTIFIER, "error");
this._exit();
});
}
_exit() {
this._reconnect = false;
if (this._publishWebSocket) {
const exitPublisher = new ExitPublisherAction({
name: this._name,
ip: this._ip
});
this._publishWebSocket.send(JSON.stringify(exitPublisher));
this._publishWebSocket.on("message", (message) => {
const parsedMessage = JSON.parse(message.toString());
require_Broker.fileLog(`[${parsedMessage.kind}]: ${JSON.stringify(parsedMessage)}`, require_Action.MF_SERVER_IDENTIFIER, "info");
});
}
if (this._publishWebSocket) {
this._publishWebSocket.close();
this._publishWebSocket = null;
}
process.exit(0);
}
exit() {
this._exit();
}
update(options) {
if (!this._publishWebSocket || !this._isConnected) return;
const { updateKind, updateMode, updateSourcePaths, clientName } = options;
require_Broker.fileLog(`update run, ${this._name} module update, updateKind: ${updateKind}, updateMode: ${updateMode}, updateSourcePaths: ${updateSourcePaths}`, require_Action.MF_SERVER_IDENTIFIER, "info");
if (updateKind === require_Broker.UpdateKind.RELOAD_PAGE) {
const notifyWebClient = new NotifyWebClientAction({
name: clientName || this._name,
updateMode
});
this._publishWebSocket.send(JSON.stringify(notifyWebClient));
return;
}
const updatePublisher = new UpdatePublisherAction({
name: this._name,
ip: this._ip,
updateMode,
updateKind,
updateSourcePaths: [this._name],
remoteTypeTarPath: this._remoteTypeTarPath
});
this._publishWebSocket.send(JSON.stringify(updatePublisher));
}
async fetchDynamicRemoteTypes(options) {
const { remoteInfo, once } = options;
const updateMode = require_Action.UpdateMode.PASSIVE;
const updateKind = require_Broker.UpdateKind.UPDATE_TYPE;
require_Broker.fileLog(`fetchDynamicRemoteTypes: remoteInfo: ${JSON.stringify(remoteInfo)}`, require_Action.MF_SERVER_IDENTIFIER, "info");
await this._updateCallback({
name: this._name,
updateMode,
updateKind,
updateSourcePaths: [],
remoteTypeTarPath: "",
remoteInfo,
once
});
const updatePublisher = new UpdatePublisherAction({
name: this._name,
ip: this._ip,
updateMode,
updateKind,
updateSourcePaths: [this._name],
remoteTypeTarPath: this._remoteTypeTarPath
});
this._publishWebSocket.send(JSON.stringify(updatePublisher));
}
};
//#endregion
//#region src/server/createHttpServer.ts
async function createHttpServer(options) {
const { typeTarPath } = options;
const freeport = await require_Broker.getFreePort();
const server = http.default.createServer((req, res) => {
if ((req.url?.split("?")[0] ?? "/") === `/${require_Action.DEFAULT_TAR_NAME}`) {
res.statusCode = 200;
res.setHeader("Content-Type", "application/x-gzip");
if (req.method === "HEAD") {
res.end();
return;
}
const stream = fs.default.createReadStream(typeTarPath);
stream.on("error", () => {
if (!res.headersSent) res.statusCode = 500;
res.end();
});
res.on("close", () => {
stream.destroy();
});
stream.pipe(res);
return;
}
res.statusCode = 404;
res.end();
});
server.listen(freeport);
return {
server,
serverAddress: `http://${require_Broker.getIPV4()}:${freeport}`
};
}
//#endregion
//#region src/core/lib/typeScriptCompiler.ts
const STARTS_WITH_SLASH = /^\//;
const DEFINITION_FILE_EXTENSION = ".d.ts";
const retrieveMfTypesPath = (tsConfig, remoteOptions) => (0, path.normalize)(tsConfig.compilerOptions.outDir.replace(remoteOptions.compiledTypesFolder, ""));
const retrieveOriginalOutDir = (tsConfig, remoteOptions) => (0, path.normalize)(tsConfig.compilerOptions.outDir.replace(remoteOptions.compiledTypesFolder, "").replace(remoteOptions.typesFolder, ""));
const retrieveMfAPITypesPath = (tsConfig, remoteOptions) => (0, path.join)(retrieveOriginalOutDir(tsConfig, remoteOptions), `${remoteOptions.typesFolder}.d.ts`);
function writeTempTsConfig(tsConfig, context, name, cwd) {
const createHash = (contents) => {
return crypto.default.createHash("md5").update(contents).digest("hex");
};
const hash = createHash(`${JSON.stringify(tsConfig)}${name}${Date.now()}`);
const tempTsConfigJsonPath = (0, path.resolve)(cwd ?? context, "node_modules", _module_federation_sdk.TEMP_DIR, `tsconfig.${hash}.json`);
(0, fs.mkdirSync)((0, path.dirname)(tempTsConfigJsonPath), { recursive: true });
(0, fs.writeFileSync)(tempTsConfigJsonPath, JSON.stringify(tsConfig, null, 2));
return tempTsConfigJsonPath;
}
const removeExt = (f) => {
const vueExt = ".vue";
const ext = (0, path.extname)(f);
if (ext === vueExt) return f;
const regexPattern = new RegExp(`\\${ext}$`);
return f.replace(regexPattern, "");
};
function getExposeKey(options) {
const { filePath, rootDir, outDir, mapExposeToEntry } = options;
return mapExposeToEntry[(0, path.relative)(outDir, filePath.replace(new RegExp(`\\.d.ts$`), ""))];
}
const processTypesFile = async (options) => {
const { outDir, filePath, rootDir, cb, mapExposeToEntry, mfTypePath } = options;
if (!(0, fs.existsSync)(filePath)) return;
if ((await (0, fs_promises.stat)(filePath)).isDirectory()) {
const files = await (0, fs_promises.readdir)(filePath);
await Promise.all(files.map((file) => processTypesFile({
...options,
filePath: (0, path.join)(filePath, file)
})));
} else if (filePath.endsWith(".d.ts")) {
const exposeKey = getExposeKey({
filePath,
rootDir,
outDir,
mapExposeToEntry
});
if (exposeKey) {
const mfeTypeEntry = (0, path.join)(mfTypePath, `${exposeKey === "." ? "index" : exposeKey}${DEFINITION_FILE_EXTENSION}`);
const mfeTypeEntryDirectory = (0, path.dirname)(mfeTypeEntry);
const relativePathToOutput = (0, path.relative)(mfeTypeEntryDirectory, filePath).replace(DEFINITION_FILE_EXTENSION, "").replace(STARTS_WITH_SLASH, "").split(path.sep).join("/");
(0, fs.mkdirSync)(mfeTypeEntryDirectory, { recursive: true });
await (0, fs_promises.writeFile)(mfeTypeEntry, `export * from './${relativePathToOutput}';\nexport { default } from './${relativePathToOutput}';`);
}
cb(await (0, fs_promises.readFile)(filePath, "utf8"));
}
};
const getPMFromUserAgent = () => {
const userAgent = process.env["npm_config_user_agent"];
if (userAgent == null) return "null";
return userAgent.split("/")[0];
};
const resolvePackageManagerExecutable = () => {
switch (getPMFromUserAgent()) {
case "yarn": return "yarn";
default: return "npx";
}
};
const splitCommandArgs = (value) => {
const args = [];
let current = "";
let quote = null;
let escaped = false;
for (const char of value) {
if (escaped) {
current += char;
escaped = false;
continue;
}
if (char === "\\") {
escaped = true;
continue;
}
if (quote) {
if (char === quote) quote = null;
else current += char;
continue;
}
if (char === "\"" || char === "'") {
quote = char;
continue;
}
if (char.trim() === "") {
if (current) {
args.push(current);
current = "";
}
continue;
}
current += char;
}
if (current) args.push(current);
return args;
};
const formatCommandForDisplay = (executable, args) => {
const formatArg = (arg) => {
if (/[\s'"]/.test(arg)) return JSON.stringify(arg);
return arg;
};
return [executable, ...args].map(formatArg).join(" ");
};
const compileTs = async (mapComponentsToExpose, tsConfig, remoteOptions) => {
if (!Object.keys(mapComponentsToExpose).length) return;
const { compilerOptions } = tsConfig;
const tempTsConfigJsonPath = writeTempTsConfig(tsConfig, remoteOptions.context, remoteOptions.moduleFederationConfig.name || "mf", typeof remoteOptions.moduleFederationConfig.dts !== "boolean" ? remoteOptions.moduleFederationConfig.dts?.cwd ?? void 0 : void 0);
require_Broker.logger.debug(`tempTsConfigJsonPath: ${tempTsConfigJsonPath}`);
try {
const mfTypePath = retrieveMfTypesPath(tsConfig, remoteOptions);
const thirdPartyExtractor = new _module_federation_third_party_dts_extractor.ThirdPartyExtractor({
destDir: (0, path.resolve)(mfTypePath, "node_modules"),
context: remoteOptions.context,
exclude: typeof remoteOptions.extractThirdParty === "object" ? remoteOptions.extractThirdParty.exclude : void 0
});
const execPromise = util.default.promisify(child_process.execFile);
const pmExecutable = resolvePackageManagerExecutable();
const compilerArgs = splitCommandArgs(remoteOptions.compilerInstance);
const cmdArgs = [
...compilerArgs.length > 0 ? compilerArgs : [remoteOptions.compilerInstance],
"--project",
tempTsConfigJsonPath
];
const cmd = formatCommandForDisplay(pmExecutable, cmdArgs);
try {
await execPromise(pmExecutable, cmdArgs, {
cwd: typeof remoteOptions.moduleFederationConfig.dts !== "boolean" ? remoteOptions.moduleFederationConfig.dts?.cwd ?? void 0 : void 0,
shell: process.platform === "win32"
});
} catch (err) {
if (compilerOptions.tsBuildInfoFile) try {
await (0, fs_promises.rm)(compilerOptions.tsBuildInfoFile);
} catch (e) {}
(0, _module_federation_error_codes_node.logAndReport)(_module_federation_error_codes.TYPE_001, _module_federation_error_codes.typeDescMap, { cmd }, (msg) => {
throw new Error(msg);
}, void 0);
}
const mapExposeToEntry = Object.fromEntries(Object.entries(mapComponentsToExpose).map(([exposed, filename]) => {
const normalizedFileName = (0, path.normalize)(filename);
let relativeFileName = "";
if ((0, path.isAbsolute)(normalizedFileName)) relativeFileName = (0, path.relative)(tsConfig.compilerOptions.rootDir, normalizedFileName);
else relativeFileName = (0, path.relative)(tsConfig.compilerOptions.rootDir, (0, path.resolve)(remoteOptions.context, normalizedFileName));
return [removeExt(relativeFileName), exposed];
}));
const cb = remoteOptions.extractThirdParty ? thirdPartyExtractor.collectPkgs.bind(thirdPartyExtractor) : () => void 0;
await processTypesFile({
outDir: compilerOptions.outDir,
filePath: compilerOptions.outDir,
rootDir: compilerOptions.rootDir,
mfTypePath,
cb,
mapExposeToEntry
});
if (remoteOptions.extractThirdParty) await thirdPartyExtractor.copyDts();
if (remoteOptions.deleteTsConfig) await (0, fs_promises.rm)(tempTsConfigJsonPath);
} catch (err) {
throw err;
}
};
//#endregion
//#region src/core/lib/archiveHandler.ts
const retrieveTypesZipPath = (mfTypesPath, remoteOptions) => (0, path.join)(mfTypesPath.replace(remoteOptions.typesFolder, ""), `${remoteOptions.typesFolder}.zip`);
const createTypesArchive = async (tsConfig, remoteOptions) => {
const mfTypesPath = retrieveMfTypesPath(tsConfig, remoteOptions);
const zip = new adm_zip.default();
zip.addLocalFolder(mfTypesPath);
return zip.writeZipPromise(retrieveTypesZipPath(mfTypesPath, remoteOptions));
};
const downloadErrorLogger = (destinationFolder, fileToDownload) => (reason) => {
throw {
...reason,
message: `Network error: Unable to download federated mocks for '${destinationFolder}' from '${fileToDownload}' because '${reason.message}'`
};
};
const retrieveTypesArchiveDestinationPath = (hostOptions, destinationFolder) => {
return (0, path.resolve)(hostOptions.context, hostOptions.typesFolder, destinationFolder);
};
const downloadTypesArchive = (hostOptions) => {
let retries = 0;
return async ([destinationFolder, fileToDownload]) => {
const destinationPath = retrieveTypesArchiveDestinationPath(hostOptions, destinationFolder);
while (retries++ < hostOptions.maxRetries) try {
const url = new URL(fileToDownload).href;
const response = await nativeFetch(url, {
responseType: "arraybuffer",
timeout: hostOptions.timeout,
family: hostOptions.family
}).catch(downloadErrorLogger(destinationFolder, url));
if (typeof response.headers?.["content-type"] === "string" && response.headers["content-type"].includes("text/html")) throw new Error(`${url} receives invalid content-type: ${response.headers["content-type"]}`);
try {
if (hostOptions.deleteTypesFolder) await (0, fs_promises.rm)(destinationPath, {
recursive: true,
force: true
});
} catch (error) {
require_Broker.fileLog(`Unable to remove types folder, ${error}`, "downloadTypesArchive", "error");
}
new adm_zip.default(Buffer.from(response.data)).extractAllTo(destinationPath, true);
require_Broker.fileLog(`zip.extractAllTo success destinationPath: ${destinationPath}; url: ${url}`, "downloadTypesArchive", "info");
return [destinationFolder, destinationPath];
} catch (error) {
require_Broker.fileLog(`Error during types archive download: ${error?.message || "unknown error"}`, "downloadTypesArchive", "error");
if (retries >= hostOptions.maxRetries) {
require_Broker.logger.error(`Failed to download types archive from "${fileToDownload}". Set FEDERATION_DEBUG=true for details.`);
if (hostOptions.abortOnError !== false) throw error;
return;
}
}
};
};
//#endregion
//#region src/core/configurations/hostPlugin.ts
const defaultOptions$1 = {
typesFolder: "@mf-types",
remoteTypesFolder: "@mf-types",
deleteTypesFolder: true,
maxRetries: 3,
implementation: "",
context: process.cwd(),
abortOnError: true,
consumeAPITypes: false,
runtimePkgs: [],
remoteTypeUrls: {},
timeout: 6e4,
typesOnBuild: false,
family: 0
};
const buildZipUrl = (hostOptions, url) => {
const remoteUrl = new URL(url, "file:");
remoteUrl.pathname = `${remoteUrl.pathname.split("/").slice(0, -1).join("/")}/${hostOptions.remoteTypesFolder}.zip`;
return remoteUrl.protocol === "file:" ? remoteUrl.pathname : remoteUrl.href;
};
const buildApiTypeUrl = (zipUrl) => {
if (!zipUrl) return;
return zipUrl.replace(".zip", ".d.ts");
};
const retrieveRemoteInfo = (options) => {
const { hostOptions, remoteAlias, remote } = options;
const { remoteTypeUrls } = hostOptions;
let decodedRemote = remote;
if (decodedRemote.startsWith(_module_federation_sdk.ENCODE_NAME_PREFIX)) decodedRemote = (0, _module_federation_sdk.decodeName)(decodedRemote, _module_federation_sdk.ENCODE_NAME_PREFIX);
const parsedInfo = (0, _module_federation_sdk.parseEntry)(decodedRemote, void 0, "@");
const url = "entry" in parsedInfo ? parsedInfo.entry : parsedInfo.name === decodedRemote ? decodedRemote : "";
let zipUrl = "";
let apiTypeUrl = "";
const name = parsedInfo.name || remoteAlias;
const remoteTypeUrl = typeof remoteTypeUrls === "object" && remoteTypeUrls[name];
if (remoteTypeUrl) {
zipUrl = remoteTypeUrl.zip;
apiTypeUrl = remoteTypeUrl.api;
}
const shouldResolveTypeUrlsByConvention = Boolean(url && !url.includes(_module_federation_sdk.MANIFEST_EXT));
if (!zipUrl && shouldResolveTypeUrlsByConvention) zipUrl = buildZipUrl(hostOptions, url);
if (!apiTypeUrl && zipUrl && (remoteTypeUrl || shouldResolveTypeUrlsByConvention)) apiTypeUrl = buildApiTypeUrl(zipUrl);
return {
name,
url,
zipUrl,
apiTypeUrl,
alias: remoteAlias
};
};
const resolveRemotes = (hostOptions) => {
const parsedOptions = _module_federation_managers.utils.parseOptions(hostOptions.moduleFederationConfig.remotes || {}, (item, key) => ({
remote: Array.isArray(item) ? item[0] : item,
key
}), (item, key) => ({
remote: Array.isArray(item.external) ? item.external[0] : item.external,
key
}));
const remoteTypeUrls = hostOptions.remoteTypeUrls ?? {};
if (typeof remoteTypeUrls !== "object") throw new Error("remoteTypeUrls must be consumed before resolveRemotes");
const remoteInfos = Object.keys(remoteTypeUrls).reduce((sum, remoteName) => {
const { zip, api, alias } = remoteTypeUrls[remoteName];
sum[alias] = {
name: remoteName,
url: "",
zipUrl: zip,
apiTypeUrl: api,
alias: alias || remoteName
};
return sum;
}, {});
return parsedOptions.reduce((accumulator, item) => {
const { key, remote } = item[1];
const res = retrieveRemoteInfo({
hostOptions,
remoteAlias: key,
remote
});
if (accumulator[key]) {
accumulator[key] = {
...accumulator[key],
url: res.url,
apiTypeUrl: accumulator[key].apiTypeUrl || res.apiTypeUrl
};
return accumulator;
}
accumulator[key] = res;
return accumulator;
}, remoteInfos);
};
const retrieveHostConfig = (options) => {
validateOptions(options);
const hostOptions = {
...defaultOptions$1,
...options
};
return {
hostOptions,
mapRemotesToDownload: resolveRemotes(hostOptions)
};
};
//#endregion
//#region src/core/constant.ts
const REMOTE_ALIAS_IDENTIFIER = "REMOTE_ALIAS_IDENTIFIER";
const REMOTE_API_TYPES_FILE_NAME = "apis.d.ts";
const HOST_API_TYPES_FILE_NAME = "index.d.ts";
//#endregion
//#region src/core/lib/DTSManager.ts
var DTSManager = class {
constructor(options) {
this.options = cloneDeepOptions(options);
this.runtimePkgs = [
"@module-federation/runtime",
"@module-federation/enhanced/runtime",
"@module-federation/runtime-tools"
];
this.loadedRemoteAPIAlias = /* @__PURE__ */ new Set();
this.remoteAliasMap = {};
this.extraOptions = options?.extraOptions || {};
this.updatedRemoteInfos = {};
}
generateAPITypes(mapComponentsToExpose) {
const exposePaths = /* @__PURE__ */ new Set();
const packageType = Object.keys(mapComponentsToExpose).reduce((sum, exposeKey) => {
const exposePath = path.default.join(REMOTE_ALIAS_IDENTIFIER, exposeKey).split(path.default.sep).join("/");
exposePaths.add(`'${exposePath}'`);
sum = `T extends '${exposePath}' ? typeof import('${exposePath}') :` + sum;
return sum;
}, "any;");
return `
export type RemoteKeys = ${[...exposePaths].join(" | ")};
type PackageType<T> = ${packageType}`;
}
async extractRemoteTypes(options) {
const { remoteOptions, tsConfig } = options;
if (!remoteOptions.extractRemoteTypes) return;
let hasRemotes = false;
const remotes = remoteOptions.moduleFederationConfig.remotes;
if (remotes) {
if (Array.isArray(remotes)) hasRemotes = Boolean(remotes.length);
else if (typeof remotes === "object") hasRemotes = Boolean(Object.keys(remotes).length);
}
const mfTypesPath = retrieveMfTypesPath(tsConfig, remoteOptions);
if (hasRemotes && this.options.host) try {
const { hostOptions } = retrieveHostConfig(this.options.host);
const remoteTypesFolder = path.default.resolve(hostOptions.context, hostOptions.typesFolder);
const targetDir = path.default.join(mfTypesPath, "node_modules");
if (fs.default.existsSync(remoteTypesFolder)) {
const targetFolder = path.default.resolve(remoteOptions.context, targetDir);
await (0, fs_promises.mkdir)(targetFolder, { recursive: true });
await (0, fs_promises.cp)(remoteTypesFolder, targetFolder, {
recursive: true,
force: true
});
}
} catch (err) {
if (this.options.host?.abortOnError === false) require_Broker.fileLog(`Unable to copy remote types, ${err}`, "extractRemoteTypes", "error");
else throw err;
}
}
async generateTypes() {
try {
const { options } = this;
if (!options.remote) throw new Error("options.remote is required if you want to generateTypes");
const { remoteOptions, tsConfig, mapComponentsToExpose } = retrieveRemoteConfig(options.remote);
if (!Object.keys(mapComponentsToExpose).length) return;
if (!tsConfig.files?.length) {
require_Broker.logger.info("No type files to compile, skip");
return;
}
if (tsConfig.compilerOptions.tsBuildInfoFile) try {
const tsBuildInfoFile = path.default.resolve(remoteOptions.context, tsConfig.compilerOptions.tsBuildInfoFile);
const mfTypesPath = retrieveMfTypesPath(tsConfig, remoteOptions);
if (!fs.default.existsSync(mfTypesPath)) fs.default.rmSync(tsBuildInfoFile, { force: true });
} catch (e) {}
await this.extractRemoteTypes({
remoteOptions,
tsConfig,
mapComponentsToExpose
});
await compileTs(mapComponentsToExpose, tsConfig, remoteOptions);
await createTypesArchive(tsConfig, remoteOptions);
let apiTypesPath = "";
if (remoteOptions.generateAPITypes) {
const apiTypes = this.generateAPITypes(mapComponentsToExpose);
apiTypesPath = retrieveMfAPITypesPath(tsConfig, remoteOptions);
fs.default.writeFileSync(apiTypesPath, apiTypes);
}
try {
if (remoteOptions.deleteTypesFolder) await (0, fs_promises.rm)(retrieveMfTypesPath(tsConfig, remoteOptions), {
recursive: true,
force: true
});
} catch (err) {
if (isDebugMode()) console.error(err);
}
require_Broker.logger.success("Federated types created correctly");
} catch (error) {
if (this.options.remote?.abortOnError === false) {
if (this.options.displayErrorInTerminal) require_Broker.logger.error(error);
} else throw error;
}
}
async requestRemoteManifest(remoteInfo, hostOptions) {
try {
if (!remoteInfo.url.includes(_module_federation_sdk.MANIFEST_EXT)) return remoteInfo;
if (remoteInfo.zipUrl) return remoteInfo;
const url = remoteInfo.url;
const manifestJson = (await nativeFetch(url, {
timeout: hostOptions.timeout,
family: hostOptions.family
})).data;
if (!manifestJson.metaData.types.zip) throw new Error(`Can not get ${remoteInfo.name}'s types archive url!`);
const addProtocol = (u) => {
if (u.startsWith("//")) return `https:${u}`;
return u;
};
let publicPath;
if ("publicPath" in manifestJson.metaData) publicPath = manifestJson.metaData.publicPath;
else {
const getPublicPath = new Function(manifestJson.metaData.getPublicPath);
if (manifestJson.metaData.getPublicPath.startsWith("function")) publicPath = getPublicPath()();
else publicPath = getPublicPath();
}
if (publicPath === "auto") publicPath = (0, _module_federation_sdk.inferAutoPublicPath)(remoteInfo.url);
const normalizedPublicPath = addProtocol(publicPath).endsWith("/") ? addProtocol(publicPath) : `${addProtocol(publicPath)}/`;
remoteInfo.zipUrl = new URL(manifestJson.metaData.types.zip, normalizedPublicPath).href;
if (!manifestJson.metaData.types.api) {
console.warn(`Can not get ${remoteInfo.name}'s api types url!`);
remoteInfo.apiTypeUrl = "";
return remoteInfo;
}
remoteInfo.apiTypeUrl = new URL(manifestJson.metaData.types.api, normalizedPublicPath).href;
return remoteInfo;
} catch (_err) {
require_Broker.fileLog(`fetch manifest failed, ${_err}, ${remoteInfo.name} will be ignored`, "requestRemoteManifest", "error");
return remoteInfo;
}
}
async consumeTargetRemotes(hostOptions, remoteInfo) {
if (!remoteInfo.zipUrl) throw new Error(`Can not get ${remoteInfo.name}'s types archive url!`);
return downloadTypesArchive(hostOptions)([remoteInfo.alias, remoteInfo.zipUrl]);
}
async downloadAPITypes(remoteInfo, destinationPath, hostOptions) {
const { apiTypeUrl } = remoteInfo;
if (!apiTypeUrl) return;
try {
let apiTypeFile = (await nativeFetch(apiTypeUrl, {
timeout: hostOptions.timeout,
family: hostOptions.family
})).data;
apiTypeFile = apiTypeFile.replaceAll(REMOTE_ALIAS_IDENTIFIER, remoteInfo.alias);
const filePath = path.default.join(destinationPath, REMOTE_API_TYPES_FILE_NAME);
fs.default.writeFileSync(filePath, apiTypeFile);
const existed = this.loadedRemoteAPIAlias.has(remoteInfo.alias);
this.loadedRemoteAPIAlias.add(remoteInfo.alias);
require_Broker.fileLog(`success`, "downloadAPITypes", "info");
return existed;
} catch (err) {
require_Broker.fileLog(`Unable to download "${remoteInfo.name}" api types, ${err}`, "downloadAPITypes", "error");
}
}
consumeAPITypes(hostOptions) {
const apiTypeFileName = path.default.join(hostOptions.context, hostOptions.typesFolder, HOST_API_TYPES_FILE_NAME);
try {
const existedFile = fs.default.readFileSync(apiTypeFileName, "utf-8");
new _module_federation_third_party_dts_extractor.ThirdPartyExtractor({ destDir: "" }).collectTypeImports(existedFile).forEach((existedImport) => {
const alias = existedImport.split("./").slice(1).join("./").replace("/apis.d.ts", "");
this.loadedRemoteAPIAlias.add(alias);
});
} catch (err) {}
if (!this.loadedRemoteAPIAlias.size) return;
const packageTypes = [];
const remoteKeys = [];
const importTypeStr = [...this.loadedRemoteAPIAlias].sort().map((alias, index) => {
const remoteKey = `RemoteKeys_${index}`;
const packageType = `PackageType_${index}`;
packageTypes.push(`T extends ${remoteKey} ? ${packageType}<T>`);
remoteKeys.push(remoteKey);
return `import type { PackageType as ${packageType},RemoteKeys as ${remoteKey} } from './${alias}/apis.d.ts';`;
}).join("\n");
const remoteKeysStr = `type RemoteKeys = ${remoteKeys.join(" | ")};`;
const packageTypesStr = `type PackageType<T, Y=any> = ${[...packageTypes, "Y"].join(" :\n")} ;`;
const runtimePkgs = /* @__PURE__ */ new Set();
[...this.runtimePkgs, ...hostOptions.runtimePkgs].forEach((pkg) => {
runtimePkgs.add(pkg);
});
const fileStr = `${importTypeStr}
${[...runtimePkgs].map((pkg) => {
return `declare module "${pkg}" {
${remoteKeysStr}
${packageTypesStr}
export function loadRemote<T extends RemoteKeys,Y>(packageName: T): Promise<PackageType<T, Y>>;
export function loadRemote<T extends string,Y>(packageName: T): Promise<PackageType<T, Y>>;
}`;
}).join("\n")}
`;
fs.default.writeFileSync(path.default.join(hostOptions.context, hostOptions.typesFolder, HOST_API_TYPES_FILE_NAME), fileStr);
}
async consumeArchiveTypes(options) {
const { hostOptions, mapRemotesToDownload } = retrieveHostConfig(options);
const downloadPromises = Object.entries(mapRemotesToDownload).map(async (item) => {
const remoteInfo = item[1];
if (!this.remoteAliasMap[remoteInfo.alias]) {
const requiredRemoteInfo = await this.requestRemoteManifest(remoteInfo, hostOptions);
this.remoteAliasMap[remoteInfo.alias] = requiredRemoteInfo;
}
return this.consumeTargetRemotes(hostOptions, this.remoteAliasMap[remoteInfo.alias]);
});
return {
hostOptions,
downloadPromisesResult: await Promise.allSettled(downloadPromises)
};
}
async consumeTypes() {
try {
const { options } = this;
if (!options.host) throw new Error("options.host is required if you want to consumeTypes");
const { mapRemotesToDownload } = retrieveHostConfig(options.host);
if (!Object.keys(mapRemotesToDownload).length) return;
const { downloadPromisesResult, hostOptions } = await this.consumeArchiveTypes(options.host);
if (hostOptions.consumeAPITypes) {
await Promise.all(downloadPromisesResult.map(async (item) => {
if (item.status === "rejected" || !item.value) return;
const [alias, destinationPath] = item.value;
const remoteInfo = this.remoteAliasMap[alias];
if (!remoteInfo) return;
await this.downloadAPITypes(remoteInfo, destinationPath, hostOptions);
}));
this.consumeAPITypes(hostOptions);
}
require_Broker.logger.success("Federated types extraction completed");
} catch (err) {
if (this.options.host?.abortOnError === false) require_Broker.fileLog(`Unable to consume federated types, ${err}`, "consumeTypes", "error");
else throw err;
}
}
async updateTypes(options) {
try {
const { remoteName, updateMode, remoteTarPath, remoteInfo: updatedRemoteInfo, once } = options;
const hostName = this.options?.host?.moduleFederationConfig?.name;
require_Broker.fileLog(`options: ${JSON.stringify(options, null, 2)};\nhostName: ${hostName}`, "updateTypes", "info");
if (updateMode === require_Action.UpdateMode.POSITIVE && remoteName === hostName) {
if (!this.options.remote) return;
await this.generateTypes();
} else {
const { remoteAliasMap } = this;
if (!this.options.host) return;
const { hostOptions, mapRemotesToDownload } = retrieveHostConfig(this.options.host);
const loadedRemoteInfo = Object.values(remoteAliasMap).find((i) => i.name === remoteName);
const consumeTypes = async (requiredRemoteInfo) => {
require_Broker.fileLog(`consumeTypes start`, "updateTypes", "info");
if (!requiredRemoteInfo.zipUrl) throw new Error(`Can not get ${requiredRemoteInfo.name}'s types archive url!`);
const [_alias, destinationPath] = await this.consumeTargetRemotes(hostOptions, {
...requiredRemoteInfo,
zipUrl: remoteTarPath || requiredRemoteInfo.zipUrl
});
if (await this.downloadAPITypes(requiredRemoteInfo, destinationPath, hostOptions)) this.consumeAPITypes(hostOptions);
require_Broker.fileLog(`consumeTypes end`, "updateTypes", "info");
};
require_Broker.fileLog(`loadedRemoteInfo: ${JSON.stringify(loadedRemoteInfo, null, 2)}`, "updateTypes", "info");
if (!loadedRemoteInfo) {
const remoteInfo = Object.values(mapRemotesToDownload).find((item) => {
return item.name === remoteName;
});
require_Broker.fileLog(`remoteInfo: ${JSON.stringify(remoteInfo, null, 2)}`, "updateTypes", "info");
if (remoteInfo) {
if (!this.remoteAliasMap[remoteInfo.alias]) {
const requiredRemoteInfo = await this.requestRemoteManifest(remoteInfo, hostOptions);
this.remoteAliasMap[remoteInfo.alias] = requiredRemoteInfo;
}
await consumeTypes(this.remoteAliasMap[remoteInfo.alias]);
} else if (updatedRemoteInfo) {
const consumeDynamicRemoteTypes = async () => {
await consumeTypes(this.updatedRemoteInfos[updatedRemoteInfo.name]);
};
if (!this.updatedRemoteInfos[updatedRemoteInfo.name]) {
const parsedRemoteInfo = retrieveRemoteInfo({
hostOptions,
remoteAlias: updatedRemoteInfo.alias || updatedRemoteInfo.name,
remote: updatedRemoteInfo.url
});
require_Broker.fileLog(`start request manifest`, "consumeTypes", "info");
this.updatedRemoteInfos[updatedRemoteInfo.name] = await this.requestRemoteManifest(parsedRemoteInfo, hostOptions);
require_Broker.fileLog(`end request manifest, this.updatedRemoteInfos[updatedRemoteInfo.name]: ${JSON.stringify(this.updatedRemoteInfos[updatedRemoteInfo.name], null, 2)}`, "updateTypes", "info");
await consumeDynamicRemoteTypes();
}
if (!once && this.updatedRemoteInfos[updatedRemoteInfo.name]) await consumeDynamicRemoteTypes();
}
} else await consumeTypes(loadedRemoteInfo);
}
} catch (err) {
require_Broker.fileLog(`updateTypes fail, ${err}`, "updateTypes", "error");
}
}
};
//#endregion
//#region src/core/lib/utils.ts
const dispatcherCache = /* @__PURE__ */ new Map();
function getDTSManagerConstructor(implementation) {
if (implementation) {
const NewConstructor = require(implementation);
return NewConstructor.default ? NewConstructor.default : NewConstructor;
}
return DTSManager;
}
const validateOptions = (options) => {
if (!options.moduleFederationConfig) throw new Error("moduleFederationConfig is required");
};
function retrieveTypesAssetsInfo(options) {
let apiTypesPath = "";
let zipTypesPath = "";
try {
const { tsConfig, remoteOptions, mapComponentsToExpose } = retrieveRemoteConfig(options);
if (!Object.keys(mapComponentsToExpose).length || !tsConfig.files.length) return {
apiTypesPath,
zipTypesPath,
zipName: "",
apiFileName: ""
};
zipTypesPath = retrieveTypesZipPath(retrieveMfTypesPath(tsConfig, remoteOptions), remoteOptions);
if (remoteOptions.generateAPITypes) apiTypesPath = retrieveMfAPITypesPath(tsConfig, remoteOptions);
return {
apiTypesPath,
zipTypesPath,
zipName: path.default.basename(zipTypesPath),
apiFileName: path.default.basename(apiTypesPath)
};
} catch (err) {
console.error(ansi_colors.default.red(`Unable to compile federated types, ${err}`));
return {
apiTypesPath: "",
zipTypesPath: "",
zipName: "",
apiFileName: ""
};
}
}
function isDebugMode() {
return Boolean(process.env["FEDERATION_DEBUG"]) || process.env["NODE_ENV"] === "test";
}
const isTSProject = (dtsOptions, context = process.cwd()) => {
if (dtsOptions === false) return false;
try {
let filepath = "";
if (typeof dtsOptions === "object" && dtsOptions.tsConfigPath) filepath = dtsOptions.tsConfigPath;
else filepath = path.default.resolve(context, "./tsconfig.json");
if (!path.default.isAbsolute(filepath)) filepath = path.default.resolve(context, filepath);
return fs.default.existsSync(filepath);
} catch (err) {
return false;
}
};
function cloneDeepOptions(options) {
const excludeKeys = new Set(["manifest", "async"]);
const cache = /* @__PURE__ */ new WeakMap();
function sanitize(val, key) {
if (key !== void 0 && excludeKeys.has(key) || typeof val === "function") return false;
if (key === "extractThirdParty" && Array.isArray(val)) return val.map(String);
if (Array.isArray(val)) {
if (cache.has(val)) return cache.get(val);
const out = [];
cache.set(val, out);
val.forEach((v, i) => out.push(sanitize(v, String(i))));
return out;
}
if (val !== null && typeof val === "object" && Object.getPrototypeOf(val) === Object.prototype) {
const obj = val;
if (cache.has(obj)) return cache.get(obj);
const out = {};
cache.set(obj, out);
for (const [k, v] of Object.entries(obj)) out[k] = sanitize(v, k);
return out;
}
return val;
}
return structuredClone(sanitize(options));
}
const getEnvHeaders = () => {
const headersStr = (0, _module_federation_sdk.getProcessEnv)()["MF_ENV_HEADERS"];
if (!headersStr || headersStr === "undefined") return {};
try {
return { ...JSON.parse(headersStr) };
} catch {
return {};
}
};
const createDispatcherFromFamily = (family) => {
if (!family) return void 0;
if (dispatcherCache.has(family)) return dispatcherCache.get(family);
try {
const dispatcher = new undici.Agent({ connect: { family } });
dispatcherCache.set(family, dispatcher);
return dispatcher;
} catch {}
};
const toHeaderRecord = (headers) => {
const out = {};
headers.forEach((value, key) => {
out[key.toLowerCase()] = value;
});
return out;
};
async function nativeFetch(url, config) {
const controller = new AbortController();
const timeoutMs = config?.timeout ?? 6e4;
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
const headers = {
...getEnvHeaders(),
...config?.headers ?? {}
};
const dispatcher = config?.dispatcher ?? createDispatcherFromFamily(config?.family);
try {
const resp = await fetch(url, {
headers,
signal: controller.signal,
...dispatcher ? { dispatcher } : {},
...config?.agent ? { agent: config.agent } : {}
});
const headerRecord = toHeaderRecord(resp.headers);
if (!resp.ok) throw new Error(`Request failed with status ${resp.status}`);
if (config?.responseType === "arraybuffer") return {
data: await resp.arrayBuffer(),
headers: headerRecord,
status: resp.status
};
return {
data: (resp.headers.get("content-type") || "").includes("application/json") || url.endsWith(".json") ? await resp.json() : await resp.text(),
headers: headerRecord,
status: resp.status
};
} finally {
clearTimeout(timeoutId);
}
}
//#endregion
//#region src/core/configurations/remotePlugin.ts
const defaultOptions = {
tsConfigPath: "./tsconfig.json",
typesFolder: "@mf-types",
compiledTypesFolder: "compiled-types",
hostRemoteTypesFolder: "@mf-types",
deleteTypesFolder: true,
additionalFilesToCompile: [],
compilerInstance: "tsc",
compileInChildProcess: false,
implementation: "",
generateAPITypes: false,
context: process.cwd(),
abortOnError: true,
extractRemoteTypes: false,
extractThirdParty: false,
outputDir: "",
deleteTsConfig: true
};
function getEffectiveRootDir(parsedCommandLine) {
const compilerOptions = parsedCommandLine.options;
if (compilerOptions.rootDir) return compilerOptions.rootDir;
const files = parsedCommandLine.fileNames;
if (files.length > 0) return files.map((file) => (0, path.dirname)(file)).reduce((commonPath, fileDir) => {
while (!fileDir.startsWith(commonPath)) commonPath = (0, path.dirname)(commonPath);
return commonPath;
}, files[0]);
if (parsedCommandLine.projectReferences.length) {
const relativeReferences = parsedCommandLine.projectReferences.filter((reference) => !(0, path.isAbsolute)(reference.originalPath ?? reference.path));
const referencesForRoot = relativeReferences.length ? relativeReferences : parsedCommandLine.projectReferences;
return referencesForRoot.map((reference) => (0, path.dirname)(reference.path)).reduce((commonPath, filePath) => {
while (!filePath.startsWith(commonPath)) commonPath = (0, path.dirname)(commonPath);
return commonPath;
}, (0, path.dirname)(referencesForRoot[0].path));
}
throw new Error("Can not get effective rootDir, please set compilerOptions.rootDir !");
}
const getDependentFiles = (rootFiles, configContent, rootDir) => {
const dependentFiles = typescript.default.createProgram(rootFiles, configContent.options).getSourceFiles().map((file) => file.fileName).filter((file) => !file.endsWith(".d.ts") && file.startsWith(rootDir));
return dependentFiles.length ? dependentFiles : rootFiles;
};
const readTsConfig = ({ tsConfigPath, typesFolder, compiledTypesFolder, context, additionalFilesToCompile, outputDir }, mapComponentsToExpose) => {
const resolvedTsConfigPath = (0, path.resolve)(context, tsConfigPath);
const readResult = typescript.default.readConfigFile(resolvedTsConfigPath, typescript.default.sys.readFile);
if (readResult.error) throw new Error(readResult.error.messageText.toString());
const rawTsConfigJson = readResult.config;
const configContent = typescript.default.parseJsonConfigFileContent(rawTsConfigJson, typescript.default.sys, (0, path.dirname)(resolvedTsConfigPath));
const rootDir = getEffectiveRootDir(configContent);
const outDir = (0, path.resolve)(context, outputDir || configContent.options.outDir || "dist", typesFolder, compiledTypesFolder);
const defaultCompilerOptions = {
rootDir,
emitDeclarationOnly: true,
noEmit: false,
declaration: true,
outDir
};
rawTsConfigJson.compilerOptions = rawTsConfigJson.compilerOptions || {};
rawTsConfigJson.compilerOptions = {
incremental: true,
tsBuildInfoFile: (0, path.resolve)(context, "node_modules/.cache/mf-types/.tsbuildinfo"),
...rawTsConfigJson.compilerOptions,
...defaultCompilerOptions
};
const { paths, baseUrl, ...restCompilerOptions } = rawTsConfigJson.compilerOptions || {};
rawTsConfigJson.compilerOptions = restCompilerOptions;
const outDirWithoutTypesFolder = (0, path.resolve)(context, outputDir || configContent.options.outDir || "dist");
const excludeExtensions = [".mdx", ".md"];
const filesToCompile = [...getDependentFiles([...Object.values(mapComponentsToExpose), ...additionalFilesToCompile].filter((filename) => !excludeExtensions.some((ext) => filename.endsWith(ext))), configContent, rootDir), ...configContent.fileNames.filter((filename) => filename.endsWith(".d.ts") && !filename.startsWith(outDirWithoutTypesFolder))];
rawTsConfigJson.include = [];
rawTsConfigJson.files = [...new Set(filesToCompile)];
rawTsConfigJson.exclude = [];
"references" in rawTsConfigJson && delete rawTsConfigJson.references;
rawTsConfigJson.extends = resolvedTsConfigPath;
rawTsConfigJson.compilerOptions.declarationDir = outDir;
return rawTsConfigJson;
};
const TS_EXTENSIONS = [
"ts",
"tsx",
"vue",
"svelte"
];
const resolveWithExtension = (exposedPath, context) => {
if ((0, path.extname)(exposedPath)) return (0, path.resolve)(context, exposedPath);
for (const extension of TS_EXTENSIONS) {
const exposedPathWithExtension = (0, path.resolve)(context, `${exposedPath}.${extension}`);
if ((0, fs.existsSync)(exposedPathWithExtension)) return exposedPathWithExtension;
}
};
const resolveExposes = (remoteOptions) => {
return _module_federation_managers.utils.parseOptions(remoteOptions.moduleFederationConfig.exposes || {}, (item, key) => ({
exposePath: Array.isArray(item) ? item[0] : item,
key
}), (item, key) => ({
exposePath: Array.isArray(item.import) ? item.import[0] : item.import[0],
key
})).reduce((accumulator, item) => {
const { exposePath, key } = item[1];
accumulator[key] = resolveWithExtension(exposePath, remoteOptions.context) || resolveWithExtension((0, path.join)(exposePath, "index"), remoteOptions.context) || exposePath;
return accumulator;
}, {});
};
const retrieveRemoteConfig = (options) => {
validateOptions(options);
const remoteOptions = {
...defaultOptions,
...options
};
const mapComponentsToExpose = resolveExposes(remoteOptions);
const tsConfig = readTsConfig(remoteOptions, mapComponentsToExpose);
if (tsConfig.compilerOptions.incremental && tsConfig.compilerOptions.tsBuildInfoFile && options.deleteTypesFolder !== true) remoteOptions.deleteTypesFolder = false;
return {
tsConfig,
mapComponentsToExpose,
remoteOptions
};
};
//#endregion
//#region src/core/lib/generateTypes.ts
async function generateTypes(options) {
return new (getDTSManagerConstructor(options.remote?.implementation))(options).generateTypes();
}
//#endregion
//#region src/core/rpc/types.ts
let RpcGMCallTypes = /* @__PURE__ */ function(RpcGMCallTypes) {
RpcGMCallTypes["CALL"] = "mf_call";
RpcGMCallTypes["RESOLVE"] = "mf_resolve";
RpcGMCallTypes["REJECT"] = "mf_reject";
RpcGMCallTypes["EXIT"] = "mf_exit";
return RpcGMCallTypes;
}({});
//#endregion
//#region src/core/rpc/expose-rpc.ts
function exposeRpc(fn) {
const sendMessage = (message) => new Promise((resolve, reject) => {
if (!process$1.default.send) reject(/* @__PURE__ */ new Error(`Process ${process$1.default.pid} doesn't have IPC channels`));
else if (!process$1.default.connected) reject(/* @__PURE__ */ new Error(`Process ${process$1.default.pid} doesn't have open IPC channels`));
else process$1.default.send(message, void 0, void 0, (error) => {
if (error) reject(error);
else resolve(void 0);
});
});
const handleMessage = async (message) => {
if (message.type === RpcGMCallTypes.CALL) {
if (!process$1.default.send) return;
let value, error;
try {
value = await fn(...message.args);
} catch (fnError) {
error = fnError;
}
try {
if (error) await sendMessage({
type: RpcGMCallTypes.REJECT,
id: message.id,
error
});
else await sendMessage({
type: RpcGMCallTypes.RESOLVE,
id: message.id,
value
});
} catch (sendError) {
if (error) {
if (error instanceof Error) console.error(error);
}
console.error(sendError);
}
}
};
process$1.default.on("message", handleMessage);
}
//#endregion
Object.defineProperty(exports, 'DTSManager', {
enumerable: true,
get: function () {
return DTSManager;
}
});
Object.defineProperty(exports, 'HOST_API_TYPES_FILE_NAME', {
enumerable: true,
get: function () {
return HOST_API_TYPES_FILE_NAME;
}
});
Object.defineProperty(exports, 'ModuleFederationDevServer', {
enumerable: true,
get: function () {
return ModuleFederationDevServer;
}
});
Object.defineProperty(exports, 'REMOTE_ALIAS_IDENTIFIER', {
enumerable: true,
get: function () {
return REMOTE_ALIAS_IDENTIFIER;
}
});
Object.defineProperty(exports, 'REMOTE_API_TYPES_FILE_NAME', {
enumerable: true,
get: function () {
return REMOTE_API_TYPES_FILE_NAME;
}
});
Object.defineProperty(exports, 'RpcGMCallTypes', {
enumerable: true,
get: function () {
return RpcGMCallTypes;
}
});
Object.defineProperty(exports, 'cloneDeepOptions', {
enumerable: true,
get: function () {
return cloneDeepOptions;
}
});
Object.defineProperty(exports, 'createHttpServer', {
enumerable: true,
get: function () {
return createHttpServer;
}
});
Object.defineProperty(exports, 'exposeRpc', {
enumerable: true,
get: function () {
return exposeRpc;
}
});
Object.defineProperty(exports, 'generateTypes', {
enumerable: true,
get: function () {
return generateTypes;
}
});
Object.defineProperty(exports, 'getDTSManagerConstructor', {
enumerable: true,
get: function () {
return getDTSManagerConstructor;
}
});
Object.defineProperty(exports, 'isDebugMode', {
enumerable: true,
get: function () {
return isDebugMode;
}
});
Object.defineProperty(exports, 'isTSProject', {
enumerable: true,
get: function () {
return isTSProject;
}
});
Object.defineProperty(exports, 'retrieveHostConfig', {
enumerable: true,
get: function () {
return retrieveHostConfig;
}
});
Object.defineProperty(exports, 'retrieveMfTypesPath', {
enumerable: true,
get: function () {
return retrieveMfTypesPath;
}
});
Object.defineProperty(exports, 'retrieveOriginalOutDir', {
enumerable: true,
get: function () {
return retrieveOriginalOutDir;
}
});
Object.defineProperty(exports, 'retrieveRemoteConfig', {
enumerable: true,
get: function () {
return retrieveRemoteConfig;
}
});
Object.defineProperty(exports, 'retrieveTypesAssetsInfo', {
enumerable: true,
get: function () {
return retrieveTypesAssetsInfo;
}
});
Object.defineProperty(exports, 'retrieveTypesZipPath', {
enumerable: true,
get: function () {
return retrieveTypesZipPath;
}
});
Object.defineProperty(exports, 'validateOptions', {
enumerable: true,
get: function () {
return validateOptions;
}
});