everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
231 lines (229 loc) • 9.51 kB
JavaScript
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