import fs, { globSync } from "node:fs"; import path from "node:path"; import { type CommandResult, failure, success } from "../../../lib/command-result"; import { parseTestCasesFromDoc } from "../../../lib/parse-doc-test-cases"; import { APP_PATHS } from "../../../lib/paths"; import { parseResolverDoc } from "./parse-resolver-doc"; import { renderResolverStub, renderStoryTestStub } from "./stubs"; export function runAppGenerateCode(appPath: string): CommandResult { const appName = path.basename(appPath); const resolverResult = generateResolverStubs(appPath, appName); if (resolverResult.exitCode !== 0) return resolverResult; generateStoryTestStubs(appPath, appName); return success(); } function generateResolverStubs(appPath: string, appName: string): CommandResult { const docsDir = path.join(appPath, APP_PATHS.docs.resolver); if (!fs.existsSync(docsDir)) return success(); const mdFiles = fs.readdirSync(docsDir).filter((f) => f.endsWith(".md")); if (mdFiles.length === 0) return success(); const resolverDir = path.join(appPath, APP_PATHS.code.resolver); fs.mkdirSync(resolverDir, { recursive: true }); let generated = 0; const missingOverview: string[] = []; for (const file of mdFiles) { const content = fs.readFileSync(path.join(docsDir, file), "utf-8"); const fileName = path.basename(file, ".md"); const doc = parseResolverDoc(fileName, content); const implFile = path.join(resolverDir, `${doc.resolverName}.ts`); if (fs.existsSync(implFile)) continue; if (!doc.overview) { missingOverview.push(file); continue; } fs.writeFileSync(implFile, renderResolverStub(doc.resolverName, doc.overview)); console.log(` scaffolded ${path.relative(appPath, implFile)}`); generated++; } if (generated > 0) { console.log(`Scaffolded ${generated} resolver(s) for ${appName}`); } if (missingOverview.length > 0) { return failure( `resolver doc(s) need a non-empty "## Overview" first paragraph — it becomes the resolver description:\n` + missingOverview.map((f) => ` - ${path.join(APP_PATHS.docs.resolver, f)}`).join("\n"), ); } return success(); } function generateStoryTestStubs(appPath: string, appName: string): void { const storyPattern = path.join(appPath, "docs/business-flow/*/story/*.md"); const storyDocs = globSync(storyPattern); let generated = 0; for (const storyDocPath of storyDocs) { const content = fs.readFileSync(storyDocPath, "utf-8"); const testCases = parseTestCasesFromDoc(content); if (testCases.length === 0) continue; const regex = /\/docs\/business-flow\/([^/]+)\/story\/([^/]+)\.md$/; const match = regex.exec(storyDocPath); if (!match) continue; const [, flow, storyName] = match; const testDir = path.join(appPath, APP_PATHS.tests.story, flow); const testFile = path.join(testDir, `${storyName}.test.ts`); if (fs.existsSync(testFile)) continue; fs.mkdirSync(testDir, { recursive: true }); fs.writeFileSync(testFile, renderStoryTestStub(storyName, testCases)); console.log(` scaffolded ${path.relative(appPath, testFile)}`); generated++; } if (generated > 0) { console.log(`Scaffolded ${generated} story test(s) for ${appName}`); } }