everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
132 lines (130 loc) • 5.27 kB
JavaScript
import { colors, icons } from "../utils/theme.mjs";
import { linkify } from "../utils/linkify.mjs";
import chalk from "chalk";
//#region src/components/streaming-view.ts
const orange = chalk.hex("#ffaa00");
const PLUGIN_PREFIX = "plugin:";
const getTimestamp = () => {
const now = /* @__PURE__ */ new Date();
return `${now.getHours().toString().padStart(2, "0")}:${now.getMinutes().toString().padStart(2, "0")}:${now.getSeconds().toString().padStart(2, "0")}`;
};
const write = (text) => process.stdout.write(`${text}\n`);
const getServiceColor = (name) => {
if (name.startsWith(PLUGIN_PREFIX)) return orange;
if (name === "host") return colors.cyan;
if (name === "ui" || name === "ui-ssr") return colors.magenta;
if (name === "api") return colors.blue;
return colors.white;
};
const getDisplayName = (name) => {
return name.startsWith(PLUGIN_PREFIX) ? name.slice(7).toUpperCase() : name.toUpperCase();
};
const isPlugin = (name) => name.startsWith(PLUGIN_PREFIX);
const getSectionedProcesses = (processes) => {
const plugins = processes.filter((p) => isPlugin(p.name));
const services = processes.filter((p) => !isPlugin(p.name));
const sections = [];
if (plugins.length > 0) sections.push({
key: "plugins",
title: "PLUGINS",
processes: plugins
});
if (services.length > 0) sections.push({
key: "services",
title: "SERVICES",
processes: services
});
return sections;
};
const getColumnWidths = (processes) => {
return {
name: Math.max(6, ...processes.map((p) => getDisplayName(p.name).length)),
source: Math.max(10, ...processes.map((p) => p.source ? ` (${p.source})`.length : 0))
};
};
const getStatusIcon = (status) => {
switch (status) {
case "pending": return icons.pending;
case "starting": return icons.scan;
case "ready": return icons.ok;
case "error": return icons.err;
}
};
function renderStreamingView(initialProcesses, description, env, onExit) {
const processes = /* @__PURE__ */ new Map();
for (const p of initialProcesses) processes.set(p.name, { ...p });
let allReadyPrinted = false;
const hostPort = initialProcesses.find((p) => p.name === "host")?.port || 3e3;
const proxyTarget = env.API_PROXY;
const sectionedProcesses = getSectionedProcesses(initialProcesses);
const columnWidths = getColumnWidths(initialProcesses);
const lastLogBySource = /* @__PURE__ */ new Map();
const headerLines = [
"",
colors.cyan(`${"─".repeat(52)}`),
` ${icons.run} ${colors.cyan(description.toUpperCase())}`,
colors.cyan(`${"─".repeat(52)}`),
""
];
if (proxyTarget) headerLines.push(orange(` ${icons.arrow} API PROXY → ${proxyTarget}`), "");
for (const section of sectionedProcesses) {
headerLines.push(colors.cyan(` ${section.title}`));
for (const proc of section.processes) {
const color = getServiceColor(proc.name);
const sourceLabel = proc.source ? ` (${proc.source})` : "";
headerLines.push(`${colors.dim(`[${getTimestamp()}]`)} ${color(`[${getDisplayName(proc.name).padEnd(columnWidths.name)}]`)} ${icons.pending} waiting${sourceLabel.padEnd(columnWidths.source)}`);
}
headerLines.push("");
}
console.log(headerLines.join("\n"));
const checkAllReady = () => {
if (allReadyPrinted) return;
if (Array.from(processes.values()).every((p) => p.status === "ready")) {
allReadyPrinted = true;
const readyLines = [
"",
colors.dim(`${"─".repeat(52)}`),
colors.green(`${icons.ok} All ${processes.size} services ready`),
colors.green(`${icons.arrow} http://localhost:${hostPort}`),
colors.dim(`${"─".repeat(52)}`),
""
];
console.log(readyLines.join("\n"));
}
};
const updateProcess = (name, status, message) => {
const proc = processes.get(name);
if (!proc) return;
proc.status = status;
if (message) proc.message = message;
const color = getServiceColor(name);
const icon = getStatusIcon(status);
const displayName = getDisplayName(name).padEnd(columnWidths.name);
const sourceLabel = proc?.source ? ` (${proc.source})` : "";
const isRemote = proc?.source === "remote";
const isHost = name === "host";
const showPort = proc.port > 0 && (isHost || !isRemote) && status === "ready";
const statusText = status === "ready" ? isRemote && !isHost ? "loaded" : "running" : status === "starting" ? "starting" : status === "error" ? "failed" : "waiting";
const portStr = showPort ? ` :${proc.port}` : "";
write(`${colors.dim(`[${getTimestamp()}]`)} ${color(`[${displayName}]`)} ${status === "ready" ? colors.green(icon) : status === "error" ? colors.error(icon) : icon} ${statusText}${sourceLabel.padEnd(columnWidths.source)}${portStr}`);
checkAllReady();
};
const addLog = (source, line, isError = false) => {
const lastLine = lastLogBySource.get(source);
const nextLine = `${isError ? "ERR" : "OUT"}:${line}`;
if (lastLine === nextLine) return;
lastLogBySource.set(source, nextLine);
const color = getServiceColor(source);
const logColor = isError ? colors.error : colors.dim;
write(`${colors.dim(`[${getTimestamp()}]`)} ${color(`[${source.toUpperCase()}]`)} ${colors.dim("│")} ${logColor(linkify(line))}`);
};
const unmount = () => onExit?.();
return {
updateProcess,
addLog,
unmount
};
}
//#endregion
export { renderStreamingView };
//# sourceMappingURL=streaming-view.mjs.map