everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
154 lines (152 loc) • 5.41 kB
JavaScript
import { rebuildOrderedConfig } from "./merge.mjs";
import { BosConfigSchema } from "./types.mjs";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { createHash } from "node:crypto";
//#region src/shared.ts
function sha256(input) {
return createHash("sha256").update(input).digest("hex");
}
function extractSemverExact(input) {
if (typeof input !== "string") return null;
const match = input.match(/\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?/);
return match ? match[0] : null;
}
function caretRange(version) {
return `^${version}`;
}
function stableDepsObject(deps) {
const keys = Object.keys(deps).sort((a, b) => a.localeCompare(b));
const out = {};
for (const k of keys) out[k] = deps[k];
return out;
}
function writeFileIfChanged(filePath, nextContent) {
try {
if (readFileSync(filePath, "utf-8") === nextContent) return false;
} catch {}
writeFileSync(filePath, nextContent);
return true;
}
function fingerprintResolved(deps) {
const stable = stableDepsObject(deps);
return sha256(JSON.stringify(stable));
}
function getSharedUiDeps(bosConfig) {
return (bosConfig.shared ?? {}).ui ?? {};
}
async function syncAndGenerateSharedUi(opts) {
const bosConfigPath = join(opts.configDir, "bos.config.json");
const resolvedConfigPath = join(opts.configDir, ".bos", "bos.resolved-config.json");
const packageJsonPath = join(opts.configDir, "package.json");
const generatedPath = join(opts.configDir, ".bos", "generated", "shared-ui.json");
let bosConfig;
if (opts.bosConfig) bosConfig = opts.bosConfig;
else {
const raw = JSON.parse(readFileSync(bosConfigPath, "utf-8"));
bosConfig = BosConfigSchema.parse(raw);
}
let pkgJson = {};
try {
pkgJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
} catch {}
const originalBos = JSON.stringify(bosConfig);
const originalPkg = JSON.stringify(pkgJson);
const catalog = pkgJson?.workspaces?.catalog ?? {};
const sharedUi = getSharedUiDeps(bosConfig);
const mode = opts.hostMode === "local" ? "catalog->bos" : "bos->catalog";
if (mode === "catalog->bos") for (const [name, cfg] of Object.entries(sharedUi)) {
const dep = cfg;
const version = catalog[name] ?? extractSemverExact(dep.version) ?? extractSemverExact(dep.requiredVersion);
if (!version) continue;
dep.version = version;
dep.requiredVersion = caretRange(version);
dep.shareScope = dep.shareScope ?? "default";
}
else {
for (const [name, cfg] of Object.entries(sharedUi)) {
const dep = cfg;
const version = extractSemverExact(dep.version) ?? extractSemverExact(dep.requiredVersion);
if (!version) continue;
dep.version = version;
dep.requiredVersion = caretRange(version);
dep.shareScope = dep.shareScope ?? "default";
if (catalog[name] !== version) catalog[name] = version;
}
if (!pkgJson.workspaces) pkgJson.workspaces = {
packages: [],
catalog: {}
};
pkgJson.workspaces.catalog = catalog;
}
const nextBos = JSON.stringify(bosConfig);
const nextPkg = JSON.stringify(pkgJson);
const bosConfigChanged = nextBos !== originalBos;
const catalogChanged = nextPkg !== originalPkg;
if (bosConfigChanged) if (mode === "catalog->bos") {
const resolvedDir = dirname(resolvedConfigPath);
if (!existsSync(resolvedDir)) mkdirSync(resolvedDir, { recursive: true });
const ordered = rebuildOrderedConfig(bosConfig);
const resolvedOutput = {
_resolved: {
env: opts.env ?? "development",
resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
extendsChain: opts.extendsChain ?? [],
source: "shared-sync"
},
...ordered
};
writeFileIfChanged(resolvedConfigPath, `${JSON.stringify(resolvedOutput, null, 2)}\n`);
} else writeFileIfChanged(bosConfigPath, `${JSON.stringify(bosConfig, null, 2)}\n`);
if (catalogChanged) writeFileIfChanged(packageJsonPath, `${JSON.stringify(pkgJson, null, 2)}\n`);
const resolvedDeps = {};
for (const [name, cfg] of Object.entries(getSharedUiDeps(bosConfig))) {
const version = catalog[name] ?? extractSemverExact(cfg.version) ?? extractSemverExact(cfg.requiredVersion);
if (!version) continue;
resolvedDeps[name] = {
name,
version,
requiredVersion: caretRange(version),
shareScope: cfg.shareScope ?? "default",
singleton: cfg.singleton ?? false,
eager: cfg.eager ?? false,
strictVersion: cfg.strictVersion ?? false
};
}
const stableResolvedDeps = stableDepsObject(resolvedDeps);
const resolved = {
deps: stableResolvedDeps,
fingerprintSha256: fingerprintResolved(stableResolvedDeps)
};
const nextGenerated = {
schemaVersion: 1,
kind: "everything-dev/shared-ui",
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
ui: {
deps: stableResolvedDeps,
fingerprintSha256: resolved.fingerprintSha256
},
inputs: {
mode,
hostMode: opts.hostMode
}
};
let prevFingerprint = null;
try {
prevFingerprint = JSON.parse(readFileSync(generatedPath, "utf-8"))?.ui?.fingerprintSha256 ?? null;
} catch {}
mkdirSync(dirname(generatedPath), { recursive: true });
writeFileIfChanged(generatedPath, `${JSON.stringify(nextGenerated, null, 2)}\n`);
const generatedChanged = prevFingerprint !== nextGenerated.ui.fingerprintSha256;
return {
mode,
hostMode: opts.hostMode,
bosConfigChanged,
catalogChanged,
generatedChanged,
resolved
};
}
//#endregion
export { syncAndGenerateSharedUi };
//# sourceMappingURL=shared.mjs.map