UNPKG

everything-dev

Version:

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

231 lines (229 loc) • 9.51 kB
import { parseBosUrl } from "../fastkv.mjs"; import { saveBosConfig } from "../utils/save-config.mjs"; import { buildInitPatterns, buildPluginRouteExclusions, buildStarterRouteExclusions, convertChildConfigToAppForm, copyFilteredFiles, detectGitRemoteUrl, fetchParentConfig, generateDatabaseMigrations, personalizeAgentsMd, personalizeConfig, removeStrayLockfiles, resolveSourceDir, runPnpmInstall, runTypesGen, scaffoldMinimalProject, writeDevOverlayTemplate, writeInitSnapshot } from "../cli/init.mjs"; import { openResolution } from "../resolution/session.mjs"; import { syncResolvedSharedDeps } from "../shared-deps.mjs"; import { generateCodeArtifacts } from "../code-artifacts.mjs"; import { makeProjectEnv } from "../env/project-env.mjs"; import { materializeViaLayer } from "../infra/materializer.mjs"; import { timePhase } from "../progress.mjs"; import { pruneUnusedUiFiles } from "../cli/prune.mjs"; import { existsSync } from "node:fs"; import { join, resolve } from "node:path"; import { Effect } from "effect"; import process from "node:process"; //#region src/commands/init.ts async function fetchInitParent(extendsAccount, extendsGateway) { const parentConfig = await fetchParentConfig(extendsAccount, extendsGateway); return { parentConfig, parentPluginKeys: parentConfig?.plugins && typeof parentConfig.plugins === "object" ? Object.keys(parentConfig.plugins) : [] }; } function normalizeExtendsRef(extendsInput, fallback) { if (!extendsInput) return fallback; return extendsInput.startsWith("bos://") ? extendsInput : `bos://${extendsInput}`; } function registerInit(builder) { return { init: builder.init.handler(async ({ input }) => { try { const timings = []; let extendsAccount = ""; let extendsGateway = ""; let directory = input.directory; const account = input.account; const domain = input.domain; let overrides = input.overrides; let plugins = input.plugins; if (input.extends) try { const { accountId, gatewayId } = parseBosUrl(normalizeExtendsRef(input.extends, "")); extendsAccount = accountId; extendsGateway = gatewayId; } catch { extendsAccount = ""; extendsGateway = ""; } extendsAccount = extendsAccount || "dev.everything.near"; extendsGateway = extendsGateway || "everything.dev"; directory = directory || domain || extendsGateway; const targetDir = resolve(directory); const extendsRef = `bos://${extendsAccount}/${extendsGateway}`; let parentPluginKeys = []; let parentConfig = null; try { const parent = await timePhase(timings, "parent config", () => fetchInitParent(extendsAccount, extendsGateway)); parentConfig = parent.parentConfig; parentPluginKeys = parent.parentPluginKeys; } catch (e) { console.warn(`[init] Failed to fetch parent config from ${extendsAccount}/${extendsGateway}: ${e instanceof Error ? e.message : String(e)}`); return { status: "error", directory, extendsRef, account, domain, extends: extendsRef, plugins, overrides, filesCopied: 0, timings, error: `No config found at ${extendsRef} — are you sure this is the right parent?` }; } overrides = overrides?.length ? overrides : ["ui", "api"]; const level = input.level ?? "simple"; if (overrides.includes("plugins") && plugins === void 0) plugins = parentPluginKeys; plugins = plugins ?? []; const pluginDirMap = {}; if (parentConfig?.plugins) for (const plugin of plugins) { const entry = parentConfig.plugins?.[plugin]; if (entry && typeof entry === "object") { const dev = entry.development; if (typeof dev === "string") { const match = dev.match(/^local:plugins\/(.+)$/); if (match?.[1] && match[1] !== plugin) pluginDirMap[plugin] = match[1]; } } } const repository = await detectGitRemoteUrl(process.cwd()).catch(() => void 0) ?? parentConfig?.repository; const { sourceDir, parentConfig: resolvedParentConfig, cleanup } = await timePhase(timings, "template source", () => resolveSourceDir({ extendsAccount, extendsGateway, source: input.source })); parentConfig = resolvedParentConfig; const isMinimalScaffold = sourceDir === ""; try { let filesCopied; let childBosConfig = null; if (isMinimalScaffold) { filesCopied = await timePhase(timings, "scaffold project", () => scaffoldMinimalProject(targetDir, parentConfig, { extendsAccount, extendsGateway, account: account || extendsAccount, domain, plugins, overrides, repository, title: parentConfig?.title, description: parentConfig?.description, starter: level })); await timePhase(timings, "personalize config", () => personalizeConfig(targetDir, { extendsAccount, extendsGateway, account: account || extendsAccount, domain: domain || extendsGateway, plugins, overrides, mode: "init", repository, title: parentConfig?.title, description: parentConfig?.description, testnet: parentConfig?.testnet, staging: parentConfig?.staging, starter: level })); childBosConfig = await timePhase(timings, "authored config form", () => convertChildConfigToAppForm(targetDir)); writeDevOverlayTemplate(targetDir, { extendsRef: `bos://${extendsAccount}/${extendsGateway}` }); } else { const patterns = buildInitPatterns(overrides, plugins, pluginDirMap); const routeExclusions = overrides.includes("ui") ? buildPluginRouteExclusions(parentConfig, plugins) : []; const starterExclusions = overrides.includes("ui") ? buildStarterRouteExclusions(level, parentConfig) : []; const copyIgnore = [...routeExclusions, ...starterExclusions]; filesCopied = await timePhase(timings, "copy files", () => copyFilteredFiles(sourceDir, targetDir, patterns, { overrides, plugins, ignore: copyIgnore })); if (!existsSync(join(targetDir, "bos.config.json"))) await saveBosConfig(targetDir, parentConfig); await timePhase(timings, "personalize config", () => personalizeConfig(targetDir, { extendsAccount, extendsGateway, account: account || extendsAccount, domain: domain || extendsGateway, plugins, overrides, workspaceOpts: { sourceDir }, repository, title: parentConfig?.title, description: parentConfig?.description, testnet: parentConfig?.testnet, staging: parentConfig?.staging, starter: level })); childBosConfig = await timePhase(timings, "authored config form", () => convertChildConfigToAppForm(targetDir)); writeDevOverlayTemplate(targetDir, { extendsRef: `bos://${extendsAccount}/${extendsGateway}` }); if (overrides.includes("ui")) await timePhase(timings, "prune unused ui files", async () => pruneUnusedUiFiles(targetDir, { log: console.log })); await timePhase(timings, "write snapshot", () => writeInitSnapshot(targetDir, extendsAccount, extendsGateway, sourceDir, patterns, { overrides, plugins, ignore: copyIgnore, starter: level })); await timePhase(timings, "personalize agents", () => personalizeAgentsMd(targetDir, { overrides, plugins })); } await timePhase(timings, "sync shared deps", () => syncResolvedSharedDeps({ configDir: targetDir, hostMode: "local", bosConfig: childBosConfig ? childBosConfig : void 0 })); removeStrayLockfiles(targetDir); const initConfig = await timePhase(timings, "resolve config", () => openResolution({ cwd: targetDir }).catch((error) => { console.warn("[init] Skipping config resolution — the child has no node_modules yet; `bos dev` resolves after `pnpm install`.", error instanceof Error ? error.message : error); return null; })); const initRuntime = initConfig?.runtime; if (initRuntime) await timePhase(timings, "generate env/docker", async () => { await materializeViaLayer(targetDir, initRuntime); }); await timePhase(timings, "create env file", async () => { await Effect.runPromise(makeProjectEnv().ensureFile(targetDir)); }); if (!input.noInstall) { await timePhase(timings, "install dependencies", () => runPnpmInstall(targetDir)); await timePhase(timings, "generate types", () => runTypesGen(targetDir)); await timePhase(timings, "generate migrations", () => generateDatabaseMigrations(targetDir)); } const initArtifactsConfig = initConfig?.config; if (input.noInstall && initArtifactsConfig) await timePhase(timings, "generate code artifacts", () => generateCodeArtifacts(targetDir, initArtifactsConfig)); return { status: "initialized", directory, extendsRef, account, domain, extends: extendsRef, plugins, overrides, filesCopied, timings, targetDir }; } finally { await cleanup(); } } catch (error) { const extendsRef = normalizeExtendsRef(input.extends, "bos://dev.everything.near/everything.dev"); return { status: "error", directory: input.directory ?? "", extendsRef, account: input.account, domain: input.domain, extends: extendsRef, plugins: input.plugins ?? [], overrides: input.overrides, filesCopied: 0, timings: [], error: error instanceof Error ? error.message : "Unknown error" }; } }) }; } //#endregion export { fetchInitParent, registerInit }; //# sourceMappingURL=init.mjs.map