UNPKG

kitcn

Version:

kitcn - React Query integration and CLI tools for Convex

159 lines (157 loc) 6.37 kB
#!/usr/bin/env node import { Dt as generateMeta, F as resolveRunDeps, Ot as getConvexConfig, j as resolveConfiguredBackend, kt as logger, q as withLocalCodegenEnv } from "./backend-core-cwf87w_T.mjs"; import path from "node:path"; import { fileURLToPath } from "node:url"; //#region src/cli/watcher.ts function getWatchRoots(functionsDir) { const convexDir = path.dirname(functionsDir); return [functionsDir, path.join(convexDir, "routers")]; } function parseWatcherBackendEnv(value) { if (value === "convex" || value === "concave") return value; } function resolveWatcherCliCommand(currentFilename = fileURLToPath(import.meta.url)) { const currentDir = path.dirname(currentFilename); const isTs = currentFilename.endsWith(".ts"); return { cliPath: isTs ? path.join(currentDir, "cli.ts") : path.join(currentDir, "cli.mjs"), runtime: isTs ? "bun" : "node" }; } function parseTrimSegmentsEnv(value) { if (!value) return; const parseFromArray = (segments) => { const normalized = [...new Set(segments.map((segment) => segment.trim()).filter((segment) => segment))]; return normalized.length > 0 ? normalized : void 0; }; const trimmed = value.trim(); if (!trimmed) return; if (trimmed.startsWith("[")) try { const parsed = JSON.parse(trimmed); if (Array.isArray(parsed) && parsed.every((segment) => typeof segment === "string")) return parseFromArray(parsed); } catch {} return parseFromArray(trimmed.split(",")); } function shouldIgnoreWatchPath(watchedPath, functionsDir, outputFile) { const normalizedPath = path.resolve(watchedPath); const normalizedFunctionsDir = path.resolve(functionsDir); const normalizedOutputFile = path.resolve(outputFile); const convexGeneratedDir = path.join(normalizedFunctionsDir, "_generated"); const generatedDir = path.join(normalizedFunctionsDir, "generated"); const generatedFile = path.join(normalizedFunctionsDir, "generated.ts"); if (normalizedPath === normalizedOutputFile) return true; if (normalizedPath === generatedFile) return true; if (normalizedPath === convexGeneratedDir || normalizedPath.startsWith(`${convexGeneratedDir}${path.sep}`)) return true; if (normalizedPath === generatedDir || normalizedPath.startsWith(`${generatedDir}${path.sep}`)) return true; return normalizedPath.endsWith(".runtime.ts") || normalizedPath.endsWith(".kitcn-parse.ts"); } async function runWatcherCodegen(params, deps = {}) { const resolveRunDepsFn = deps.resolveRunDeps ?? resolveRunDeps; const resolveConfiguredBackendFn = deps.resolveConfiguredBackendFn ?? resolveConfiguredBackend; const { execa: execaFn, generateMeta: generateMetaFn, loadCliConfig: loadCliConfigFn } = resolveRunDepsFn(); const config = loadCliConfigFn(params.configPath); const backend = resolveConfiguredBackendFn({ backendArg: params.backendArg, config }); if (backend !== "concave") { await withLocalCodegenEnv(params.sharedDir, backend, async () => { await generateMetaFn(params.sharedDir, { debug: params.debug, silent: true, scope: params.scope, trimSegments: params.trimSegments }); }); return; } const { cliPath, runtime } = resolveWatcherCliCommand(); const args = [ cliPath, "codegen", "--backend", "concave", "--scope", params.scope ]; if (params.sharedDir) args.push("--api", params.sharedDir); if (params.configPath) args.push("--config", params.configPath); if (params.debug) args.push("--debug"); const result = await execaFn(runtime, args, { cwd: process.cwd(), reject: false, stdio: "pipe" }); if (result.exitCode !== 0) throw new Error(`Watcher codegen failed with exit code ${result.exitCode}.\n${`${result.stdout ?? ""}\n${result.stderr ?? ""}`.trim()}`); } async function startWatcher(opts) { const backendArg = opts?.backend ?? parseWatcherBackendEnv(process.env.KITCN_BACKEND); const configPath = opts?.configPath ?? process.env.KITCN_CONFIG_PATH; const sharedDir = opts?.sharedDir ?? (process.env.KITCN_API_OUTPUT_DIR || void 0); const debug = opts?.debug ?? process.env.KITCN_DEBUG === "1"; const scope = opts?.scope ?? process.env.KITCN_CODEGEN_SCOPE ?? "all"; const trimSegments = opts?.trimSegments ?? parseTrimSegmentsEnv(process.env.KITCN_CODEGEN_TRIM_SEGMENTS); const debounceMs = opts?.debounceMs ?? 100; const resolveConfig = opts?.getConvexConfig ?? getConvexConfig; const runGenerateMeta = opts?.generateMeta ?? generateMeta; const runWatchCodegen = opts?.runWatcherCodegen ?? runWatcherCodegen; const { functionsDir, outputFile } = resolveConfig(sharedDir); const watchRoots = getWatchRoots(functionsDir); const watch = opts?.watch ?? (await import("chokidar")).watch; let debounceTimer = null; let generateMetaInFlight = false; let generateMetaQueued = false; const createGenerateOptions = () => { const generateOptions = { debug, silent: true, scope }; if (trimSegments && trimSegments.length > 0) generateOptions.trimSegments = trimSegments; return generateOptions; }; const runGenerateMetaSafely = async () => { if (generateMetaInFlight) { generateMetaQueued = true; return; } generateMetaInFlight = true; try { if (backendArg || configPath) await runWatchCodegen({ backendArg, configPath, debug, scope, sharedDir, trimSegments }); else await runGenerateMeta(sharedDir, createGenerateOptions()); logger.success("Convex api updated"); } catch (error) { logger.error("Watch codegen error:", error); } finally { generateMetaInFlight = false; if (generateMetaQueued) { generateMetaQueued = false; scheduleGenerateMeta(); } } }; const scheduleGenerateMeta = () => { if (debounceTimer) clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { debounceTimer = null; runGenerateMetaSafely(); }, debounceMs); }; return watch(watchRoots, { ignoreInitial: true, ignored: (watchedPath) => shouldIgnoreWatchPath(watchedPath, functionsDir, outputFile) }).on("add", scheduleGenerateMeta).on("change", scheduleGenerateMeta).on("unlink", scheduleGenerateMeta).on("error", (err) => logger.error("Watch error:", err)); } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) startWatcher().catch((error) => { logger.error("Watch error:", error); process.exitCode = 1; }); //#endregion export { getWatchRoots, runWatcherCodegen, shouldIgnoreWatchPath, startWatcher };