UNPKG

everything-dev

Version:

A consolidated product package for building Module Federation apps with oRPC APIs.

272 lines (270 loc) • 10.1 kB
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