everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
194 lines (192 loc) • 6.38 kB
JavaScript
import { resolveLocalDevelopmentPath } from "./config.mjs";
import { run } from "./utils/run.mjs";
import { colors, icons } from "./utils/theme.mjs";
import { formatDuration } from "./cli/timing.mjs";
import { ensureFreshDeps, findWorkspaceRoot } from "./workspace.mjs";
import { syncResolvedSharedDeps } from "./shared-deps.mjs";
import { padRight } from "./utils/string.mjs";
import process from "node:process";
import { access, readFile } from "node:fs/promises";
//#region src/build.ts
const buildCommands = {
host: {
cmd: "pnpm",
args: ["run", "build"]
},
ui: {
cmd: "pnpm",
args: ["run", "build"]
},
api: {
cmd: "pnpm",
args: ["run", "build"]
}
};
function getPluginRef(entry) {
if (!entry || typeof entry === "string") return null;
return entry;
}
async function fileExists(path) {
try {
await access(path);
return true;
} catch {
return false;
}
}
function resolveWorkspaceTarget(key, bosConfig, runtimeConfig, configDir) {
if (bosConfig?.app && key in bosConfig.app) {
const appEntry = bosConfig.app[key];
const devPath = resolveLocalDevelopmentPath(appEntry?.development, configDir);
if (devPath) return {
key,
kind: "app",
path: devPath
};
return {
key,
kind: "app",
path: `${configDir}/${key}`
};
}
const pluginPath = (runtimeConfig?.plugins?.[key])?.localPath ?? resolveLocalDevelopmentPath(getPluginRef(bosConfig?.plugins?.[key])?.development, configDir);
if (pluginPath) return {
key,
kind: "plugin",
path: pluginPath
};
return null;
}
function selectWorkspaceTargets(packages, bosConfig) {
const allPackages = [...Object.keys(bosConfig?.app ?? {}), ...Object.keys(bosConfig?.plugins ?? {})];
if (packages === "all") return allPackages;
if (packages === "local") return allPackages.filter((k) => isLocalTarget(k, bosConfig));
return packages.split(",").map((pkg) => pkg.trim()).filter((pkg) => allPackages.includes(pkg));
}
function isLocalTarget(key, bosConfig) {
const dev = ((bosConfig?.app)?.[key] ?? bosConfig?.plugins?.[key])?.development;
return typeof dev === "string" && dev.startsWith("local:");
}
async function buildOneWorkspace(ws, env, opts) {
const buildConfig = buildCommands[ws.key] ?? {
cmd: "pnpm",
args: ["run", "build"]
};
const verbose = opts.verbose ?? false;
const startTime = Date.now();
const proc = await run(buildConfig.cmd, buildConfig.args, {
cwd: ws.path,
env,
capture: true,
onChunk: (stream, chunk) => {
if (stream === "stderr") process.stderr.write(chunk);
else if (verbose) process.stdout.write(chunk);
}
});
const exitCode = proc?.exitCode ?? 0;
const durationMs = Date.now() - startTime;
const output = `${proc?.stdout ?? ""}\n${proc?.stderr ?? ""}`;
const result = {
key: ws.key,
kind: ws.kind,
success: exitCode === 0,
...exitCode !== 0 && { error: `Build failed (exit code ${exitCode})\n${output.trim().split("\n").slice(-5).join("\n")}` },
durationMs
};
if (!verbose) {
const name = padRight(ws.key, 28);
if (result.success) console.log(` ${colors.green(icons.ok)} ${name} ${colors.dim(formatDuration(durationMs))}`);
else {
const errorLine = (result.error ?? "Failed").split("\n")[0];
console.log(` ${colors.error(icons.err)} ${name} ${errorLine}`);
}
}
return result;
}
async function buildWorkspaceTargets(opts) {
const existing = [];
const skipped = [];
for (const target of opts.targets) {
const resolved = resolveWorkspaceTarget(target, opts.bosConfig, opts.runtimeConfig, opts.configDir);
if (!resolved) {
skipped.push(target);
continue;
}
if (await fileExists(`${resolved.path}/package.json`)) existing.push(resolved);
else skipped.push(target);
}
if (existing.length === 0) return {
built: [],
skipped
};
if ((await syncResolvedSharedDeps({
configDir: opts.configDir,
hostMode: "local",
bosConfig: opts.bosConfig ?? void 0,
extendsChain: []
})).catalogChanged) await run("pnpm", ["install"], { cwd: findWorkspaceRoot(opts.configDir)?.dir ?? opts.configDir });
const env = {
...process.env,
NODE_ENV: opts.deploy ? "production" : "development"
};
if (opts.deploy) env.DEPLOY = "true";
else delete env.DEPLOY;
const depsReport = await ensureFreshDeps(opts.configDir, existing.map((entry) => entry.path), {
force: opts.deploy,
env
});
if (depsReport.rebuilt.length > 0) console.log(` ${colors.dim(`prerequisites: rebuilt ${depsReport.rebuilt.map((member) => member.name).join(", ")}`)}`);
const orderedExisting = opts.deploy ? [
...existing.filter((entry) => entry.kind === "app" && entry.key !== "host"),
...existing.filter((entry) => entry.kind === "plugin"),
...existing.filter((entry) => entry.kind === "app" && entry.key === "host")
] : existing;
const parallelGroup = opts.deploy ? orderedExisting.filter((e) => e.key !== "host") : orderedExisting;
const sequentialGroup = opts.deploy ? orderedExisting.filter((e) => e.key === "host") : [];
const built = [];
const deployResults = [];
if (opts.deploy && parallelGroup.length > 0) {
const total = parallelGroup.length + sequentialGroup.length;
console.log();
console.log(` Building ${total} workspace${total > 1 ? "s" : ""}...`);
console.log();
const results = await Promise.allSettled(parallelGroup.map((ws) => buildOneWorkspace(ws, env, opts)));
for (let i = 0; i < parallelGroup.length; i++) {
const ws = parallelGroup[i];
const result = results[i];
if (result.status === "fulfilled") {
if (result.value.success) built.push(ws.key);
deployResults.push(result.value);
} else deployResults.push({
key: ws.key,
kind: ws.kind,
success: false,
error: result.reason?.message ?? "Unknown error"
});
}
for (const ws of sequentialGroup) {
const result = await buildOneWorkspace(ws, env, opts);
if (result.success) built.push(ws.key);
deployResults.push(result);
}
console.log();
} else for (const resolved of orderedExisting) {
const buildConfig = buildCommands[resolved.key] ?? {
cmd: "pnpm",
args: ["run", "build"]
};
await run(buildConfig.cmd, buildConfig.args, {
cwd: resolved.path,
env
});
built.push(resolved.key);
}
return {
built,
skipped,
deployResults: opts.deploy ? deployResults : void 0
};
}
//#endregion
export { buildWorkspaceTargets, fileExists, getPluginRef, resolveWorkspaceTarget, selectWorkspaceTargets };
//# sourceMappingURL=build.mjs.map