everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
272 lines (270 loc) • 10.1 kB
JavaScript
const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
const require_runtime$1 = require('../resolution/runtime.cjs');
const require_run = require('../../utils/run.cjs');
const require_workspace = require('./workspace.cjs');
const require_framework_version = require('../framework-version.cjs');
const require_shared_deps = require('./shared-deps.cjs');
let effect = require("effect");
let node_fs_promises = require("node:fs/promises");
let node_os = require("node:os");
node_os = require_runtime.__toESM(node_os, 1);
let node_process = require("node:process");
node_process = require_runtime.__toESM(node_process, 1);
//#region src/services/workspace/build-train.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 (0, node_fs_promises.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 = require_runtime$1.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 ?? require_runtime$1.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:");
}
/** Pulls warning segments out of a build's stderr: a WARNING/⚠ line opens a
* segment that runs to the next blank line; `$ ` command echoes never count. */
function extractBuildWarnings(stderr) {
const warnings = [];
let current = null;
for (const line of stderr.split("\n")) if (current === null) {
if (!/^\$\s/.test(line.trim()) && /WARNING|⚠|\bwarning\b/i.test(line)) current = [line];
} else if (line.trim() === "") {
if (current.some((l) => l.trim().length > 0)) warnings.push(current.join("\n"));
current = null;
} else current.push(line);
if (current?.some((l) => l.trim().length > 0)) warnings.push(current.join("\n"));
return warnings;
}
async function buildOneWorkspace(ws, env, opts, deps) {
const buildConfig = buildCommands[ws.key] ?? {
cmd: "pnpm",
args: ["run", "build"]
};
const verbose = opts.verbose ?? false;
const startTime = Date.now();
const stderrBuffer = [];
const proc = await (deps.runChild ?? require_run.run)(buildConfig.cmd, buildConfig.args, {
cwd: ws.path,
env,
capture: true,
onChunk: (stream, chunk) => {
if (stream === "stderr") {
if (verbose) node_process.default.stderr.write(chunk);
stderrBuffer.push(chunk.toString());
} else if (verbose) node_process.default.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,
stderr: proc?.stderr ?? "",
warnings: extractBuildWarnings(stderrBuffer.join("")),
...exitCode !== 0 && { error: `Build failed (exit code ${exitCode})\n${output.trim().split("\n").slice(-5).join("\n")}` },
durationMs
};
if (!verbose) deps.render?.outcome?.(result);
if (result.success) deps.onWorkspaceBuilt?.(ws.key);
return result;
}
async function buildWorkspaceTargets(input, deps = {}) {
const render = deps.render;
const existing = [];
const skipped = [];
for (const target of input.targets) {
const resolved = resolveWorkspaceTarget(target, input.bosConfig, input.runtimeConfig, input.configDir);
if (!resolved) {
skipped.push(target);
continue;
}
if (await (deps.fileExists ?? fileExists)(`${resolved.path}/package.json`)) existing.push(resolved);
else skipped.push(target);
}
if (existing.length === 0) return {
built: [],
skipped
};
if ((await require_shared_deps.syncResolvedSharedDeps({
configDir: input.configDir,
hostMode: "local",
bosConfig: input.bosConfig ?? void 0,
extendsChain: []
})).catalogChanged) await require_run.run("pnpm", ["install"], { cwd: require_workspace.findWorkspaceRoot(input.configDir)?.dir ?? input.configDir });
const env = {
...node_process.default.env,
NODE_ENV: input.deploy ? "production" : "development"
};
if (input.deploy) env.DEPLOY = "true";
else delete env.DEPLOY;
if (input.deploy) {
const frameworkVersion = require_framework_version.readInstalledFrameworkVersion(input.configDir, "everything-dev");
if (frameworkVersion) env.MF_BUILD_VERSION = frameworkVersion;
else delete env.MF_BUILD_VERSION;
}
const root = require_workspace.findWorkspaceRoot(input.configDir);
const force = input.deploy && (input.forceBuild === true || node_process.default.env.BOS_FORCE_BUILD === "1");
const depsReport = await (deps.freshDeps ?? require_workspace.ensureFreshDeps)(input.configDir, existing.map((entry) => entry.path), {
force,
env,
...input.deploy && root && !force ? { oracle: require_workspace.contentHashStalenessOracle(root.dir, root.members) } : {}
});
if (depsReport.rebuilt.length > 0) render?.prerequisites?.(depsReport.rebuilt.map((member) => member.name));
const orderedExisting = input.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 = input.deploy ? orderedExisting.filter((e) => e.key !== "host") : orderedExisting;
const sequentialGroup = input.deploy ? orderedExisting.filter((e) => e.key === "host") : [];
const built = [];
const deployResults = [];
if (input.deploy && parallelGroup.length > 0) {
const total = parallelGroup.length + sequentialGroup.length;
render?.banner?.(total);
const results = await mapWithConcurrency(parallelGroup, (ws) => buildOneWorkspace(ws, env, input, deps), buildConcurrencyCap(input.concurrency));
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",
warnings: []
});
}
for (const ws of sequentialGroup) {
const result = await buildOneWorkspace(ws, env, input, deps);
if (result.success) built.push(ws.key);
deployResults.push(result);
}
render?.end?.();
} else for (const resolved of orderedExisting) {
const buildConfig = buildCommands[resolved.key] ?? {
cmd: "pnpm",
args: ["run", "build"]
};
await (deps.runChild ?? require_run.run)(buildConfig.cmd, buildConfig.args, {
cwd: resolved.path,
env
});
built.push(resolved.key);
}
return {
built,
skipped,
deployResults: input.deploy ? deployResults : void 0
};
}
function buildConcurrencyCap(requested) {
const cpus = node_os.default.cpus().length;
return Math.max(1, Math.min(requested ?? cpus, cpus));
}
/** Runs `fn` over `items` with at most `limit` in flight; results keep input
* order. Rejections propagate after all in-flight work settles. */
async function mapWithConcurrency(items, fn, limit) {
const results = new Array(items.length);
let next = 0;
const workers = Array.from({ length: Math.max(1, Math.min(limit, items.length)) }, async () => {
while (true) {
const index = next++;
if (index >= items.length) return;
try {
results[index] = {
status: "fulfilled",
value: await fn(items[index], index)
};
} catch (reason) {
results[index] = {
status: "rejected",
reason
};
}
}
});
await Promise.all(workers);
return results;
}
/** Presentation seam the composition root wires to `cli/render` (#424). */
var WorkspaceBuildRenderService = class extends effect.Context.Service()("everything-dev/workspace/build-render") {};
/**
* The build train as a Context.Service (ticket #424): parallel/sequential
* group logic and the prerequisite train behind one tag; the ✓ table renders
* through the composition root's render, never the service.
*/
var BuildTrainService = class BuildTrainService extends effect.Context.Service()("everything-dev/workspace/build-train") {
static Live = effect.Layer.effect(this, effect.Effect.gen(function* () {
const renderOption = yield* effect.Effect.serviceOption(WorkspaceBuildRenderService);
return BuildTrainService.of({ buildTargets: (input, deps) => buildWorkspaceTargets(input, {
...deps,
render: deps?.render ?? effect.Option.getOrUndefined(renderOption)
}) });
}));
};
//#endregion
exports.BuildTrainService = BuildTrainService;
exports.WorkspaceBuildRenderService = WorkspaceBuildRenderService;
exports.buildConcurrencyCap = buildConcurrencyCap;
exports.buildWorkspaceTargets = buildWorkspaceTargets;
exports.extractBuildWarnings = extractBuildWarnings;
exports.fileExists = fileExists;
exports.getPluginRef = getPluginRef;
exports.mapWithConcurrency = mapWithConcurrency;
exports.resolveWorkspaceTarget = resolveWorkspaceTarget;
exports.selectWorkspaceTargets = selectWorkspaceTargets;
//# sourceMappingURL=build-train.cjs.map