everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
201 lines (199 loc) • 8.25 kB
JavaScript
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
const require_merge = require('./merge.cjs');
const require_types = require('./types.cjs');
let node_fs = require("node:fs");
let node_path = require("node:path");
let node_crypto = require("node:crypto");
//#region src/shared-deps.ts
function sha256(input) {
return (0, node_crypto.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 key of keys) out[key] = deps[key];
return out;
}
function normalizeSharedDepConfig(config) {
return {
version: config.version,
requiredVersion: config.requiredVersion ?? false,
singleton: config.singleton ?? false,
eager: config.eager ?? false,
strictVersion: config.strictVersion ?? false,
shareScope: config.shareScope ?? "default"
};
}
function getObject(value) {
return require_merge.isPlainObject(value) ? value : void 0;
}
function getSharedDepsMap(value, source) {
if (value === void 0) return void 0;
if (!require_types.SharedDepMapSchema.safeParse(value).success) throw new Error(`Invalid shared dependency map at ${source}`);
return value;
}
function writeFileIfChanged(filePath, nextContent) {
try {
if ((0, node_fs.readFileSync)(filePath, "utf-8") === nextContent) return false;
} catch {}
(0, node_fs.writeFileSync)(filePath, nextContent);
return true;
}
function fingerprintResolved(deps) {
return sha256(JSON.stringify(stableDepsObject(deps)));
}
function isSameSharedDepConfig(a, b) {
const left = normalizeSharedDepConfig(a);
const right = normalizeSharedDepConfig(b);
return left.version === right.version && left.requiredVersion === right.requiredVersion && left.singleton === right.singleton && left.eager === right.eager && left.strictVersion === right.strictVersion && left.shareScope === right.shareScope;
}
function collectSharedDepRefs(bosConfig) {
const refs = /* @__PURE__ */ new Map();
const app = getObject(bosConfig.app);
const appUi = getObject(app?.ui);
const appApi = getObject(app?.api);
const appAuth = getObject(app?.auth);
const plugins = getObject(bosConfig.plugins);
if (appUi && "shared" in appUi) throw new Error("app.ui.shared is no longer supported. Move shared deps to app.api.shared, app.auth.shared, or plugins.*.shared.");
const append = (source, shared) => {
if (!shared) return;
for (const [name, config] of Object.entries(shared)) {
const existing = refs.get(name);
if (!existing) {
refs.set(name, [{
source,
config
}]);
continue;
}
if (!isSameSharedDepConfig(existing[0].config, config)) {
const previous = existing.map((ref) => ref.source).join(", ");
throw new Error(`Conflicting shared dependency "${name}" between ${previous} and ${source}`);
}
existing.push({
source,
config
});
}
};
append("app.api", getSharedDepsMap(appApi?.shared, "app.api.shared"));
append("app.auth", getSharedDepsMap(appAuth?.shared, "app.auth.shared"));
for (const [pluginId, plugin] of Object.entries(plugins ?? {})) {
const pluginRecord = getObject(plugin);
if (!pluginRecord) continue;
const pluginUi = getObject(getObject(pluginRecord.app)?.ui);
if (pluginUi && "shared" in pluginUi) throw new Error(`app.ui.shared is no longer supported in plugins.${pluginId}. Move shared deps to app.api.shared, app.auth.shared, or plugins.*.shared.`);
append(`plugins.${pluginId}`, getSharedDepsMap(pluginRecord.shared, `plugins.${pluginId}.shared`));
}
return Object.fromEntries(refs);
}
async function syncResolvedSharedDeps(opts) {
const bosConfigPath = (0, node_path.join)(opts.configDir, "bos.config.json");
const resolvedConfigPath = (0, node_path.join)(opts.configDir, ".bos", "bos.resolved-config.json");
const packageJsonPath = (0, node_path.join)(opts.configDir, "package.json");
const generatedPath = (0, node_path.join)(opts.configDir, ".bos", "generated", "shared-deps.json");
const bosConfig = opts.bosConfig ?? JSON.parse((0, node_fs.readFileSync)(bosConfigPath, "utf-8"));
if (!require_merge.isPlainObject(bosConfig)) throw new Error("bos.config.json must be an object");
const pkgJson = (0, node_fs.existsSync)(packageJsonPath) ? JSON.parse((0, node_fs.readFileSync)(packageJsonPath, "utf-8")) : {};
const originalBos = JSON.stringify(bosConfig);
const originalPkg = JSON.stringify(pkgJson);
const mode = opts.hostMode === "local" ? "catalog->bos" : "bos->catalog";
const refsByName = collectSharedDepRefs(bosConfig);
const catalog = pkgJson.workspaces?.catalog ?? {};
const resolvedDeps = {};
for (const [name, refs] of Object.entries(refsByName)) {
const first = refs[0];
if (!first) continue;
const exactFromConfig = extractSemverExact(first.config.version) ?? extractSemverExact(first.config.requiredVersion);
const exactFromCatalog = extractSemverExact(catalog[name]);
const version = mode === "catalog->bos" ? exactFromCatalog ?? exactFromConfig : exactFromConfig ?? exactFromCatalog;
if (!version) {
const sources = refs.map((ref) => ref.source).join(", ");
throw new Error(`Could not resolve exact version for shared dependency "${name}" from ${sources}`);
}
if (mode === "catalog->bos" && exactFromCatalog === null && exactFromConfig) catalog[name] = exactFromConfig;
if (mode === "bos->catalog" && catalog[name] !== version) catalog[name] = version;
for (const ref of refs) {
ref.config.version = version;
ref.config.requiredVersion = caretRange(version);
ref.config.shareScope = ref.config.shareScope ?? "default";
}
resolvedDeps[name] = {
name,
version,
requiredVersion: caretRange(version),
shareScope: first.config.shareScope ?? "default",
singleton: first.config.singleton ?? false,
eager: first.config.eager ?? false,
strictVersion: first.config.strictVersion ?? false,
sources: refs.map((ref) => ref.source).sort((a, b) => a.localeCompare(b))
};
}
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) {
const resolvedDir = (0, node_path.dirname)(resolvedConfigPath);
if (!(0, node_fs.existsSync)(resolvedDir)) (0, node_fs.mkdirSync)(resolvedDir, { recursive: true });
const ordered = require_merge.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`);
}
if (catalogChanged) writeFileIfChanged(packageJsonPath, `${JSON.stringify(pkgJson, null, 2)}\n`);
const stableResolvedDeps = stableDepsObject(resolvedDeps);
const resolved = {
deps: stableResolvedDeps,
fingerprintSha256: fingerprintResolved(stableResolvedDeps)
};
const nextGenerated = {
schemaVersion: 1,
kind: "everything-dev/shared-deps",
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
deps: stableResolvedDeps,
fingerprintSha256: resolved.fingerprintSha256,
inputs: {
mode,
hostMode: opts.hostMode
}
};
let prevFingerprint = null;
try {
prevFingerprint = JSON.parse((0, node_fs.readFileSync)(generatedPath, "utf-8"))?.fingerprintSha256 ?? null;
} catch {}
(0, node_fs.mkdirSync)((0, node_path.dirname)(generatedPath), { recursive: true });
writeFileIfChanged(generatedPath, `${JSON.stringify(nextGenerated, null, 2)}\n`);
const generatedChanged = prevFingerprint !== nextGenerated.fingerprintSha256;
return {
mode,
hostMode: opts.hostMode,
bosConfigChanged,
catalogChanged,
generatedChanged,
resolved
};
}
//#endregion
exports.syncResolvedSharedDeps = syncResolvedSharedDeps;
//# sourceMappingURL=shared-deps.cjs.map