@screenbook/cli 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["CONFIG_FILES","config: Awaited<ReturnType<typeof loadConfig>>","coverage: CoverageResult","content: string","extension: string","duplicateIds: string[]","cycles: CycleInfo[]","path: string[]","current: string | null | undefined","lines: string[]","matches: Array<{ candidate: string; distance: number }>","matrix: number[][]","errors: ValidationError[]","lines: string[]","screens: ScreenWithFilePath[]","routeFiles: string[]","missing: CoverageData[\"missing\"]","byOwner: CoverageData[\"byOwner\"]","byTag: CoverageData[\"byTag\"]","lines: string[]","resolveUiPackage","screens: ScreenWithFilePath[]","results: CheckResult[]","version","summary: string[]","result: FlatRoute[]","fullPath: string","warnings: string[]","content: string","ast: ReturnType<typeof parse>","routes: ParsedRoute[]","parseRoutesArray","classNode: any","parseRouteObject","path: string | undefined","component: string | undefined","children: ParsedRoute[] | undefined","redirectTo: string | undefined","loc","navigations: DetectedNavigation[]","warnings: string[]","ast: ReturnType<typeof parse>","line","templateNavigations: DetectedNavigation[]","scriptNavigations: DetectedNavigation[]","warnings: string[]","warning: string","navigations: DetectedNavigation[]","parserErrors: string[]","imports: DetectedApiImport[]","warnings: string[]","ast: ReturnType<typeof parse>","warnings: string[]","content: string","ast: ReturnType<typeof parse>","routes: ParsedRoute[]","parseRoutesArray","parseRouteObject","paths: string[]","component: string | undefined","children: ParsedRoute[] | undefined","extractLazyImportPath","loc","warnings: string[]","content: string","ast: ReturnType<typeof parse>","extractLazyImportPath","routeDef: RouteDefinition","parentVarName: string | undefined","childNames: string[]","routes: ParsedRoute[]","route: ParsedRoute","children: ParsedRoute[]","warnings: string[]","content: string","ast: ReturnType<typeof parse>","routes: ParsedRoute[]","parseRoutesArray","parseRouteObject","route: ParsedRoute","parts: string[]","loc","warnings: string[]","content: string","ast: ReturnType<typeof parse>","routes: ParsedRoute[]","route: ParsedRoute","templateNavigations: DetectedNavigation[]","scriptNavigations: DetectedNavigation[]","warnings: string[]","parse","navigations: DetectedNavigation[]","parseResult: ParseResult","screenMeta: InferredScreenMeta","detectedApis: string[]","detectedNext: string[]","componentContent: string","routeContent: string","transitive: TransitiveDependency[]","queue: Array<{ id: string; path: string[] }>","lines: string[]","screens: Screen[]","FRAMEWORKS: FrameworkDefinition[]","screens: ScreenWithFilePath[]","framework: FrameworkInfo | null","specs: ParsedOpenApiSpec[]","errors: OpenApiParseError[]","identifiers: string[]","errors: DependsOnValidationError[]","missingMeta: string[]","covered: string[]","flatRoutes: FlatRoute[]","parseResult: ParseResult","missingMeta: FlatRoute[]","covered: FlatRoute[]","invalid: InvalidNavigation[]","orphans: Screen[]","lines: string[]","changedFiles: string[]","screens: Screen[]","results: ImpactResult[]","version: string"],"sources":["../src/utils/config.ts","../src/utils/errors.ts","../src/utils/logger.ts","../src/commands/badge.ts","../src/utils/cycleDetection.ts","../src/utils/suggestions.ts","../src/utils/validation.ts","../src/commands/build.ts","../src/commands/dev.ts","../src/commands/doctor.ts","../src/utils/routeParserUtils.ts","../src/utils/angularRouterParser.ts","../src/utils/navigationAnalyzer.ts","../src/utils/angularTemplateAnalyzer.ts","../src/utils/apiImportAnalyzer.ts","../src/utils/solidRouterParser.ts","../src/utils/tanstackRouterParser.ts","../src/utils/reactRouterParser.ts","../src/utils/vueRouterParser.ts","../src/utils/vueSFCTemplateAnalyzer.ts","../src/commands/generate.ts","../src/utils/impactAnalysis.ts","../src/commands/impact.ts","../src/utils/detectFramework.ts","../src/utils/isInteractive.ts","../src/commands/init.ts","../src/utils/openApiParser.ts","../src/utils/dependsOnValidation.ts","../src/commands/lint.ts","../src/utils/prImpact.ts","../src/commands/pr-impact.ts","../src/index.ts"],"sourcesContent":["import { existsSync } from \"node:fs\"\nimport { resolve } from \"node:path\"\nimport { type Config, defineConfig } from \"@screenbook/core\"\nimport { createJiti } from \"jiti\"\n\nconst CONFIG_FILES = [\n\t\"screenbook.config.ts\",\n\t\"screenbook.config.js\",\n\t\"screenbook.config.mjs\",\n]\n\nexport async function loadConfig(configPath?: string): Promise<Config> {\n\tconst cwd = process.cwd()\n\n\t// If config path is provided, use it\n\tif (configPath) {\n\t\tconst absolutePath = resolve(cwd, configPath)\n\t\tif (!existsSync(absolutePath)) {\n\t\t\tthrow new Error(`Config file not found: ${configPath}`)\n\t\t}\n\t\treturn await importConfig(absolutePath, cwd)\n\t}\n\n\t// Search for config file in cwd\n\tfor (const configFile of CONFIG_FILES) {\n\t\tconst absolutePath = resolve(cwd, configFile)\n\t\tif (existsSync(absolutePath)) {\n\t\t\treturn await importConfig(absolutePath, cwd)\n\t\t}\n\t}\n\n\t// Return default config if no config file found\n\treturn defineConfig()\n}\n\nasync function importConfig(\n\tabsolutePath: string,\n\tcwd: string,\n): Promise<Config> {\n\tconst jiti = createJiti(cwd)\n\tconst module = (await jiti.import(absolutePath)) as { default?: Config }\n\n\tif (module.default) {\n\t\treturn module.default\n\t}\n\n\tthrow new Error(`Config file must have a default export: ${absolutePath}`)\n}\n","import type { ErrorOptions } from \"./logger.js\"\n\n/**\n * Centralized error definitions for consistent error messages across CLI commands\n */\nexport const ERRORS = {\n\t// ============================================\n\t// Configuration Errors\n\t// ============================================\n\n\tROUTES_PATTERN_MISSING: {\n\t\ttitle: \"Routes configuration not found\",\n\t\tsuggestion:\n\t\t\t\"Add routesPattern (for file-based routing) or routesFile (for config-based routing) to your screenbook.config.ts.\",\n\t\texample: `import { defineConfig } from \"@screenbook/core\"\n\n// Option 1: File-based routing (Next.js, Nuxt, etc.)\nexport default defineConfig({\n routesPattern: \"src/pages/**/page.tsx\",\n})\n\n// Option 2: Config-based routing (Vue Router, React Router, etc.)\nexport default defineConfig({\n routesFile: \"src/router/routes.ts\",\n})`,\n\t} satisfies ErrorOptions,\n\n\tROUTES_FILE_NOT_FOUND: (filePath: string): ErrorOptions => ({\n\t\ttitle: `Routes file not found: ${filePath}`,\n\t\tsuggestion:\n\t\t\t\"Check the routesFile path in your screenbook.config.ts. The file should export a routes array.\",\n\t\texample: `import { defineConfig } from \"@screenbook/core\"\n\nexport default defineConfig({\n routesFile: \"src/router/routes.ts\", // Make sure this file exists\n})`,\n\t}),\n\n\tROUTES_FILE_PARSE_ERROR: (filePath: string, error: string): ErrorOptions => ({\n\t\ttitle: `Failed to parse routes file: ${filePath}`,\n\t\tmessage: error,\n\t\tsuggestion:\n\t\t\t\"Ensure the file exports a valid routes array. Check for syntax errors or unsupported patterns.\",\n\t}),\n\n\tCONFIG_NOT_FOUND: {\n\t\ttitle: \"Configuration file not found\",\n\t\tsuggestion:\n\t\t\t\"Run 'screenbook init' to create a screenbook.config.ts file, or create one manually.\",\n\t\texample: `import { defineConfig } from \"@screenbook/core\"\n\nexport default defineConfig({\n metaPattern: \"src/**/screen.meta.ts\",\n})`,\n\t} satisfies ErrorOptions,\n\n\tNO_ROUTES_FOUND: (pattern: string): ErrorOptions => ({\n\t\ttitle: `No routes found matching pattern: ${pattern}`,\n\t\tsuggestion: \"Check your routesPattern in screenbook.config.ts.\",\n\t\texample: `// Common patterns:\n\"src/app/**/page.tsx\" // Next.js App Router\n\"src/pages/**/*.tsx\" // Pages Router\n\"src/pages/**/*.vue\" // Vue/Nuxt\n\"src/routes/**/*.tsx\" // SolidStart/QwikCity`,\n\t}),\n\n\t// ============================================\n\t// Build/File Errors\n\t// ============================================\n\n\tSCREENS_NOT_FOUND: {\n\t\ttitle: \"screens.json not found\",\n\t\tsuggestion: \"Run 'screenbook build' first to generate the screen catalog.\",\n\t\tmessage:\n\t\t\t\"If you haven't set up Screenbook yet, run 'screenbook init' to get started.\",\n\t} satisfies ErrorOptions,\n\n\tSCREENS_PARSE_ERROR: {\n\t\ttitle: \"Failed to parse screens.json\",\n\t\tsuggestion:\n\t\t\t\"The screens.json file may be corrupted. Try running 'screenbook build' to regenerate it.\",\n\t} satisfies ErrorOptions,\n\n\tMETA_FILE_LOAD_ERROR: (filePath: string): ErrorOptions => ({\n\t\ttitle: `Failed to load ${filePath}`,\n\t\tsuggestion:\n\t\t\t\"Check the file for syntax errors or missing exports. The file should export a 'screen' object.\",\n\t\texample: `import { defineScreen } from \"@screenbook/core\"\n\nexport const screen = defineScreen({\n id: \"example.screen\",\n title: \"Example Screen\",\n route: \"/example\",\n})`,\n\t}),\n\n\tFILE_READ_ERROR: (filePath: string, error: string): ErrorOptions => ({\n\t\ttitle: `Failed to read file: ${filePath}`,\n\t\tmessage: error,\n\t\tsuggestion: \"Check if the file exists and you have read permissions.\",\n\t}),\n\n\tPARSE_ERROR: (filePath: string, error: string): ErrorOptions => ({\n\t\ttitle: `Failed to parse: ${filePath}`,\n\t\tmessage: error,\n\t\tsuggestion: \"Check for syntax errors in the file.\",\n\t}),\n\n\tSCREEN_NOT_FOUND: (\n\t\tscreenId: string,\n\t\tsuggestions?: string[],\n\t): ErrorOptions => ({\n\t\ttitle: `Screen \"${screenId}\" not found`,\n\t\tmessage:\n\t\t\tsuggestions && suggestions.length > 0\n\t\t\t\t? `Did you mean one of these?\\n${suggestions.map((s) => ` - ${s}`).join(\"\\n\")}`\n\t\t\t\t: undefined,\n\t\tsuggestion: \"Check the screen ID in your screen.meta.ts file.\",\n\t}),\n\n\t// ============================================\n\t// Command Argument Errors\n\t// ============================================\n\n\tAPI_NAME_REQUIRED: {\n\t\ttitle: \"API name is required\",\n\t\tsuggestion: \"Provide the API name as an argument.\",\n\t\texample: `screenbook impact UserAPI.getProfile\nscreenbook impact \"PaymentAPI.*\" # Use quotes for patterns`,\n\t} satisfies ErrorOptions,\n\n\t// ============================================\n\t// Git Errors\n\t// ============================================\n\n\tGIT_CHANGED_FILES_ERROR: (baseBranch: string): ErrorOptions => ({\n\t\ttitle: \"Failed to get changed files from git\",\n\t\tmessage: `Make sure you are in a git repository and the base branch '${baseBranch}' exists.`,\n\t\tsuggestion: `Verify the base branch exists with: git branch -a | grep ${baseBranch}`,\n\t}),\n\n\tGIT_NOT_REPOSITORY: {\n\t\ttitle: \"Not a git repository\",\n\t\tsuggestion:\n\t\t\t\"This command requires a git repository. Initialize one with 'git init' or navigate to an existing repository.\",\n\t} satisfies ErrorOptions,\n\n\t// ============================================\n\t// Server Errors\n\t// ============================================\n\n\tSERVER_START_FAILED: (error: string): ErrorOptions => ({\n\t\ttitle: \"Failed to start development server\",\n\t\tmessage: error,\n\t\tsuggestion:\n\t\t\t\"Check if the port is already in use or if there are any dependency issues.\",\n\t}),\n\n\t// ============================================\n\t// Validation Errors\n\t// ============================================\n\n\tVALIDATION_FAILED: (errorCount: number): ErrorOptions => ({\n\t\ttitle: `Validation failed with ${errorCount} error${errorCount === 1 ? \"\" : \"s\"}`,\n\t\tsuggestion:\n\t\t\t\"Fix the validation errors above. Screen references must point to existing screens.\",\n\t}),\n\n\tINVALID_API_DEPENDENCIES: (count: number): ErrorOptions => ({\n\t\ttitle: `${count} invalid API ${count === 1 ? \"dependency\" : \"dependencies\"} detected`,\n\t\tsuggestion:\n\t\t\t\"Fix the dependsOn values to match operationIds or HTTP endpoints from your OpenAPI spec.\",\n\t\texample: `// Using operationId\ndependsOn: [\"getInvoiceById\", \"updatePaymentStatus\"]\n\n// Using HTTP method + path\ndependsOn: [\"GET /api/invoices/{id}\", \"POST /api/payments\"]`,\n\t}),\n\n\t// ============================================\n\t// OpenAPI Errors\n\t// ============================================\n\n\tOPENAPI_PARSE_ERROR: (source: string): ErrorOptions => ({\n\t\ttitle: `Failed to parse OpenAPI spec: ${source}`,\n\t\tsuggestion:\n\t\t\t\"Check that the file path is correct and the OpenAPI specification is valid YAML or JSON.\",\n\t}),\n\n\t// ============================================\n\t// Lint Errors\n\t// ============================================\n\n\tLINT_MISSING_META: (\n\t\tmissingCount: number,\n\t\ttotalRoutes: number,\n\t): ErrorOptions => ({\n\t\ttitle: `${missingCount} route${missingCount === 1 ? \"\" : \"s\"} missing screen.meta.ts`,\n\t\tmessage: `Found ${totalRoutes} route file${totalRoutes === 1 ? \"\" : \"s\"}, but ${missingCount} ${missingCount === 1 ? \"is\" : \"are\"} missing colocated screen.meta.ts.`,\n\t\tsuggestion:\n\t\t\t\"Add screen.meta.ts files next to your route files, or run 'screenbook generate' to create them.\",\n\t}),\n\n\t// ============================================\n\t// Cycle Detection Errors\n\t// ============================================\n\n\tCYCLES_DETECTED: (cycleCount: number): ErrorOptions => ({\n\t\ttitle: `${cycleCount} circular navigation${cycleCount === 1 ? \"\" : \"s\"} detected`,\n\t\tsuggestion:\n\t\t\t\"Review the navigation flow. Use 'allowCycles: true' in screen.meta.ts to allow intentional cycles, or use --allow-cycles to suppress all warnings.\",\n\t\texample: `// Allow a specific screen to be part of cycles\nexport const screen = defineScreen({\n id: \"billing.invoice.detail\",\n next: [\"billing.invoices\"],\n allowCycles: true, // This cycle is intentional\n})`,\n\t}),\n}\n","import pc from \"picocolors\"\n\n// ============================================\n// Verbose Mode State\n// ============================================\n\nlet verboseMode = false\n\n/**\n * Enable or disable verbose mode for detailed output\n */\nexport function setVerbose(verbose: boolean): void {\n\tverboseMode = verbose\n}\n\n/**\n * Check if verbose mode is enabled\n */\nexport function isVerbose(): boolean {\n\treturn verboseMode\n}\n\n/**\n * Structured error options for detailed error messages\n */\nexport interface ErrorOptions {\n\t/** Error title (shown after \"Error:\") */\n\ttitle: string\n\t/** Additional error message/details */\n\tmessage?: string\n\t/** Actionable suggestion for the user */\n\tsuggestion?: string\n\t/** Code example to help the user */\n\texample?: string\n}\n\n/**\n * Logger utility for consistent, color-coded CLI output\n */\nexport const logger = {\n\t// ============================================\n\t// Status Messages\n\t// ============================================\n\n\t/**\n\t * Success message (green checkmark)\n\t */\n\tsuccess: (msg: string): void => {\n\t\tconsole.log(`${pc.green(\"✓\")} ${msg}`)\n\t},\n\n\t/**\n\t * Error message (red X)\n\t */\n\terror: (msg: string): void => {\n\t\tconsole.error(`${pc.red(\"✗\")} ${pc.red(`Error: ${msg}`)}`)\n\t},\n\n\t/**\n\t * Warning message (yellow warning sign)\n\t */\n\twarn: (msg: string): void => {\n\t\tconsole.log(`${pc.yellow(\"⚠\")} ${pc.yellow(`Warning: ${msg}`)}`)\n\t},\n\n\t/**\n\t * Info message (cyan info icon)\n\t */\n\tinfo: (msg: string): void => {\n\t\tconsole.log(`${pc.cyan(\"ℹ\")} ${msg}`)\n\t},\n\n\t// ============================================\n\t// Detailed Error with Guidance\n\t// ============================================\n\n\t/**\n\t * Display a detailed error with actionable suggestions\n\t */\n\terrorWithHelp: (options: ErrorOptions): void => {\n\t\tconst { title, message, suggestion, example } = options\n\n\t\tconsole.error()\n\t\tconsole.error(`${pc.red(\"✗\")} ${pc.red(`Error: ${title}`)}`)\n\n\t\tif (message) {\n\t\t\tconsole.error()\n\t\t\tconsole.error(` ${message}`)\n\t\t}\n\n\t\tif (suggestion) {\n\t\t\tconsole.error()\n\t\t\tconsole.error(` ${pc.cyan(\"Suggestion:\")} ${suggestion}`)\n\t\t}\n\n\t\tif (example) {\n\t\t\tconsole.error()\n\t\t\tconsole.error(` ${pc.dim(\"Example:\")}`)\n\t\t\tfor (const line of example.split(\"\\n\")) {\n\t\t\t\tconsole.error(` ${pc.dim(line)}`)\n\t\t\t}\n\t\t}\n\n\t\tconsole.error()\n\t},\n\n\t/**\n\t * Display an error with optional stack trace (shown only in verbose mode)\n\t */\n\terrorWithStack: (error: unknown, context?: string): void => {\n\t\tconst err = error instanceof Error ? error : new Error(String(error))\n\t\tconst message = context ? `${context}: ${err.message}` : err.message\n\t\tconsole.error(`${pc.red(\"✗\")} ${pc.red(`Error: ${message}`)}`)\n\t\tif (verboseMode && err.stack) {\n\t\t\tconsole.error()\n\t\t\tconsole.error(` ${pc.dim(\"Stack trace:\")}`)\n\t\t\tfor (const line of err.stack.split(\"\\n\").slice(1)) {\n\t\t\t\tconsole.error(` ${pc.dim(line)}`)\n\t\t\t}\n\t\t}\n\t\tconsole.error()\n\t},\n\n\t// ============================================\n\t// Progress Indicators\n\t// ============================================\n\n\t/**\n\t * Step indicator (dimmed arrow)\n\t */\n\tstep: (msg: string): void => {\n\t\tconsole.log(`${pc.dim(\"→\")} ${msg}`)\n\t},\n\n\t/**\n\t * Done/completed indicator (green checkmark with green text)\n\t */\n\tdone: (msg: string): void => {\n\t\tconsole.log(`${pc.green(\"✓\")} ${pc.green(msg)}`)\n\t},\n\n\t/**\n\t * Item success (green checkmark, indented)\n\t */\n\titemSuccess: (msg: string): void => {\n\t\tconsole.log(` ${pc.green(\"✓\")} ${msg}`)\n\t},\n\n\t/**\n\t * Item failure (red X, indented)\n\t */\n\titemError: (msg: string): void => {\n\t\tconsole.log(` ${pc.red(\"✗\")} ${msg}`)\n\t},\n\n\t/**\n\t * Item warning (yellow warning, indented)\n\t */\n\titemWarn: (msg: string): void => {\n\t\tconsole.log(` ${pc.yellow(\"⚠\")} ${msg}`)\n\t},\n\n\t// ============================================\n\t// Plain Output\n\t// ============================================\n\n\t/**\n\t * Plain log (no prefix)\n\t */\n\tlog: (msg: string): void => {\n\t\tconsole.log(msg)\n\t},\n\n\t/**\n\t * Blank line\n\t */\n\tblank: (): void => {\n\t\tconsole.log()\n\t},\n\n\t// ============================================\n\t// Formatting Helpers\n\t// ============================================\n\n\t/**\n\t * Bold text\n\t */\n\tbold: (msg: string): string => pc.bold(msg),\n\n\t/**\n\t * Dimmed text\n\t */\n\tdim: (msg: string): string => pc.dim(msg),\n\n\t/**\n\t * Code/command text (cyan)\n\t */\n\tcode: (msg: string): string => pc.cyan(msg),\n\n\t/**\n\t * File path text (underlined)\n\t */\n\tpath: (msg: string): string => pc.underline(msg),\n\n\t/**\n\t * Highlight text (cyan bold)\n\t */\n\thighlight: (msg: string): string => pc.cyan(pc.bold(msg)),\n\n\t/**\n\t * Green text\n\t */\n\tgreen: (msg: string): string => pc.green(msg),\n\n\t/**\n\t * Red text\n\t */\n\tred: (msg: string): string => pc.red(msg),\n\n\t/**\n\t * Yellow text\n\t */\n\tyellow: (msg: string): string => pc.yellow(msg),\n}\n","import { existsSync, mkdirSync, writeFileSync } from \"node:fs\"\nimport { dirname, join } from \"node:path\"\nimport { define } from \"gunshi\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger, setVerbose } from \"../utils/logger.js\"\n\nconst VALID_FORMATS = [\"svg\", \"json\", \"shields-json\"] as const\nconst VALID_STYLES = [\"flat\", \"flat-square\"] as const\n\ntype BadgeFormat = (typeof VALID_FORMATS)[number]\ntype BadgeStyle = (typeof VALID_STYLES)[number]\n\ninterface CoverageResult {\n\ttotal: number\n\tcovered: number\n\tpercentage: number\n}\n\n/**\n * Calculate coverage based on route files and meta files\n */\nasync function calculateCoverage(\n\troutesPattern: string,\n\tmetaPattern: string,\n\tignore: string[],\n\tcwd: string,\n): Promise<CoverageResult> {\n\t// Find all route files\n\tconst routeFiles = await glob(routesPattern, { cwd, ignore })\n\n\tif (routeFiles.length === 0) {\n\t\treturn { total: 0, covered: 0, percentage: 0 }\n\t}\n\n\t// Find all screen.meta.ts files\n\tconst metaFiles = await glob(metaPattern, { cwd, ignore })\n\n\t// Build a set of directories that have screen.meta.ts\n\tconst metaDirs = new Set<string>()\n\tfor (const metaFile of metaFiles) {\n\t\tmetaDirs.add(dirname(metaFile))\n\t}\n\n\t// Count covered routes\n\tlet covered = 0\n\tfor (const routeFile of routeFiles) {\n\t\tif (metaDirs.has(dirname(routeFile))) {\n\t\t\tcovered++\n\t\t}\n\t}\n\n\tconst total = routeFiles.length\n\tconst percentage = Math.round((covered / total) * 100)\n\n\treturn { total, covered, percentage }\n}\n\n/**\n * Get badge color based on coverage percentage\n */\nfunction getBadgeColor(percentage: number): string {\n\tif (percentage >= 80) return \"#4c1\" // Green\n\tif (percentage >= 50) return \"#dfb317\" // Yellow\n\treturn \"#e05d44\" // Red\n}\n\n/**\n * Get shields.io color name based on coverage percentage\n */\nfunction getShieldsColorName(percentage: number): string {\n\tif (percentage >= 80) return \"brightgreen\"\n\tif (percentage >= 50) return \"yellow\"\n\treturn \"red\"\n}\n\n/**\n * Generate SVG badge\n */\nexport function generateSvgBadge(\n\tpercentage: number,\n\tstyle: BadgeStyle = \"flat\",\n): string {\n\tconst color = getBadgeColor(percentage)\n\tconst label = \"screenbook\"\n\tconst value = `${percentage}%`\n\n\tconst labelWidth = 70\n\tconst valueWidth = 45\n\tconst totalWidth = labelWidth + valueWidth\n\tconst radius = style === \"flat\" ? 3 : 0\n\n\treturn `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${totalWidth}\" height=\"20\">\n <linearGradient id=\"b\" x2=\"0\" y2=\"100%\">\n <stop offset=\"0\" stop-color=\"#bbb\" stop-opacity=\".1\"/>\n <stop offset=\"1\" stop-opacity=\".1\"/>\n </linearGradient>\n <clipPath id=\"a\">\n <rect width=\"${totalWidth}\" height=\"20\" rx=\"${radius}\" fill=\"#fff\"/>\n </clipPath>\n <g clip-path=\"url(#a)\">\n <rect width=\"${labelWidth}\" height=\"20\" fill=\"#555\"/>\n <rect x=\"${labelWidth}\" width=\"${valueWidth}\" height=\"20\" fill=\"${color}\"/>\n <rect width=\"${totalWidth}\" height=\"20\" fill=\"url(#b)\"/>\n </g>\n <g fill=\"#fff\" text-anchor=\"middle\" font-family=\"Verdana,Geneva,sans-serif\" font-size=\"11\">\n <text x=\"${labelWidth / 2}\" y=\"15\" fill=\"#010101\" fill-opacity=\".3\">${label}</text>\n <text x=\"${labelWidth / 2}\" y=\"14\">${label}</text>\n <text x=\"${labelWidth + valueWidth / 2}\" y=\"15\" fill=\"#010101\" fill-opacity=\".3\">${value}</text>\n <text x=\"${labelWidth + valueWidth / 2}\" y=\"14\">${value}</text>\n </g>\n</svg>`\n}\n\n/**\n * Generate shields.io endpoint JSON\n */\nexport function generateShieldsJson(percentage: number): object {\n\treturn {\n\t\tschemaVersion: 1,\n\t\tlabel: \"screenbook\",\n\t\tmessage: `${percentage}%`,\n\t\tcolor: getShieldsColorName(percentage),\n\t}\n}\n\n/**\n * Generate simple JSON with coverage data\n */\nexport function generateSimpleJson(coverage: CoverageResult): object {\n\treturn {\n\t\tpercentage: coverage.percentage,\n\t\tcovered: coverage.covered,\n\t\ttotal: coverage.total,\n\t}\n}\n\nexport const badgeCommand = define({\n\tname: \"badge\",\n\tdescription: \"Generate coverage badge for README\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\toutput: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"o\",\n\t\t\tdescription: \"Output file path\",\n\t\t},\n\t\tformat: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Output format: svg (default), json, shields-json\",\n\t\t\tdefault: \"svg\",\n\t\t},\n\t\tstyle: {\n\t\t\ttype: \"string\",\n\t\t\tdescription: \"Badge style: flat (default), flat-square\",\n\t\t\tdefault: \"flat\",\n\t\t},\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output including stack traces\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tsetVerbose(ctx.values.verbose)\n\t\tconst cwd = process.cwd()\n\n\t\t// Validate format and style arguments\n\t\tconst formatInput = ctx.values.format ?? \"svg\"\n\t\tconst styleInput = ctx.values.style ?? \"flat\"\n\n\t\tif (!VALID_FORMATS.includes(formatInput as BadgeFormat)) {\n\t\t\tlogger.error(`Invalid format: \"${formatInput}\"`)\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(`Valid formats: ${VALID_FORMATS.join(\", \")}`)}`,\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tif (!VALID_STYLES.includes(styleInput as BadgeStyle)) {\n\t\t\tlogger.error(`Invalid style: \"${styleInput}\"`)\n\t\t\tlogger.log(` ${logger.dim(`Valid styles: ${VALID_STYLES.join(\", \")}`)}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst format = formatInput as BadgeFormat\n\t\tconst style = styleInput as BadgeStyle\n\n\t\t// Load configuration\n\t\tlet config: Awaited<ReturnType<typeof loadConfig>>\n\t\ttry {\n\t\t\tconfig = await loadConfig(ctx.values.config)\n\t\t} catch (error) {\n\t\t\tif (ctx.values.config) {\n\t\t\t\tlogger.errorWithStack(\n\t\t\t\t\terror,\n\t\t\t\t\t`Failed to load config from ${ctx.values.config}`,\n\t\t\t\t)\n\t\t\t} else {\n\t\t\t\tlogger.errorWithHelp(ERRORS.CONFIG_NOT_FOUND)\n\t\t\t}\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Check for routes configuration\n\t\tif (!config.routesPattern) {\n\t\t\tlogger.errorWithHelp(ERRORS.ROUTES_PATTERN_MISSING)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlogger.info(\"Calculating coverage...\")\n\n\t\t// Calculate coverage\n\t\tlet coverage: CoverageResult\n\t\ttry {\n\t\t\tcoverage = await calculateCoverage(\n\t\t\t\tconfig.routesPattern,\n\t\t\t\tconfig.metaPattern,\n\t\t\t\tconfig.ignore,\n\t\t\t\tcwd,\n\t\t\t)\n\t\t} catch (error) {\n\t\t\tlogger.errorWithStack(error, \"Failed to calculate coverage\")\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"Check that your routesPattern and metaPattern are valid glob patterns.\")}`,\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tif (coverage.total === 0) {\n\t\t\tlogger.warn(`No route files found matching: ${config.routesPattern}`)\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"Badge will show 0% coverage for empty projects.\")}`,\n\t\t\t)\n\t\t}\n\n\t\tlogger.log(\n\t\t\t`Coverage: ${coverage.covered}/${coverage.total} (${coverage.percentage}%)`,\n\t\t)\n\n\t\t// Generate badge content\n\t\tlet content: string\n\t\tlet extension: string\n\n\t\tswitch (format) {\n\t\t\tcase \"shields-json\":\n\t\t\t\tcontent = JSON.stringify(\n\t\t\t\t\tgenerateShieldsJson(coverage.percentage),\n\t\t\t\t\tnull,\n\t\t\t\t\t2,\n\t\t\t\t)\n\t\t\t\textension = \"json\"\n\t\t\t\tbreak\n\t\t\tcase \"json\":\n\t\t\t\tcontent = JSON.stringify(generateSimpleJson(coverage), null, 2)\n\t\t\t\textension = \"json\"\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tcontent = generateSvgBadge(coverage.percentage, style)\n\t\t\t\textension = \"svg\"\n\t\t\t\tbreak\n\t\t}\n\n\t\t// Determine output path\n\t\tconst defaultFileName =\n\t\t\tformat === \"shields-json\"\n\t\t\t\t? \"coverage.json\"\n\t\t\t\t: `coverage-badge.${extension}`\n\t\tconst outputPath =\n\t\t\tctx.values.output ?? join(cwd, config.outDir, defaultFileName)\n\n\t\t// Ensure output directory exists\n\t\tconst outputDir = dirname(outputPath)\n\t\ttry {\n\t\t\tif (!existsSync(outputDir)) {\n\t\t\t\tmkdirSync(outputDir, { recursive: true })\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogger.errorWithStack(\n\t\t\t\terror,\n\t\t\t\t`Failed to create output directory: ${outputDir}`,\n\t\t\t)\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"Check that you have write permissions to this location.\")}`,\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Write the badge\n\t\ttry {\n\t\t\twriteFileSync(outputPath, content)\n\t\t} catch (error) {\n\t\t\tlogger.errorWithStack(error, `Failed to write badge file: ${outputPath}`)\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"Check that you have write permissions and sufficient disk space.\")}`,\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlogger.blank()\n\t\tlogger.success(`Generated ${logger.path(outputPath)}`)\n\n\t\t// Show usage hint\n\t\tif (format === \"svg\") {\n\t\t\tlogger.blank()\n\t\t\tlogger.log(logger.dim(\"Usage in README.md:\"))\n\t\t\tlogger.log(\n\t\t\t\tlogger.dim(` ![Screenbook Coverage](${outputPath.replace(cwd, \".\")})`),\n\t\t\t)\n\t\t} else if (format === \"shields-json\") {\n\t\t\tlogger.blank()\n\t\t\tlogger.log(logger.dim(\"Usage with shields.io:\"))\n\t\t\tlogger.log(\n\t\t\t\tlogger.dim(\n\t\t\t\t\t\" ![Coverage](https://img.shields.io/endpoint?url=YOUR_URL/coverage.json)\",\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\t},\n})\n","import type { Screen } from \"@screenbook/core\"\n\n/**\n * Information about a detected cycle\n */\nexport interface CycleInfo {\n\t/**\n\t * Array of screen IDs forming the cycle, including the repeated start node\n\t * @example [\"A\", \"B\", \"C\", \"A\"]\n\t */\n\tcycle: string[]\n\t/**\n\t * True if any screen in the cycle has allowCycles: true\n\t */\n\tallowed: boolean\n}\n\n/**\n * Result of cycle detection analysis\n */\nexport interface CycleDetectionResult {\n\t/**\n\t * True if any cycles were detected\n\t */\n\thasCycles: boolean\n\t/**\n\t * All detected cycles\n\t */\n\tcycles: CycleInfo[]\n\t/**\n\t * Cycles that are not allowed (allowed: false)\n\t */\n\tdisallowedCycles: CycleInfo[]\n\t/**\n\t * Screen IDs that appear more than once (indicates data issue)\n\t */\n\tduplicateIds: string[]\n}\n\n// Node colors for DFS\nenum Color {\n\tWhite = 0, // Unvisited\n\tGray = 1, // In progress (on current path)\n\tBlack = 2, // Completed\n}\n\n/**\n * Detect circular navigation dependencies in screen definitions.\n * Uses DFS with coloring algorithm: O(V + E) complexity.\n *\n * @example\n * ```ts\n * const screens = [\n * { id: \"A\", next: [\"B\"] },\n * { id: \"B\", next: [\"C\"] },\n * { id: \"C\", next: [\"A\"] }, // Creates cycle A → B → C → A\n * ]\n * const result = detectCycles(screens)\n * // result.hasCycles === true\n * // result.cycles[0].cycle === [\"A\", \"B\", \"C\", \"A\"]\n * ```\n */\nexport function detectCycles(screens: Screen[]): CycleDetectionResult {\n\tconst screenMap = new Map<string, Screen>()\n\tconst duplicateIds: string[] = []\n\n\t// Build screen map and detect duplicates\n\tfor (const screen of screens) {\n\t\tif (!screen.id || typeof screen.id !== \"string\") {\n\t\t\t// Skip screens with invalid IDs\n\t\t\tcontinue\n\t\t}\n\t\tif (screenMap.has(screen.id)) {\n\t\t\tduplicateIds.push(screen.id)\n\t\t}\n\t\tscreenMap.set(screen.id, screen)\n\t}\n\n\tconst color = new Map<string, Color>()\n\tconst parent = new Map<string, string | null>()\n\tconst cycles: CycleInfo[] = []\n\n\t// Initialize all nodes as white\n\tfor (const id of screenMap.keys()) {\n\t\tcolor.set(id, Color.White)\n\t}\n\n\t// DFS from each unvisited node\n\tfor (const id of screenMap.keys()) {\n\t\tif (color.get(id) === Color.White) {\n\t\t\tdfs(id, null)\n\t\t}\n\t}\n\n\tfunction dfs(nodeId: string, parentId: string | null): void {\n\t\tcolor.set(nodeId, Color.Gray)\n\t\tparent.set(nodeId, parentId)\n\n\t\tconst node = screenMap.get(nodeId)\n\t\tconst neighbors = node?.next ?? []\n\n\t\tfor (const neighborId of neighbors) {\n\t\t\tconst neighborColor = color.get(neighborId)\n\n\t\t\tif (neighborColor === Color.Gray) {\n\t\t\t\t// Back edge found - cycle detected\n\t\t\t\tconst cyclePath = reconstructCycle(nodeId, neighborId)\n\t\t\t\tconst allowed = isCycleAllowed(cyclePath, screenMap)\n\t\t\t\tcycles.push({ cycle: cyclePath, allowed })\n\t\t\t} else if (neighborColor === Color.White) {\n\t\t\t\t// Only visit if the neighbor exists in our screen map\n\t\t\t\tif (screenMap.has(neighborId)) {\n\t\t\t\t\tdfs(neighborId, nodeId)\n\t\t\t\t}\n\t\t\t}\n\t\t\t// If Black, already fully processed - skip\n\t\t}\n\n\t\tcolor.set(nodeId, Color.Black)\n\t}\n\n\t/**\n\t * Reconstruct cycle path from back edge\n\t */\n\tfunction reconstructCycle(from: string, to: string): string[] {\n\t\tconst path: string[] = []\n\t\tlet current: string | null | undefined = from\n\t\tconst visited = new Set<string>()\n\t\tconst maxIterations = screenMap.size + 1\n\n\t\t// Trace back from 'from' to 'to' with loop guard\n\t\twhile (\n\t\t\tcurrent &&\n\t\t\tcurrent !== to &&\n\t\t\t!visited.has(current) &&\n\t\t\tpath.length < maxIterations\n\t\t) {\n\t\t\tvisited.add(current)\n\t\t\tpath.unshift(current)\n\t\t\tcurrent = parent.get(current)\n\t\t}\n\n\t\t// Add 'to' at the beginning (the cycle start)\n\t\tpath.unshift(to)\n\n\t\t// Add 'to' at the end to close the cycle\n\t\tpath.push(to)\n\n\t\treturn path\n\t}\n\n\tconst disallowedCycles = cycles.filter((c) => !c.allowed)\n\n\treturn {\n\t\thasCycles: cycles.length > 0,\n\t\tcycles,\n\t\tdisallowedCycles,\n\t\tduplicateIds,\n\t}\n}\n\n/**\n * Check if any screen in the cycle has allowCycles: true\n */\nfunction isCycleAllowed(\n\tcyclePath: string[],\n\tscreenMap: Map<string, Screen>,\n): boolean {\n\t// Exclude the last element as it's a duplicate of the first\n\tconst uniqueNodes = cyclePath.slice(0, -1)\n\n\tfor (const nodeId of uniqueNodes) {\n\t\tconst screen = screenMap.get(nodeId)\n\t\tif (screen?.allowCycles === true) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n/**\n * Format cycle information for console output\n *\n * @example\n * ```\n * Cycle 1: A → B → C → A\n * Cycle 2 (allowed): D → E → D\n * ```\n */\nexport function formatCycleWarnings(cycles: CycleInfo[]): string {\n\tif (cycles.length === 0) {\n\t\treturn \"\"\n\t}\n\n\tconst lines: string[] = []\n\n\tfor (let i = 0; i < cycles.length; i++) {\n\t\tconst cycle = cycles[i]\n\t\tif (!cycle) continue\n\n\t\tconst cycleStr = cycle.cycle.join(\" → \")\n\t\tconst allowedSuffix = cycle.allowed ? \" (allowed)\" : \"\"\n\t\tlines.push(` Cycle ${i + 1}${allowedSuffix}: ${cycleStr}`)\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n\n/**\n * Get a summary of cycle detection results\n */\nexport function getCycleSummary(result: CycleDetectionResult): string {\n\tif (!result.hasCycles) {\n\t\treturn \"No circular navigation detected\"\n\t}\n\n\tconst total = result.cycles.length\n\tconst disallowed = result.disallowedCycles.length\n\tconst allowed = total - disallowed\n\n\tif (disallowed === 0) {\n\t\treturn `${total} circular navigation${total > 1 ? \"s\" : \"\"} detected (all allowed)`\n\t}\n\n\tif (allowed === 0) {\n\t\treturn `${total} circular navigation${total > 1 ? \"s\" : \"\"} detected`\n\t}\n\n\treturn `${total} circular navigation${total > 1 ? \"s\" : \"\"} detected (${disallowed} not allowed, ${allowed} allowed)`\n}\n","/**\n * Fuzzy matching utilities for suggesting similar values\n */\n\nexport interface FindSimilarOptions {\n\t/** Maximum Levenshtein distance ratio (0-1). Default: 0.4 (40% of target length) */\n\tmaxDistanceRatio?: number\n\t/** Maximum number of suggestions to return. Default: 3 */\n\tmaxSuggestions?: number\n}\n\n/**\n * Find similar strings using Levenshtein distance\n */\nexport function findSimilar(\n\ttarget: string,\n\tcandidates: string[] | Set<string>,\n\toptions: FindSimilarOptions = {},\n): string[] {\n\tconst { maxDistanceRatio = 0.4, maxSuggestions = 3 } = options\n\n\t// Runtime validation for options\n\tconst effectiveRatio = Math.max(0, Math.min(1, maxDistanceRatio))\n\tconst effectiveSuggestions = Math.max(1, Math.floor(maxSuggestions))\n\n\tconst candidateArray = Array.isArray(candidates)\n\t\t? candidates\n\t\t: Array.from(candidates)\n\n\t// Only suggest if distance is reasonable\n\tconst maxDistance = Math.ceil(target.length * effectiveRatio)\n\n\tconst matches: Array<{ candidate: string; distance: number }> = []\n\n\tfor (const candidate of candidateArray) {\n\t\tconst distance = levenshteinDistance(target, candidate)\n\t\tif (distance <= maxDistance) {\n\t\t\tmatches.push({ candidate, distance })\n\t\t}\n\t}\n\n\t// Sort by distance (closest first) and return top suggestions\n\treturn matches\n\t\t.sort((a, b) => a.distance - b.distance)\n\t\t.slice(0, effectiveSuggestions)\n\t\t.map((m) => m.candidate)\n}\n\n/**\n * Find the single best match (convenience wrapper for findSimilar)\n */\nexport function findBestMatch(\n\ttarget: string,\n\tcandidates: string[] | Set<string>,\n\tmaxDistanceRatio = 0.4,\n): string | undefined {\n\tconst matches = findSimilar(target, candidates, {\n\t\tmaxDistanceRatio,\n\t\tmaxSuggestions: 1,\n\t})\n\treturn matches[0]\n}\n\n/**\n * Format suggestions for display in error messages\n */\nexport function formatSuggestions(suggestions: string[]): string {\n\tif (suggestions.length === 0) {\n\t\treturn \"\"\n\t}\n\n\tconst lines = [\"Did you mean one of these?\"]\n\tfor (const suggestion of suggestions) {\n\t\tlines.push(` - ${suggestion}`)\n\t}\n\treturn lines.join(\"\\n\")\n}\n\n/**\n * Calculate Levenshtein distance between two strings\n */\nexport function levenshteinDistance(a: string, b: string): number {\n\t// Pre-initialize matrix with proper dimensions\n\tconst matrix: number[][] = Array.from({ length: a.length + 1 }, () =>\n\t\tArray.from({ length: b.length + 1 }, () => 0),\n\t)\n\n\t// Helper to safely get/set matrix values (matrix is pre-initialized, so these are always valid)\n\tconst get = (i: number, j: number): number => matrix[i]?.[j] ?? 0\n\tconst set = (i: number, j: number, value: number): void => {\n\t\tconst row = matrix[i]\n\t\tif (row) row[j] = value\n\t}\n\n\t// Initialize first column\n\tfor (let i = 0; i <= a.length; i++) {\n\t\tset(i, 0, i)\n\t}\n\n\t// Initialize first row\n\tfor (let j = 0; j <= b.length; j++) {\n\t\tset(0, j, j)\n\t}\n\n\t// Fill the matrix\n\tfor (let i = 1; i <= a.length; i++) {\n\t\tfor (let j = 1; j <= b.length; j++) {\n\t\t\tconst cost = a[i - 1] === b[j - 1] ? 0 : 1\n\t\t\tset(\n\t\t\t\ti,\n\t\t\t\tj,\n\t\t\t\tMath.min(\n\t\t\t\t\tget(i - 1, j) + 1, // deletion\n\t\t\t\t\tget(i, j - 1) + 1, // insertion\n\t\t\t\t\tget(i - 1, j - 1) + cost, // substitution\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\t}\n\n\treturn get(a.length, b.length)\n}\n","import type { Screen } from \"@screenbook/core\"\nimport { findBestMatch } from \"./suggestions.js\"\n\nexport interface ValidationError {\n\tscreenId: string\n\tfield: \"next\" | \"entryPoints\"\n\tinvalidRef: string\n\tsuggestion?: string\n}\n\nexport interface ValidationResult {\n\tvalid: boolean\n\terrors: ValidationError[]\n}\n\n/**\n * Validate screen references (next and entryPoints)\n */\nexport function validateScreenReferences(screens: Screen[]): ValidationResult {\n\tconst screenIds = new Set(screens.map((s) => s.id))\n\tconst errors: ValidationError[] = []\n\n\tfor (const screen of screens) {\n\t\t// Validate next references\n\t\tif (screen.next) {\n\t\t\tfor (const nextId of screen.next) {\n\t\t\t\tif (!screenIds.has(nextId)) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\tscreenId: screen.id,\n\t\t\t\t\t\tfield: \"next\",\n\t\t\t\t\t\tinvalidRef: nextId,\n\t\t\t\t\t\tsuggestion: findBestMatch(nextId, screenIds),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Validate entryPoints references\n\t\tif (screen.entryPoints) {\n\t\t\tfor (const entryId of screen.entryPoints) {\n\t\t\t\tif (!screenIds.has(entryId)) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\tscreenId: screen.id,\n\t\t\t\t\t\tfield: \"entryPoints\",\n\t\t\t\t\t\tinvalidRef: entryId,\n\t\t\t\t\t\tsuggestion: findBestMatch(entryId, screenIds),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tvalid: errors.length === 0,\n\t\terrors,\n\t}\n}\n\n/**\n * Format validation errors for console output\n */\nexport function formatValidationErrors(errors: ValidationError[]): string {\n\tconst lines: string[] = []\n\n\tfor (const error of errors) {\n\t\tlines.push(` Screen \"${error.screenId}\"`)\n\t\tlines.push(\n\t\t\t` → ${error.field} references non-existent screen \"${error.invalidRef}\"`,\n\t\t)\n\t\tif (error.suggestion) {\n\t\t\tlines.push(` Did you mean \"${error.suggestion}\"?`)\n\t\t}\n\t\tlines.push(\"\")\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n","import { existsSync, mkdirSync, writeFileSync } from \"node:fs\"\nimport { dirname, join, resolve } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { createJiti } from \"jiti\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.js\"\nimport {\n\tdetectCycles,\n\tformatCycleWarnings,\n\tgetCycleSummary,\n} from \"../utils/cycleDetection.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger, setVerbose } from \"../utils/logger.js\"\nimport {\n\tformatValidationErrors,\n\tvalidateScreenReferences,\n} from \"../utils/validation.js\"\n\nexport interface CoverageData {\n\ttotal: number\n\tcovered: number\n\tpercentage: number\n\tmissing: Array<{\n\t\troute: string\n\t\tsuggestedPath: string\n\t}>\n\tbyOwner: Record<string, { count: number; screens: string[] }>\n\tbyTag: Record<string, number>\n\ttimestamp: string\n}\n\nexport const buildCommand = define({\n\tname: \"build\",\n\tdescription: \"Build screen metadata JSON from screen.meta.ts files\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\toutDir: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"o\",\n\t\t\tdescription: \"Output directory\",\n\t\t},\n\t\tstrict: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"s\",\n\t\t\tdescription: \"Fail on validation errors and disallowed cycles\",\n\t\t\tdefault: false,\n\t\t},\n\t\tallowCycles: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Suppress all circular navigation warnings\",\n\t\t\tdefault: false,\n\t\t},\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output including stack traces\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tsetVerbose(ctx.values.verbose)\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst outDir = ctx.values.outDir ?? config.outDir\n\t\tconst cwd = process.cwd()\n\n\t\tlogger.info(\"Building screen metadata...\")\n\n\t\t// Find all screen.meta.ts files\n\t\tconst files = await glob(config.metaPattern, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\n\t\tif (files.length === 0) {\n\t\t\tlogger.warn(\n\t\t\t\t`No screen.meta.ts files found matching: ${config.metaPattern}`,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\tlogger.info(`Found ${files.length} screen files`)\n\n\t\t// Create jiti instance for loading TypeScript files\n\t\tconst jiti = createJiti(cwd)\n\n\t\t// Extended screen type with file path (for internal use)\n\t\ttype ScreenWithFilePath = Screen & { filePath: string }\n\n\t\t// Load and collect screen metadata\n\t\tconst screens: ScreenWithFilePath[] = []\n\n\t\tfor (const file of files) {\n\t\t\tconst absolutePath = resolve(cwd, file)\n\n\t\t\ttry {\n\t\t\t\tconst module = (await jiti.import(absolutePath)) as { screen?: Screen }\n\t\t\t\tif (module.screen) {\n\t\t\t\t\tscreens.push({ ...module.screen, filePath: absolutePath })\n\t\t\t\t\tlogger.itemSuccess(module.screen.id)\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlogger.itemError(`Failed to load ${file}`)\n\t\t\t\tif (error instanceof Error) {\n\t\t\t\t\tlogger.log(` ${logger.dim(error.message)}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Validate screen references\n\t\tconst validation = validateScreenReferences(screens)\n\t\tif (!validation.valid) {\n\t\t\tlogger.blank()\n\t\t\tlogger.warn(\"Invalid screen references found:\")\n\t\t\tlogger.log(formatValidationErrors(validation.errors))\n\n\t\t\tif (ctx.values.strict) {\n\t\t\t\tlogger.errorWithHelp(ERRORS.VALIDATION_FAILED(validation.errors.length))\n\t\t\t\tprocess.exit(1)\n\t\t\t}\n\t\t}\n\n\t\t// Detect circular navigation\n\t\tif (!ctx.values.allowCycles) {\n\t\t\tconst cycleResult = detectCycles(screens)\n\t\t\tif (cycleResult.hasCycles) {\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.warn(getCycleSummary(cycleResult))\n\t\t\t\tlogger.log(formatCycleWarnings(cycleResult.cycles))\n\n\t\t\t\tif (ctx.values.strict && cycleResult.disallowedCycles.length > 0) {\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.errorWithHelp(\n\t\t\t\t\t\tERRORS.CYCLES_DETECTED(cycleResult.disallowedCycles.length),\n\t\t\t\t\t)\n\t\t\t\t\tprocess.exit(1)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Generate screens.json\n\t\tconst outputPath = join(cwd, outDir, \"screens.json\")\n\t\tconst outputDir = dirname(outputPath)\n\n\t\tif (!existsSync(outputDir)) {\n\t\t\tmkdirSync(outputDir, { recursive: true })\n\t\t}\n\n\t\twriteFileSync(outputPath, JSON.stringify(screens, null, 2))\n\t\tlogger.blank()\n\t\tlogger.success(`Generated ${logger.path(outputPath)}`)\n\n\t\t// Generate Mermaid graph\n\t\tconst mermaidPath = join(cwd, outDir, \"graph.mmd\")\n\t\tconst mermaidContent = generateMermaidGraph(screens)\n\t\twriteFileSync(mermaidPath, mermaidContent)\n\t\tlogger.success(`Generated ${logger.path(mermaidPath)}`)\n\n\t\t// Generate coverage.json\n\t\tconst coverage = await generateCoverageData(config, cwd, screens)\n\t\tconst coveragePath = join(cwd, outDir, \"coverage.json\")\n\t\twriteFileSync(coveragePath, JSON.stringify(coverage, null, 2))\n\t\tlogger.success(`Generated ${logger.path(coveragePath)}`)\n\t\tlogger.blank()\n\t\tlogger.done(\n\t\t\t`Coverage: ${coverage.covered}/${coverage.total} (${coverage.percentage}%)`,\n\t\t)\n\t},\n})\n\nasync function generateCoverageData(\n\tconfig: { routesPattern?: string; metaPattern: string; ignore: string[] },\n\tcwd: string,\n\tscreens: Screen[],\n): Promise<CoverageData> {\n\t// Get all route files if routesPattern is configured\n\tlet routeFiles: string[] = []\n\tif (config.routesPattern) {\n\t\trouteFiles = await glob(config.routesPattern, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\t}\n\n\t// Get directories that have screen.meta.ts\n\tconst _metaDirs = new Set(\n\t\tscreens.map((s) => {\n\t\t\t// Extract directory from route or id\n\t\t\tconst parts = s.id.split(\".\")\n\t\t\treturn parts.slice(0, -1).join(\"/\") || parts[0]\n\t\t}),\n\t)\n\n\t// Find missing routes (routes without screen.meta.ts)\n\tconst missing: CoverageData[\"missing\"] = []\n\tfor (const routeFile of routeFiles) {\n\t\tconst routeDir = dirname(routeFile)\n\t\tconst hasMetaFile = screens.some((s) => {\n\t\t\t// Check if any screen's route matches this route file's directory\n\t\t\tconst screenDir = s.id.replace(/\\./g, \"/\")\n\t\t\treturn (\n\t\t\t\trouteDir.includes(screenDir) ||\n\t\t\t\tscreenDir.includes(\n\t\t\t\t\trouteDir.replace(/^src\\/pages\\//, \"\").replace(/^app\\//, \"\"),\n\t\t\t\t)\n\t\t\t)\n\t\t})\n\n\t\tif (!hasMetaFile) {\n\t\t\tmissing.push({\n\t\t\t\troute: routeFile,\n\t\t\t\tsuggestedPath: join(dirname(routeFile), \"screen.meta.ts\"),\n\t\t\t})\n\t\t}\n\t}\n\n\t// Calculate coverage\n\tconst total = routeFiles.length > 0 ? routeFiles.length : screens.length\n\tconst covered = screens.length\n\tconst percentage = total > 0 ? Math.round((covered / total) * 100) : 100\n\n\t// Group by owner\n\tconst byOwner: CoverageData[\"byOwner\"] = {}\n\tfor (const screen of screens) {\n\t\tconst owners = screen.owner || [\"unassigned\"]\n\t\tfor (const owner of owners) {\n\t\t\tif (!byOwner[owner]) {\n\t\t\t\tbyOwner[owner] = { count: 0, screens: [] }\n\t\t\t}\n\t\t\tbyOwner[owner].count++\n\t\t\tbyOwner[owner].screens.push(screen.id)\n\t\t}\n\t}\n\n\t// Group by tag\n\tconst byTag: CoverageData[\"byTag\"] = {}\n\tfor (const screen of screens) {\n\t\tconst tags = screen.tags || []\n\t\tfor (const tag of tags) {\n\t\t\tbyTag[tag] = (byTag[tag] || 0) + 1\n\t\t}\n\t}\n\n\treturn {\n\t\ttotal,\n\t\tcovered,\n\t\tpercentage,\n\t\tmissing,\n\t\tbyOwner,\n\t\tbyTag,\n\t\ttimestamp: new Date().toISOString(),\n\t}\n}\n\nfunction generateMermaidGraph(screens: Screen[]): string {\n\tconst lines: string[] = [\"flowchart TD\"]\n\n\t// Create nodes\n\tfor (const screen of screens) {\n\t\tconst label = screen.title.replace(/\"/g, \"'\")\n\t\tlines.push(` ${sanitizeId(screen.id)}[\"${label}\"]`)\n\t}\n\n\tlines.push(\"\")\n\n\t// Create edges from next relationships\n\tfor (const screen of screens) {\n\t\tif (screen.next) {\n\t\t\tfor (const nextId of screen.next) {\n\t\t\t\tlines.push(` ${sanitizeId(screen.id)} --> ${sanitizeId(nextId)}`)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n\nfunction sanitizeId(id: string): string {\n\treturn id.replace(/\\./g, \"_\")\n}\n","import { spawn } from \"node:child_process\"\nimport { copyFileSync, existsSync, mkdirSync, writeFileSync } from \"node:fs\"\nimport { createRequire } from \"node:module\"\nimport { dirname, join, resolve } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { createJiti } from \"jiti\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger, setVerbose } from \"../utils/logger.js\"\n\nexport const devCommand = define({\n\tname: \"dev\",\n\tdescription: \"Start the Screenbook development server\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tport: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"p\",\n\t\t\tdescription: \"Port to run the server on\",\n\t\t\tdefault: \"4321\",\n\t\t},\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output including stack traces\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tsetVerbose(ctx.values.verbose)\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst port = ctx.values.port ?? \"4321\"\n\t\tconst cwd = process.cwd()\n\n\t\tlogger.info(\"Starting Screenbook development server...\")\n\n\t\t// First, build the screen metadata\n\t\tawait buildScreens(config, cwd)\n\n\t\t// Find the UI package location\n\t\tconst uiPackagePath = resolveUiPackage()\n\n\t\tif (!uiPackagePath) {\n\t\t\tlogger.errorWithHelp({\n\t\t\t\ttitle: \"Could not find @screenbook/ui package\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Make sure @screenbook/ui is installed. Run 'npm install @screenbook/ui' or 'pnpm add @screenbook/ui'.\",\n\t\t\t})\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Copy screens.json and coverage.json to UI package\n\t\tconst screensJsonPath = join(cwd, config.outDir, \"screens.json\")\n\t\tconst coverageJsonPath = join(cwd, config.outDir, \"coverage.json\")\n\t\tconst uiScreensDir = join(uiPackagePath, \".screenbook\")\n\n\t\tif (!existsSync(uiScreensDir)) {\n\t\t\tmkdirSync(uiScreensDir, { recursive: true })\n\t\t}\n\n\t\tif (existsSync(screensJsonPath)) {\n\t\t\tcopyFileSync(screensJsonPath, join(uiScreensDir, \"screens.json\"))\n\t\t}\n\n\t\tif (existsSync(coverageJsonPath)) {\n\t\t\tcopyFileSync(coverageJsonPath, join(uiScreensDir, \"coverage.json\"))\n\t\t}\n\n\t\t// Start Astro dev server\n\t\tlogger.blank()\n\t\tlogger.info(\n\t\t\t`Starting UI server on ${logger.highlight(`http://localhost:${port}`)}`,\n\t\t)\n\n\t\tconst astroProcess = spawn(\"npx\", [\"astro\", \"dev\", \"--port\", port], {\n\t\t\tcwd: uiPackagePath,\n\t\t\tstdio: \"inherit\",\n\t\t\tshell: true,\n\t\t})\n\n\t\tastroProcess.on(\"error\", (error) => {\n\t\t\tlogger.errorWithHelp(ERRORS.SERVER_START_FAILED(error.message))\n\t\t\tprocess.exit(1)\n\t\t})\n\n\t\tastroProcess.on(\"close\", (code) => {\n\t\t\tprocess.exit(code ?? 0)\n\t\t})\n\n\t\t// Handle graceful shutdown\n\t\tprocess.on(\"SIGINT\", () => {\n\t\t\tastroProcess.kill(\"SIGINT\")\n\t\t})\n\n\t\tprocess.on(\"SIGTERM\", () => {\n\t\t\tastroProcess.kill(\"SIGTERM\")\n\t\t})\n\t},\n})\n\nexport async function buildScreens(\n\tconfig: { metaPattern: string; outDir: string; ignore: string[] },\n\tcwd: string,\n): Promise<void> {\n\tconst files = await glob(config.metaPattern, {\n\t\tcwd,\n\t\tignore: config.ignore,\n\t})\n\n\tif (files.length === 0) {\n\t\tlogger.warn(`No screen.meta.ts files found matching: ${config.metaPattern}`)\n\t\treturn\n\t}\n\n\tlogger.info(`Found ${files.length} screen files`)\n\n\t// Extended screen type with file path (for internal use)\n\ttype ScreenWithFilePath = Screen & { filePath: string }\n\n\tconst jiti = createJiti(cwd)\n\tconst screens: ScreenWithFilePath[] = []\n\n\tfor (const file of files) {\n\t\tconst absolutePath = resolve(cwd, file)\n\n\t\ttry {\n\t\t\tconst module = (await jiti.import(absolutePath)) as { screen?: Screen }\n\t\t\tif (module.screen) {\n\t\t\t\tscreens.push({ ...module.screen, filePath: absolutePath })\n\t\t\t\tlogger.itemSuccess(module.screen.id)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogger.itemError(`Failed to load ${file}`)\n\t\t\tif (error instanceof Error) {\n\t\t\t\tlogger.log(` ${logger.dim(error.message)}`)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst outputPath = join(cwd, config.outDir, \"screens.json\")\n\tconst outputDir = dirname(outputPath)\n\n\tif (!existsSync(outputDir)) {\n\t\tmkdirSync(outputDir, { recursive: true })\n\t}\n\n\twriteFileSync(outputPath, JSON.stringify(screens, null, 2))\n\tlogger.blank()\n\tlogger.success(`Generated ${logger.path(outputPath)}`)\n}\n\nexport function resolveUiPackage(): string | null {\n\t// Try to resolve @screenbook/ui from node_modules\n\ttry {\n\t\tconst require = createRequire(import.meta.url)\n\t\tconst uiPackageJson = require.resolve(\"@screenbook/ui/package.json\")\n\t\treturn dirname(uiPackageJson)\n\t} catch {\n\t\t// Fallback: look in common locations\n\t\tconst possiblePaths = [\n\t\t\tjoin(process.cwd(), \"node_modules\", \"@screenbook\", \"ui\"),\n\t\t\tjoin(process.cwd(), \"..\", \"ui\"),\n\t\t\tjoin(process.cwd(), \"packages\", \"ui\"),\n\t\t]\n\n\t\tfor (const p of possiblePaths) {\n\t\t\tif (existsSync(join(p, \"package.json\"))) {\n\t\t\t\treturn p\n\t\t\t}\n\t\t}\n\n\t\treturn null\n\t}\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { join, resolve } from \"node:path\"\nimport { define } from \"gunshi\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { logger } from \"../utils/logger.js\"\n\nconst CONFIG_FILES = [\n\t\"screenbook.config.ts\",\n\t\"screenbook.config.js\",\n\t\"screenbook.config.mjs\",\n]\n\nexport interface CheckResult {\n\tname: string\n\tstatus: \"pass\" | \"fail\" | \"warn\"\n\tmessage: string\n\tsuggestion?: string\n}\n\ninterface PackageJson {\n\tdependencies?: Record<string, string>\n\tdevDependencies?: Record<string, string>\n}\n\nexport const doctorCommand = define({\n\tname: \"doctor\",\n\tdescription: \"Diagnose common issues with Screenbook setup\",\n\targs: {\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst cwd = process.cwd()\n\t\tconst verbose = ctx.values.verbose\n\n\t\tlogger.log(\"\")\n\t\tlogger.log(logger.bold(\"Screenbook Doctor\"))\n\t\tlogger.log(\"─────────────────\")\n\t\tlogger.log(\"\")\n\n\t\tconst results: CheckResult[] = []\n\n\t\t// Run all checks\n\t\tresults.push(await checkConfigFile(cwd))\n\t\tresults.push(await checkDependencies(cwd))\n\n\t\t// Load config for remaining checks\n\t\tconst config = await loadConfig()\n\n\t\tresults.push(await checkMetaPattern(cwd, config.metaPattern, config.ignore))\n\t\tresults.push(\n\t\t\tawait checkRoutesPattern(cwd, config.routesPattern, config.ignore),\n\t\t)\n\t\tresults.push(await checkBuildOutput(cwd, config.outDir))\n\t\tresults.push(await checkVersionCompatibility(cwd))\n\t\tresults.push(await checkGitRepository(cwd))\n\n\t\t// Display results\n\t\tdisplayResults(results, verbose)\n\t},\n})\n\n// Exported for testing\nexport async function checkConfigFile(cwd: string): Promise<CheckResult> {\n\tfor (const configFile of CONFIG_FILES) {\n\t\tconst absolutePath = resolve(cwd, configFile)\n\t\tif (existsSync(absolutePath)) {\n\t\t\treturn {\n\t\t\t\tname: \"Config file\",\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tmessage: `Found: ${configFile}`,\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tname: \"Config file\",\n\t\tstatus: \"warn\",\n\t\tmessage: \"No config file found (using defaults)\",\n\t\tsuggestion: \"Run 'screenbook init' to create a config file\",\n\t}\n}\n\nexport async function checkDependencies(cwd: string): Promise<CheckResult> {\n\tconst packageJsonPath = join(cwd, \"package.json\")\n\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn {\n\t\t\tname: \"Dependencies\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: \"package.json not found\",\n\t\t\tsuggestion: \"Run 'npm init' or 'pnpm init' to create package.json\",\n\t\t}\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(packageJsonPath, \"utf-8\")\n\t\tconst pkg = JSON.parse(content) as PackageJson\n\n\t\tconst allDeps = { ...pkg.dependencies, ...pkg.devDependencies }\n\t\tconst unifiedVersion = allDeps.screenbook\n\t\tconst coreVersion = allDeps[\"@screenbook/core\"]\n\t\tconst cliVersion = allDeps[\"@screenbook/cli\"]\n\n\t\t// Check for unified screenbook package first\n\t\tif (unifiedVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Dependencies\",\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tmessage: `screenbook@${unifiedVersion}`,\n\t\t\t}\n\t\t}\n\n\t\tif (!coreVersion && !cliVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Dependencies\",\n\t\t\t\tstatus: \"fail\",\n\t\t\t\tmessage: \"Screenbook packages not installed\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Run 'pnpm add -D screenbook' or 'pnpm add -D @screenbook/core @screenbook/cli' to install\",\n\t\t\t}\n\t\t}\n\n\t\tif (!coreVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Dependencies\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: \"@screenbook/core not found in dependencies\",\n\t\t\t\tsuggestion: \"Run 'pnpm add -D @screenbook/core' to install\",\n\t\t\t}\n\t\t}\n\n\t\tif (!cliVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Dependencies\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: \"@screenbook/cli not found in dependencies\",\n\t\t\t\tsuggestion: \"Run 'pnpm add -D @screenbook/cli' to install\",\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: \"Dependencies\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `@screenbook/core@${coreVersion}, @screenbook/cli@${cliVersion}`,\n\t\t}\n\t} catch (error) {\n\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\t\treturn {\n\t\t\tname: \"Dependencies\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `Failed to read package.json: ${errorMessage}`,\n\t\t\tsuggestion: \"Ensure package.json is valid JSON\",\n\t\t}\n\t}\n}\n\nexport async function checkMetaPattern(\n\tcwd: string,\n\tmetaPattern: string,\n\tignore: string[],\n): Promise<CheckResult> {\n\ttry {\n\t\tconst files = await glob(metaPattern, { cwd, ignore })\n\n\t\tif (files.length === 0) {\n\t\t\treturn {\n\t\t\t\tname: \"Screen meta files\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: `No files matching: ${metaPattern}`,\n\t\t\t\tsuggestion: \"Run 'screenbook generate' to create screen.meta.ts files\",\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: \"Screen meta files\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `Found ${files.length} screen.meta.ts file${files.length > 1 ? \"s\" : \"\"}`,\n\t\t}\n\t} catch (error) {\n\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\t\treturn {\n\t\t\tname: \"Screen meta files\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `Invalid pattern: ${metaPattern} (${errorMessage})`,\n\t\t\tsuggestion: \"Check metaPattern in your config file\",\n\t\t}\n\t}\n}\n\nexport async function checkRoutesPattern(\n\tcwd: string,\n\troutesPattern: string | undefined,\n\tignore: string[],\n): Promise<CheckResult> {\n\tif (!routesPattern) {\n\t\treturn {\n\t\t\tname: \"Routes pattern\",\n\t\t\tstatus: \"warn\",\n\t\t\tmessage: \"routesPattern not configured\",\n\t\t\tsuggestion:\n\t\t\t\t\"Set routesPattern in config to enable 'lint' and 'generate' commands\",\n\t\t}\n\t}\n\n\ttry {\n\t\tconst files = await glob(routesPattern, { cwd, ignore })\n\n\t\tif (files.length === 0) {\n\t\t\treturn {\n\t\t\t\tname: \"Routes pattern\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: `No files matching: ${routesPattern}`,\n\t\t\t\tsuggestion: \"Check routesPattern in your config file\",\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: \"Routes pattern\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `Found ${files.length} route file${files.length > 1 ? \"s\" : \"\"}`,\n\t\t}\n\t} catch (error) {\n\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\t\treturn {\n\t\t\tname: \"Routes pattern\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `Invalid pattern: ${routesPattern} (${errorMessage})`,\n\t\t\tsuggestion: \"Check routesPattern in your config file\",\n\t\t}\n\t}\n}\n\nexport async function checkBuildOutput(\n\tcwd: string,\n\toutDir: string,\n): Promise<CheckResult> {\n\tconst screensJsonPath = join(cwd, outDir, \"screens.json\")\n\n\tif (!existsSync(screensJsonPath)) {\n\t\treturn {\n\t\t\tname: \"Build output\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `screens.json not found in ${outDir}/`,\n\t\t\tsuggestion: \"Run 'screenbook build' to generate metadata\",\n\t\t}\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(screensJsonPath, \"utf-8\")\n\t\tconst screens = JSON.parse(content) as unknown[]\n\n\t\treturn {\n\t\t\tname: \"Build output\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `screens.json contains ${screens.length} screen${screens.length > 1 ? \"s\" : \"\"}`,\n\t\t}\n\t} catch (error) {\n\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\t\treturn {\n\t\t\tname: \"Build output\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `screens.json is corrupted: ${errorMessage}`,\n\t\t\tsuggestion: \"Run 'screenbook build' to regenerate\",\n\t\t}\n\t}\n}\n\nexport async function checkVersionCompatibility(\n\tcwd: string,\n): Promise<CheckResult> {\n\tconst packageJsonPath = join(cwd, \"package.json\")\n\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn {\n\t\t\tname: \"Version compatibility\",\n\t\t\tstatus: \"warn\",\n\t\t\tmessage: \"Cannot check - package.json not found\",\n\t\t}\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(packageJsonPath, \"utf-8\")\n\t\tconst pkg = JSON.parse(content) as PackageJson\n\n\t\tconst allDeps = { ...pkg.dependencies, ...pkg.devDependencies }\n\t\tconst unifiedVersion = allDeps.screenbook\n\t\tconst coreVersion = allDeps[\"@screenbook/core\"]\n\t\tconst cliVersion = allDeps[\"@screenbook/cli\"]\n\n\t\t// Unified package - no compatibility check needed\n\t\tif (unifiedVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Version compatibility\",\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tmessage: \"Using unified screenbook package\",\n\t\t\t}\n\t\t}\n\n\t\tif (!coreVersion || !cliVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Version compatibility\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: \"Cannot check - packages not installed\",\n\t\t\t}\n\t\t}\n\n\t\t// Extract major version (handle ^, ~, etc.)\n\t\tconst extractMajor = (version: string): string => {\n\t\t\tconst cleaned = version.replace(/^[\\^~>=<]+/, \"\")\n\t\t\treturn cleaned.split(\".\")[0] ?? \"0\"\n\t\t}\n\n\t\tconst coreMajor = extractMajor(coreVersion)\n\t\tconst cliMajor = extractMajor(cliVersion)\n\n\t\tif (coreMajor !== cliMajor) {\n\t\t\treturn {\n\t\t\t\tname: \"Version compatibility\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: `Major version mismatch: core@${coreVersion} vs cli@${cliVersion}`,\n\t\t\t\tsuggestion: \"Update packages to matching major versions\",\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: \"Version compatibility\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: \"Package versions are compatible\",\n\t\t}\n\t} catch (error) {\n\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\t\treturn {\n\t\t\tname: \"Version compatibility\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `Failed to read package.json: ${errorMessage}`,\n\t\t\tsuggestion: \"Ensure package.json is valid JSON\",\n\t\t}\n\t}\n}\n\nexport async function checkGitRepository(cwd: string): Promise<CheckResult> {\n\tconst gitDir = join(cwd, \".git\")\n\n\tif (!existsSync(gitDir)) {\n\t\treturn {\n\t\t\tname: \"Git repository\",\n\t\t\tstatus: \"warn\",\n\t\t\tmessage: \"Not a git repository\",\n\t\t\tsuggestion:\n\t\t\t\t\"Run 'git init' to enable 'pr-impact' command for PR analysis\",\n\t\t}\n\t}\n\n\treturn {\n\t\tname: \"Git repository\",\n\t\tstatus: \"pass\",\n\t\tmessage: \"Git repository detected\",\n\t}\n}\n\nfunction displayResults(results: CheckResult[], verbose: boolean): void {\n\tlet passCount = 0\n\tlet failCount = 0\n\tlet warnCount = 0\n\n\tfor (const result of results) {\n\t\tconst icon =\n\t\t\tresult.status === \"pass\"\n\t\t\t\t? logger.green(\"✓\")\n\t\t\t\t: result.status === \"fail\"\n\t\t\t\t\t? logger.red(\"✗\")\n\t\t\t\t\t: logger.yellow(\"⚠\")\n\n\t\tconst statusColor =\n\t\t\tresult.status === \"pass\"\n\t\t\t\t? logger.green\n\t\t\t\t: result.status === \"fail\"\n\t\t\t\t\t? logger.red\n\t\t\t\t\t: logger.yellow\n\n\t\tlogger.log(`${icon} ${statusColor(result.name)}: ${result.message}`)\n\n\t\tif (result.suggestion && (result.status !== \"pass\" || verbose)) {\n\t\t\tlogger.log(` ${logger.dim(\"→\")} ${result.suggestion}`)\n\t\t}\n\n\t\tif (result.status === \"pass\") passCount++\n\t\telse if (result.status === \"fail\") failCount++\n\t\telse warnCount++\n\t}\n\n\tlogger.log(\"\")\n\n\tconst summary: string[] = []\n\tif (passCount > 0) summary.push(logger.green(`${passCount} passed`))\n\tif (failCount > 0) summary.push(logger.red(`${failCount} failed`))\n\tif (warnCount > 0) summary.push(logger.yellow(`${warnCount} warnings`))\n\n\tlogger.log(`Summary: ${summary.join(\", \")}`)\n\n\tif (failCount > 0) {\n\t\tlogger.log(\"\")\n\t\tlogger.log(\n\t\t\tlogger.dim(\"Run the suggested commands above to fix the issues.\"),\n\t\t)\n\t}\n}\n","import { resolve } from \"node:path\"\n\n/**\n * Supported router types for auto-detection.\n * Detection order: TanStack Router -> Solid Router -> Angular Router -> React Router -> Vue Router.\n */\nexport type RouterType =\n\t| \"react-router\"\n\t| \"vue-router\"\n\t| \"tanstack-router\"\n\t| \"solid-router\"\n\t| \"angular-router\"\n\t| \"unknown\"\n\n/**\n * Parsed route from router config (Vue Router, React Router, etc.)\n */\nexport interface ParsedRoute {\n\tpath: string\n\tname?: string\n\tcomponent?: string\n\tchildren?: ParsedRoute[]\n\tredirect?: string\n}\n\n/**\n * Flattened route with computed properties\n */\nexport interface FlatRoute {\n\t/** Full path including parent paths */\n\tfullPath: string\n\t/** Route name if defined */\n\tname?: string\n\t/** Component file path if available */\n\tcomponentPath?: string\n\t/** Computed screen ID from path */\n\tscreenId: string\n\t/** Computed screen title from path */\n\tscreenTitle: string\n\t/** Nesting depth */\n\tdepth: number\n}\n\n/**\n * Result of parsing a router config file\n */\nexport interface ParseResult {\n\troutes: ParsedRoute[]\n\twarnings: string[]\n}\n\n/**\n * Resolve relative import path to absolute path\n */\nexport function resolveImportPath(importPath: string, baseDir: string): string {\n\tif (importPath.startsWith(\".\")) {\n\t\treturn resolve(baseDir, importPath)\n\t}\n\treturn importPath\n}\n\n/**\n * Flatten nested routes into a flat list with computed properties\n */\nexport function flattenRoutes(\n\troutes: ParsedRoute[],\n\tparentPath = \"\",\n\tdepth = 0,\n): FlatRoute[] {\n\tconst result: FlatRoute[] = []\n\n\tfor (const route of routes) {\n\t\t// Skip redirect-only routes\n\t\tif (route.redirect && !route.component) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Compute full path\n\t\tlet fullPath: string\n\t\tif (route.path.startsWith(\"/\")) {\n\t\t\tfullPath = route.path\n\t\t} else if (parentPath === \"/\") {\n\t\t\tfullPath = `/${route.path}`\n\t\t} else {\n\t\t\tfullPath = parentPath ? `${parentPath}/${route.path}` : `/${route.path}`\n\t\t}\n\n\t\t// Normalize path\n\t\tfullPath = fullPath.replace(/\\/+/g, \"/\")\n\t\tif (fullPath !== \"/\" && fullPath.endsWith(\"/\")) {\n\t\t\tfullPath = fullPath.slice(0, -1)\n\t\t}\n\n\t\t// Handle empty path (index routes)\n\t\tif (fullPath === \"\") {\n\t\t\tfullPath = parentPath || \"/\"\n\t\t}\n\n\t\t// Only add routes with components (skip abstract parent routes)\n\t\tif (route.component || !route.children) {\n\t\t\tresult.push({\n\t\t\t\tfullPath,\n\t\t\t\tname: route.name,\n\t\t\t\tcomponentPath: route.component,\n\t\t\t\tscreenId: pathToScreenId(fullPath),\n\t\t\t\tscreenTitle: pathToScreenTitle(fullPath),\n\t\t\t\tdepth,\n\t\t\t})\n\t\t}\n\n\t\t// Process children\n\t\tif (route.children) {\n\t\t\tresult.push(...flattenRoutes(route.children, fullPath, depth + 1))\n\t\t}\n\t}\n\n\treturn result\n}\n\n/**\n * Convert route path to screen ID\n * /user/:id/profile -> user.id.profile\n */\nexport function pathToScreenId(path: string): string {\n\tif (path === \"/\" || path === \"\") {\n\t\treturn \"home\"\n\t}\n\n\treturn path\n\t\t.replace(/^\\//, \"\") // Remove leading slash\n\t\t.replace(/\\/$/, \"\") // Remove trailing slash\n\t\t.split(\"/\")\n\t\t.map((segment) => {\n\t\t\t// Convert :param to param\n\t\t\tif (segment.startsWith(\":\")) {\n\t\t\t\treturn segment.slice(1)\n\t\t\t}\n\t\t\t// Convert *catchall or ** to catchall\n\t\t\tif (segment.startsWith(\"*\")) {\n\t\t\t\t// Handle ** (Angular) as catchall\n\t\t\t\tif (segment === \"**\") {\n\t\t\t\t\treturn \"catchall\"\n\t\t\t\t}\n\t\t\t\treturn segment.slice(1) || \"catchall\"\n\t\t\t}\n\t\t\treturn segment\n\t\t})\n\t\t.join(\".\")\n}\n\n/**\n * Convert route path to screen title\n * /user/:id/profile -> Profile\n */\nexport function pathToScreenTitle(path: string): string {\n\tif (path === \"/\" || path === \"\") {\n\t\treturn \"Home\"\n\t}\n\n\tconst segments = path\n\t\t.replace(/^\\//, \"\")\n\t\t.replace(/\\/$/, \"\")\n\t\t.split(\"/\")\n\t\t.filter((s) => !s.startsWith(\":\") && !s.startsWith(\"*\"))\n\n\tconst lastSegment = segments[segments.length - 1] || \"Home\"\n\n\t// Convert kebab-case or snake_case to Title Case\n\treturn lastSegment\n\t\t.split(/[-_]/)\n\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n\t\t.join(\" \")\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { parse } from \"@babel/parser\"\nimport {\n\ttype ParsedRoute,\n\ttype ParseResult,\n\tresolveImportPath,\n} from \"./routeParserUtils.js\"\n\n// Re-export shared types\nexport type { ParsedRoute, ParseResult }\n\n/**\n * Parse Angular Router configuration file and extract routes.\n * Supports both Standalone (Angular 14+) and NgModule patterns.\n *\n * Supported patterns:\n * - `export const routes: Routes = [...]`\n * - `const routes: Routes = [...]`\n * - `RouterModule.forRoot([...])`\n * - `RouterModule.forChild([...])`\n * - `export default [...]`\n * - `export default [...] satisfies Routes`\n *\n * @param filePath - Path to the router configuration file\n * @param preloadedContent - Optional pre-read file content. When provided, the file is not read from disk,\n * enabling testing with virtual content or avoiding duplicate file reads.\n * @returns ParseResult containing extracted routes and any warnings\n * @throws Error if the file cannot be read or contains syntax errors\n */\nexport function parseAngularRouterConfig(\n\tfilePath: string,\n\tpreloadedContent?: string,\n): ParseResult {\n\tconst absolutePath = resolve(filePath)\n\tconst routesFileDir = dirname(absolutePath)\n\tconst warnings: string[] = []\n\n\t// Read file with proper error handling (skip if content is preloaded)\n\tlet content: string\n\tif (preloadedContent !== undefined) {\n\t\tcontent = preloadedContent\n\t} else {\n\t\ttry {\n\t\t\tcontent = readFileSync(absolutePath, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to read routes file \"${absolutePath}\": ${message}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t// Parse with Babel - wrap for better error messages\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", [\"decorators\", { decoratorsBeforeExport: true }]],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Syntax error in routes file \"${absolutePath}\": ${error.message}`,\n\t\t\t)\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tthrow new Error(`Failed to parse routes file \"${absolutePath}\": ${message}`)\n\t}\n\n\tconst routes: ParsedRoute[] = []\n\n\t// Find routes array in the AST\n\tfor (const node of ast.program.body) {\n\t\t// Handle: const routes: Routes = [...] or export const routes: Routes = [...]\n\t\tif (node.type === \"VariableDeclaration\") {\n\t\t\tfor (const decl of node.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\t// Check if it looks like a routes array (name contains \"route\" or has Routes type)\n\t\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\t\t\t\t\tconst typeAnnotation = (decl.id as any).typeAnnotation?.typeAnnotation\n\t\t\t\t\tconst isRoutesVariable =\n\t\t\t\t\t\tdecl.id.name.toLowerCase().includes(\"route\") ||\n\t\t\t\t\t\t(typeAnnotation?.type === \"TSTypeReference\" &&\n\t\t\t\t\t\t\ttypeAnnotation.typeName?.type === \"Identifier\" &&\n\t\t\t\t\t\t\ttypeAnnotation.typeName.name === \"Routes\")\n\t\t\t\t\tif (isRoutesVariable) {\n\t\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export const routes: Routes = [...]\n\t\tif (\n\t\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t\t) {\n\t\t\tfor (const decl of node.declaration.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\t\t\t\t\tconst typeAnnotation = (decl.id as any).typeAnnotation?.typeAnnotation\n\t\t\t\t\tconst isRoutesVariable =\n\t\t\t\t\t\tdecl.id.name.toLowerCase().includes(\"route\") ||\n\t\t\t\t\t\t(typeAnnotation?.type === \"TSTypeReference\" &&\n\t\t\t\t\t\t\ttypeAnnotation.typeName?.type === \"Identifier\" &&\n\t\t\t\t\t\t\ttypeAnnotation.typeName.name === \"Routes\")\n\t\t\t\t\tif (isRoutesVariable) {\n\t\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export default [...] (less common but possible)\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(node.declaration, routesFileDir, warnings)\n\t\t\troutes.push(...parsed)\n\t\t}\n\n\t\t// Handle: export default [...] satisfies Routes\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"TSSatisfiesExpression\" &&\n\t\t\tnode.declaration.expression.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(\n\t\t\t\tnode.declaration.expression,\n\t\t\t\troutesFileDir,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t\troutes.push(...parsed)\n\t\t}\n\n\t\t// Handle NgModule pattern: RouterModule.forRoot([...]) or RouterModule.forChild([...])\n\t\t// This can appear in @NgModule decorator or as a call expression\n\t\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\t\tlet classNode: any = null\n\t\tif (node.type === \"ClassDeclaration\") {\n\t\t\tclassNode = node\n\t\t} else if (\n\t\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: AST node types require type assertions\n\t\t\t(node as any).declaration?.type === \"ClassDeclaration\"\n\t\t) {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: AST node types require type assertions\n\t\t\tclassNode = (node as any).declaration\n\t\t}\n\n\t\tif (classNode) {\n\t\t\tconst decorators = classNode.decorators || []\n\t\t\tfor (const decorator of decorators) {\n\t\t\t\tif (decorator.expression?.type === \"CallExpression\") {\n\t\t\t\t\tconst routesFromDecorator = extractRoutesFromNgModule(\n\t\t\t\t\t\tdecorator.expression,\n\t\t\t\t\t\troutesFileDir,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t)\n\t\t\t\t\troutes.push(...routesFromDecorator)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle standalone RouterModule.forRoot/forChild calls in exports\n\t\tif (node.type === \"ExpressionStatement\") {\n\t\t\tconst routesFromExpr = extractRoutesFromExpression(\n\t\t\t\tnode.expression,\n\t\t\t\troutesFileDir,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t\troutes.push(...routesFromExpr)\n\t\t}\n\t}\n\n\t// Warn if no routes were found\n\tif (routes.length === 0) {\n\t\twarnings.push(\n\t\t\t\"No routes found. Supported patterns: 'export const routes: Routes = [...]', 'RouterModule.forRoot([...])', or 'RouterModule.forChild([...])'\",\n\t\t)\n\t}\n\n\treturn { routes, warnings }\n}\n\n/**\n * Extract routes from @NgModule decorator\n */\nfunction extractRoutesFromNgModule(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tcallExpr: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\t// Check if it's @NgModule({...})\n\tif (\n\t\tcallExpr.callee?.type === \"Identifier\" &&\n\t\tcallExpr.callee.name === \"NgModule\"\n\t) {\n\t\tconst arg = callExpr.arguments[0]\n\t\tif (arg?.type === \"ObjectExpression\") {\n\t\t\tfor (const prop of arg.properties) {\n\t\t\t\tif (\n\t\t\t\t\tprop.type === \"ObjectProperty\" &&\n\t\t\t\t\tprop.key?.type === \"Identifier\" &&\n\t\t\t\t\tprop.key.name === \"imports\"\n\t\t\t\t) {\n\t\t\t\t\tif (prop.value?.type === \"ArrayExpression\") {\n\t\t\t\t\t\tfor (const element of prop.value.elements) {\n\t\t\t\t\t\t\tif (!element) continue\n\t\t\t\t\t\t\tconst extracted = extractRoutesFromExpression(\n\t\t\t\t\t\t\t\telement,\n\t\t\t\t\t\t\t\tbaseDir,\n\t\t\t\t\t\t\t\twarnings,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\troutes.push(...extracted)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Extract routes from RouterModule.forRoot/forChild call expressions\n */\nfunction extractRoutesFromExpression(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\tif (node?.type !== \"CallExpression\") return routes\n\n\tconst callee = node.callee\n\tif (\n\t\tcallee?.type === \"MemberExpression\" &&\n\t\tcallee.object?.type === \"Identifier\" &&\n\t\tcallee.object.name === \"RouterModule\" &&\n\t\tcallee.property?.type === \"Identifier\" &&\n\t\t(callee.property.name === \"forRoot\" || callee.property.name === \"forChild\")\n\t) {\n\t\tconst routesArg = node.arguments[0]\n\t\tif (routesArg?.type === \"ArrayExpression\") {\n\t\t\tconst parsed = parseRoutesArray(routesArg, baseDir, warnings)\n\t\t\troutes.push(...parsed)\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Parse an array expression containing route objects\n */\nfunction parseRoutesArray(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tarrayNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const element of arrayNode.elements) {\n\t\tif (!element) continue\n\n\t\t// Handle spread elements\n\t\tif (element.type === \"SpreadElement\") {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Spread operator detected${loc}. Routes from spread cannot be statically analyzed.`,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (element.type === \"ObjectExpression\") {\n\t\t\tconst parsedRoute = parseRouteObject(element, baseDir, warnings)\n\t\t\tif (parsedRoute) {\n\t\t\t\troutes.push(parsedRoute)\n\t\t\t}\n\t\t} else {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Non-object route element (${element.type})${loc}. Only object literals can be statically analyzed.`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Parse a single route object expression\n */\nfunction parseRouteObject(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tobjectNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute | null {\n\tlet path: string | undefined\n\tlet component: string | undefined\n\tlet children: ParsedRoute[] | undefined\n\tlet redirectTo: string | undefined\n\tlet hasPath = false\n\n\tfor (const prop of objectNode.properties) {\n\t\tif (prop.type !== \"ObjectProperty\") continue\n\t\tif (prop.key.type !== \"Identifier\") continue\n\n\t\tconst key = prop.key.name\n\n\t\tswitch (key) {\n\t\t\tcase \"path\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\tpath = prop.value.value\n\t\t\t\t\thasPath = true\n\t\t\t\t} else {\n\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t`Dynamic path value (${prop.value.type})${loc}. Only string literal paths can be statically analyzed.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"component\":\n\t\t\t\t// Direct component reference: component: HomeComponent\n\t\t\t\tif (prop.value.type === \"Identifier\") {\n\t\t\t\t\tcomponent = prop.value.name\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"loadComponent\":\n\t\t\t\t// Lazy component: loadComponent: () => import('./path').then(m => m.Component)\n\t\t\t\tcomponent = extractLazyComponent(prop.value, baseDir, warnings)\n\t\t\t\tbreak\n\n\t\t\tcase \"loadChildren\": {\n\t\t\t\t// Lazy children: loadChildren: () => import('./path').then(m => m.routes)\n\t\t\t\t// We just note this for now - the actual routes are in another file\n\t\t\t\tconst lazyPath = extractLazyPath(prop.value, baseDir, warnings)\n\t\t\t\tif (lazyPath) {\n\t\t\t\t\tcomponent = `[lazy: ${lazyPath}]`\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase \"children\":\n\t\t\t\tif (prop.value.type === \"ArrayExpression\") {\n\t\t\t\t\tchildren = parseRoutesArray(prop.value, baseDir, warnings)\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"redirectTo\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\tredirectTo = prop.value.value\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\t// Skip these properties (not relevant for screen detection)\n\t\t\tcase \"pathMatch\":\n\t\t\tcase \"canActivate\":\n\t\t\tcase \"canDeactivate\":\n\t\t\tcase \"canMatch\":\n\t\t\tcase \"resolve\":\n\t\t\tcase \"data\":\n\t\t\tcase \"title\":\n\t\t\tcase \"providers\":\n\t\t\tcase \"runGuardsAndResolvers\":\n\t\t\tcase \"outlet\":\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t// Skip redirect-only routes (no component)\n\tif (redirectTo && !component && !children) {\n\t\treturn null\n\t}\n\n\t// Skip routes without path (unless they have children for layout purposes)\n\tif (!hasPath) {\n\t\tif (children && children.length > 0) {\n\t\t\treturn { path: \"\", component, children }\n\t\t}\n\t\treturn null\n\t}\n\n\treturn {\n\t\tpath: path || \"\",\n\t\tcomponent,\n\t\tchildren,\n\t}\n}\n\n/**\n * Extract component from lazy loadComponent pattern\n * loadComponent: () => import('./path').then(m => m.Component)\n */\nfunction extractLazyComponent(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Arrow function: () => import('./path').then(m => m.Component)\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\t// Handle .then(m => m.Component) chain\n\t\tif (\n\t\t\tbody.type === \"CallExpression\" &&\n\t\t\tbody.callee?.type === \"MemberExpression\" &&\n\t\t\tbody.callee.property?.type === \"Identifier\" &&\n\t\t\tbody.callee.property.name === \"then\"\n\t\t) {\n\t\t\tconst importCall = body.callee.object\n\t\t\tconst thenArg = body.arguments[0]\n\n\t\t\t// Check if it's an import() call\n\t\t\tif (\n\t\t\t\timportCall?.type === \"CallExpression\" &&\n\t\t\t\timportCall.callee?.type === \"Import\"\n\t\t\t) {\n\t\t\t\t// Check if the argument is a string literal\n\t\t\t\tif (importCall.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\t\tconst importPath = resolveImportPath(\n\t\t\t\t\t\timportCall.arguments[0].value,\n\t\t\t\t\t\tbaseDir,\n\t\t\t\t\t)\n\n\t\t\t\t\t// Extract component name from .then(m => m.Component)\n\t\t\t\t\tif (\n\t\t\t\t\t\tthenArg?.type === \"ArrowFunctionExpression\" &&\n\t\t\t\t\t\tthenArg.body?.type === \"MemberExpression\" &&\n\t\t\t\t\t\tthenArg.body.property?.type === \"Identifier\"\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn `${importPath}#${thenArg.body.property.name}`\n\t\t\t\t\t}\n\n\t\t\t\t\treturn importPath\n\t\t\t\t}\n\t\t\t\t// Dynamic import path - warn the user\n\t\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Lazy loadComponent with dynamic path${loc}. Only string literal imports can be analyzed.`,\n\t\t\t\t)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t}\n\n\t\t// Direct import without .then(): () => import('./path')\n\t\tif (body.type === \"CallExpression\" && body.callee?.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Lazy loadComponent with dynamic path${loc}. Only string literal imports can be analyzed.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\twarnings.push(\n\t\t`Unrecognized loadComponent pattern (${node.type})${loc}. Expected arrow function with import().then().`,\n\t)\n\treturn undefined\n}\n\n/**\n * Extract path from lazy loadChildren pattern\n * loadChildren: () => import('./path').then(m => m.routes)\n */\nfunction extractLazyPath(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\t// Handle .then() chain\n\t\tif (\n\t\t\tbody.type === \"CallExpression\" &&\n\t\t\tbody.callee?.type === \"MemberExpression\" &&\n\t\t\tbody.callee.property?.type === \"Identifier\" &&\n\t\t\tbody.callee.property.name === \"then\"\n\t\t) {\n\t\t\tconst importCall = body.callee.object\n\t\t\tif (\n\t\t\t\timportCall?.type === \"CallExpression\" &&\n\t\t\t\timportCall.callee?.type === \"Import\" &&\n\t\t\t\timportCall.arguments[0]?.type === \"StringLiteral\"\n\t\t\t) {\n\t\t\t\treturn resolveImportPath(importCall.arguments[0].value, baseDir)\n\t\t\t}\n\t\t}\n\n\t\t// Direct import without .then()\n\t\tif (body.type === \"CallExpression\" && body.callee?.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\twarnings.push(\n\t\t`Unrecognized loadChildren pattern (${node.type})${loc}. Expected arrow function with import().`,\n\t)\n\treturn undefined\n}\n\n/**\n * Detect if content is Angular Router based on patterns.\n * Checks for @angular/router import, RouterModule patterns, or Routes type annotation.\n */\nexport function isAngularRouterContent(content: string): boolean {\n\t// Check for Angular Router import\n\tif (content.includes(\"@angular/router\")) {\n\t\treturn true\n\t}\n\n\t// Check for RouterModule patterns\n\tif (\n\t\tcontent.includes(\"RouterModule.forRoot\") ||\n\t\tcontent.includes(\"RouterModule.forChild\")\n\t) {\n\t\treturn true\n\t}\n\n\t// Check for Routes type annotation: : Routes = or : Routes[\n\tif (/:\\s*Routes\\s*[=[]/.test(content)) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n","import { parse } from \"@babel/parser\"\nimport { pathToScreenId } from \"./routeParserUtils.js\"\n\n/**\n * Detected navigation target from a source file\n */\nexport interface DetectedNavigation {\n\t/** The target path (e.g., \"/users\", \"/billing/invoices/:id\") */\n\treadonly path: string\n\t/** Converted screen ID using pathToScreenId */\n\treadonly screenId: string\n\t/** Navigation method type */\n\treadonly type:\n\t\t| \"link\"\n\t\t| \"router-push\"\n\t\t| \"navigate\"\n\t\t| \"redirect\"\n\t\t| \"navigate-by-url\"\n\t/** Line number where detected */\n\treadonly line: number\n}\n\n/**\n * Factory function to create DetectedNavigation with proper screenId derivation.\n * Ensures the screenId is always correctly derived from the path.\n */\nexport function createDetectedNavigation(\n\tpath: string,\n\ttype: DetectedNavigation[\"type\"],\n\tline: number,\n): DetectedNavigation {\n\t// Strip query params and hash from path before converting to screenId\n\tconst pathWithoutQuery = path.split(\"?\")[0] ?? path\n\tconst cleanPath = pathWithoutQuery.split(\"#\")[0] ?? pathWithoutQuery\n\treturn {\n\t\tpath,\n\t\tscreenId: pathToScreenId(cleanPath),\n\t\ttype,\n\t\tline,\n\t}\n}\n\n/**\n * Result of analyzing a file for navigation\n */\nexport interface NavigationAnalysisResult {\n\t/** Detected navigation targets */\n\treadonly navigations: readonly DetectedNavigation[]\n\t/** Any warnings during analysis */\n\treadonly warnings: readonly string[]\n}\n\n/**\n * Result of analyzing a component for navigation.\n * Shared interface for framework-specific analyzers (Angular, Vue SFC, etc.)\n */\nexport interface ComponentAnalysisResult {\n\t/** Navigation targets detected in template */\n\treadonly templateNavigations: readonly DetectedNavigation[]\n\t/** Navigation targets detected in script */\n\treadonly scriptNavigations: readonly DetectedNavigation[]\n\t/** Any warnings during analysis */\n\treadonly warnings: readonly string[]\n}\n\n/**\n * Deduplicate navigations by screenId.\n * Exported for use by framework-specific analyzers.\n *\n * @param navigations - Array of detected navigations\n * @returns Array with duplicate screenIds removed (keeps first occurrence)\n */\nexport function deduplicateByScreenId(\n\tnavigations: DetectedNavigation[],\n): DetectedNavigation[] {\n\tconst seen = new Set<string>()\n\treturn navigations.filter((nav) => {\n\t\tif (seen.has(nav.screenId)) {\n\t\t\treturn false\n\t\t}\n\t\tseen.add(nav.screenId)\n\t\treturn true\n\t})\n}\n\n/**\n * Result of framework detection\n */\nexport interface FrameworkDetectionResult {\n\t/** Detected or default framework */\n\treadonly framework: NavigationFramework\n\t/** Whether the framework was explicitly detected (false means defaulted to Next.js) */\n\treadonly detected: boolean\n}\n\n/**\n * Navigation framework to detect\n */\nexport type NavigationFramework =\n\t| \"nextjs\"\n\t| \"react-router\"\n\t| \"vue-router\"\n\t| \"angular\"\n\t| \"solid-router\"\n\t| \"tanstack-router\"\n\n/**\n * Analyze a file's content for navigation patterns.\n *\n * Supports:\n * - Next.js: `<Link href=\"/path\">`, `router.push(\"/path\")`, `router.replace(\"/path\")`, `redirect(\"/path\")`\n * - React Router: `<Link to=\"/path\">`, `navigate(\"/path\")`\n * - Vue Router: `router.push(\"/path\")`, `router.replace(\"/path\")`, `router.push({ path: \"/path\" })`\n * - Angular: `router.navigate(['/path'])`, `router.navigateByUrl('/path')`\n * - Solid Router: `<A href=\"/path\">`, `navigate(\"/path\")`\n * - TanStack Router: `<Link to=\"/path\">`, `navigate({ to: \"/path\" })`\n *\n * @param content - The file content to analyze\n * @param framework - The navigation framework to detect\n * @returns Analysis result with detected navigations and warnings\n */\nexport function analyzeNavigation(\n\tcontent: string,\n\tframework: NavigationFramework,\n): NavigationAnalysisResult {\n\tconst navigations: DetectedNavigation[] = []\n\tconst warnings: string[] = []\n\n\t// Parse with Babel\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", \"jsx\", \"decorators-legacy\"],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\twarnings.push(`Syntax error during navigation analysis: ${error.message}`)\n\t\t\treturn { navigations, warnings }\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\twarnings.push(`Failed to parse file for navigation analysis: ${message}`)\n\t\treturn { navigations, warnings }\n\t}\n\n\t// Walk the AST with error handling\n\ttry {\n\t\twalkNode(ast.program, (node) => {\n\t\t\t// Detect JSX Link components\n\t\t\tif (node.type === \"JSXOpeningElement\") {\n\t\t\t\tconst linkNav = extractLinkNavigation(node, framework, warnings)\n\t\t\t\tif (linkNav) {\n\t\t\t\t\tnavigations.push(linkNav)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Detect call expressions (router.push, router.replace, navigate, redirect)\n\t\t\tif (node.type === \"CallExpression\") {\n\t\t\t\tconst callNav = extractCallNavigation(node, framework, warnings)\n\t\t\t\tif (callNav) {\n\t\t\t\t\tnavigations.push(callNav)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof RangeError) {\n\t\t\twarnings.push(\n\t\t\t\t`File too complex for navigation analysis (${error.message}). Consider simplifying the file structure.`,\n\t\t\t)\n\t\t\treturn { navigations, warnings }\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tconst errorType = error?.constructor?.name ?? \"Unknown\"\n\t\twarnings.push(`Unexpected ${errorType} during AST traversal: ${message}`)\n\t\treturn { navigations, warnings }\n\t}\n\n\t// Remove duplicates (same screenId)\n\tconst seen = new Set<string>()\n\tconst uniqueNavigations = navigations.filter((nav) => {\n\t\tif (seen.has(nav.screenId)) {\n\t\t\treturn false\n\t\t}\n\t\tseen.add(nav.screenId)\n\t\treturn true\n\t})\n\n\treturn { navigations: uniqueNavigations, warnings }\n}\n\n/**\n * Extract navigation from JSX Link component\n */\nfunction extractLinkNavigation(\n\t// biome-ignore lint/suspicious/noExplicitAny: Babel AST nodes have complex union types that are impractical to fully type\n\tnode: any,\n\tframework: NavigationFramework,\n\twarnings: string[],\n): DetectedNavigation | null {\n\t// Check if it's a Link component\n\tif (node.name?.type !== \"JSXIdentifier\") {\n\t\treturn null\n\t}\n\n\tconst componentName = node.name.name\n\t// Solid Router uses <A> component, others use <Link>\n\tif (componentName !== \"Link\" && componentName !== \"A\") {\n\t\treturn null\n\t}\n\n\t// Determine which attribute to look for based on framework\n\t// Next.js and Solid Router use href, React Router uses to\n\tconst attrName =\n\t\tframework === \"nextjs\" || framework === \"solid-router\" ? \"href\" : \"to\"\n\n\tfor (const attr of node.attributes || []) {\n\t\tif (\n\t\t\tattr.type === \"JSXAttribute\" &&\n\t\t\tattr.name?.type === \"JSXIdentifier\" &&\n\t\t\tattr.name.name === attrName\n\t\t) {\n\t\t\t// String literal: href=\"/path\"\n\t\t\tif (attr.value?.type === \"StringLiteral\") {\n\t\t\t\tconst path = attr.value.value\n\t\t\t\tif (isValidInternalPath(path)) {\n\t\t\t\t\treturn createDetectedNavigation(\n\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\"link\",\n\t\t\t\t\t\tnode.loc?.start.line ?? 0,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// JSX expression: href={\"/path\"} or href={`/path`}\n\t\t\tif (attr.value?.type === \"JSXExpressionContainer\") {\n\t\t\t\tconst expr = attr.value.expression\n\n\t\t\t\t// Simple string literal in expression\n\t\t\t\tif (expr.type === \"StringLiteral\") {\n\t\t\t\t\tconst path = expr.value\n\t\t\t\t\tif (isValidInternalPath(path)) {\n\t\t\t\t\t\treturn createDetectedNavigation(\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\"link\",\n\t\t\t\t\t\t\tnode.loc?.start.line ?? 0,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Template literal with no expressions (static template)\n\t\t\t\tif (\n\t\t\t\t\texpr.type === \"TemplateLiteral\" &&\n\t\t\t\t\texpr.expressions.length === 0 &&\n\t\t\t\t\texpr.quasis.length === 1\n\t\t\t\t) {\n\t\t\t\t\tconst path = expr.quasis[0].value.cooked\n\t\t\t\t\tif (path && isValidInternalPath(path)) {\n\t\t\t\t\t\treturn createDetectedNavigation(\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\"link\",\n\t\t\t\t\t\t\tnode.loc?.start.line ?? 0,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Dynamic expression - add warning with actionable guidance\n\t\t\t\tconst line = node.loc?.start.line ?? 0\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Dynamic ${componentName} ${attrName} at line ${line} cannot be statically analyzed. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null\n}\n\n/**\n * Extract navigation from function calls (router.push, router.replace, navigate, redirect)\n */\nfunction extractCallNavigation(\n\t// biome-ignore lint/suspicious/noExplicitAny: Babel AST nodes have complex union types that are impractical to fully type\n\tnode: any,\n\tframework: NavigationFramework,\n\twarnings: string[],\n): DetectedNavigation | null {\n\tconst callee = node.callee\n\n\t// router.push() or router.replace() - Next.js\n\tif (\n\t\tframework === \"nextjs\" &&\n\t\tcallee?.type === \"MemberExpression\" &&\n\t\tcallee.object?.type === \"Identifier\" &&\n\t\tcallee.object.name === \"router\" &&\n\t\tcallee.property?.type === \"Identifier\" &&\n\t\t(callee.property.name === \"push\" || callee.property.name === \"replace\")\n\t) {\n\t\treturn extractPathFromCallArgs(node, \"router-push\", warnings)\n\t}\n\n\t// router.push() or router.replace() - Vue Router\n\tif (\n\t\tframework === \"vue-router\" &&\n\t\tcallee?.type === \"MemberExpression\" &&\n\t\tcallee.object?.type === \"Identifier\" &&\n\t\tcallee.object.name === \"router\" &&\n\t\tcallee.property?.type === \"Identifier\" &&\n\t\t(callee.property.name === \"push\" || callee.property.name === \"replace\")\n\t) {\n\t\treturn extractPathFromCallArgs(node, \"router-push\", warnings)\n\t}\n\n\t// navigate() - React Router or Solid Router\n\tif (\n\t\t(framework === \"react-router\" || framework === \"solid-router\") &&\n\t\tcallee?.type === \"Identifier\" &&\n\t\tcallee.name === \"navigate\"\n\t) {\n\t\treturn extractPathFromCallArgs(node, \"navigate\", warnings)\n\t}\n\n\t// navigate({ to: '/path' }) - TanStack Router\n\tif (\n\t\tframework === \"tanstack-router\" &&\n\t\tcallee?.type === \"Identifier\" &&\n\t\tcallee.name === \"navigate\"\n\t) {\n\t\treturn extractPathFromObjectArg(node, \"navigate\", warnings, \"to\")\n\t}\n\n\t// redirect() - Next.js\n\tif (\n\t\tframework === \"nextjs\" &&\n\t\tcallee?.type === \"Identifier\" &&\n\t\tcallee.name === \"redirect\"\n\t) {\n\t\treturn extractPathFromCallArgs(node, \"redirect\", warnings)\n\t}\n\n\t// router.navigate() - Angular Router\n\t// Matches: this.router.navigate(['/path']) or router.navigate(['/path'])\n\tif (\n\t\tframework === \"angular\" &&\n\t\tcallee?.type === \"MemberExpression\" &&\n\t\tcallee.property?.type === \"Identifier\" &&\n\t\tcallee.property.name === \"navigate\"\n\t) {\n\t\tconst obj = callee.object\n\t\t// Match: this.router.navigate() or router.navigate()\n\t\tif (\n\t\t\t(obj?.type === \"MemberExpression\" &&\n\t\t\t\tobj.property?.type === \"Identifier\" &&\n\t\t\t\tobj.property.name === \"router\") ||\n\t\t\t(obj?.type === \"Identifier\" && obj.name === \"router\")\n\t\t) {\n\t\t\treturn extractPathFromArrayArg(node, \"navigate\", warnings)\n\t\t}\n\t}\n\n\t// router.navigateByUrl() - Angular Router\n\tif (\n\t\tframework === \"angular\" &&\n\t\tcallee?.type === \"MemberExpression\" &&\n\t\tcallee.property?.type === \"Identifier\" &&\n\t\tcallee.property.name === \"navigateByUrl\"\n\t) {\n\t\tconst obj = callee.object\n\t\tif (\n\t\t\t(obj?.type === \"MemberExpression\" &&\n\t\t\t\tobj.property?.type === \"Identifier\" &&\n\t\t\t\tobj.property.name === \"router\") ||\n\t\t\t(obj?.type === \"Identifier\" && obj.name === \"router\")\n\t\t) {\n\t\t\treturn extractPathFromCallArgs(node, \"navigate-by-url\", warnings)\n\t\t}\n\t}\n\n\treturn null\n}\n\n/**\n * Extract path from function call arguments\n */\nfunction extractPathFromCallArgs(\n\t// biome-ignore lint/suspicious/noExplicitAny: Babel AST nodes have complex union types that are impractical to fully type\n\tnode: any,\n\ttype: DetectedNavigation[\"type\"],\n\twarnings: string[],\n): DetectedNavigation | null {\n\tconst firstArg = node.arguments?.[0]\n\n\tif (!firstArg) {\n\t\treturn null\n\t}\n\n\t// String literal\n\tif (firstArg.type === \"StringLiteral\") {\n\t\tconst path = firstArg.value\n\t\tif (isValidInternalPath(path)) {\n\t\t\treturn createDetectedNavigation(path, type, node.loc?.start.line ?? 0)\n\t\t}\n\t}\n\n\t// Template literal with no expressions\n\tif (\n\t\tfirstArg.type === \"TemplateLiteral\" &&\n\t\tfirstArg.expressions.length === 0 &&\n\t\tfirstArg.quasis.length === 1\n\t) {\n\t\tconst path = firstArg.quasis[0].value.cooked\n\t\tif (path && isValidInternalPath(path)) {\n\t\t\treturn createDetectedNavigation(path, type, node.loc?.start.line ?? 0)\n\t\t}\n\t}\n\n\t// Object argument with path property (Vue Router style): { path: \"/users\" }\n\tif (firstArg.type === \"ObjectExpression\") {\n\t\tfor (const prop of firstArg.properties || []) {\n\t\t\tif (\n\t\t\t\tprop.type === \"ObjectProperty\" &&\n\t\t\t\tprop.key?.type === \"Identifier\" &&\n\t\t\t\tprop.key.name === \"path\"\n\t\t\t) {\n\t\t\t\t// String literal value\n\t\t\t\tif (prop.value?.type === \"StringLiteral\") {\n\t\t\t\t\tconst path = prop.value.value\n\t\t\t\t\tif (isValidInternalPath(path)) {\n\t\t\t\t\t\treturn createDetectedNavigation(\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\ttype,\n\t\t\t\t\t\t\tnode.loc?.start.line ?? 0,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Static template literal value\n\t\t\t\tif (\n\t\t\t\t\tprop.value?.type === \"TemplateLiteral\" &&\n\t\t\t\t\tprop.value.expressions.length === 0 &&\n\t\t\t\t\tprop.value.quasis.length === 1\n\t\t\t\t) {\n\t\t\t\t\tconst path = prop.value.quasis[0].value.cooked\n\t\t\t\t\tif (path && isValidInternalPath(path)) {\n\t\t\t\t\t\treturn createDetectedNavigation(\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\ttype,\n\t\t\t\t\t\t\tnode.loc?.start.line ?? 0,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Object without static path (named route or dynamic path)\n\t\tconst line = node.loc?.start.line ?? 0\n\t\twarnings.push(\n\t\t\t`Object-based navigation at line ${line} cannot be statically analyzed. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t\treturn null\n\t}\n\n\t// Dynamic argument - add warning with actionable guidance\n\tconst line = node.loc?.start.line ?? 0\n\twarnings.push(\n\t\t`Dynamic navigation path at line ${line} cannot be statically analyzed. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t)\n\n\treturn null\n}\n\n/**\n * Extract path from object argument with a specific property.\n *\n * @param node - The AST CallExpression node\n * @param type - The navigation type to assign\n * @param warnings - Array to collect warnings\n * @param propertyName - The property name to look for (e.g., \"to\" for TanStack Router)\n * @returns DetectedNavigation if a valid path is found, null otherwise\n *\n * @example\n * // TanStack Router: navigate({ to: '/users' }) -> extracts '/users'\n */\nfunction extractPathFromObjectArg(\n\t// biome-ignore lint/suspicious/noExplicitAny: Babel AST nodes have complex union types that are impractical to fully type\n\tnode: any,\n\ttype: DetectedNavigation[\"type\"],\n\twarnings: string[],\n\tpropertyName: string,\n): DetectedNavigation | null {\n\tconst firstArg = node.arguments?.[0]\n\n\tif (!firstArg) {\n\t\tconst line = node.loc?.start.line ?? 0\n\t\twarnings.push(\n\t\t\t`Navigation call at line ${line} has no arguments. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t\treturn null\n\t}\n\n\t// Object argument with specified property: { to: \"/users\" } or { path: \"/users\" }\n\tif (firstArg.type === \"ObjectExpression\") {\n\t\tfor (const prop of firstArg.properties || []) {\n\t\t\tif (\n\t\t\t\tprop.type === \"ObjectProperty\" &&\n\t\t\t\tprop.key?.type === \"Identifier\" &&\n\t\t\t\tprop.key.name === propertyName\n\t\t\t) {\n\t\t\t\t// String literal value\n\t\t\t\tif (prop.value?.type === \"StringLiteral\") {\n\t\t\t\t\tconst path = prop.value.value\n\t\t\t\t\tif (isValidInternalPath(path)) {\n\t\t\t\t\t\treturn createDetectedNavigation(\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\ttype,\n\t\t\t\t\t\t\tnode.loc?.start.line ?? 0,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Static template literal value\n\t\t\t\tif (\n\t\t\t\t\tprop.value?.type === \"TemplateLiteral\" &&\n\t\t\t\t\tprop.value.expressions.length === 0 &&\n\t\t\t\t\tprop.value.quasis.length === 1\n\t\t\t\t) {\n\t\t\t\t\tconst path = prop.value.quasis[0].value.cooked\n\t\t\t\t\tif (path && isValidInternalPath(path)) {\n\t\t\t\t\t\treturn createDetectedNavigation(\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\ttype,\n\t\t\t\t\t\t\tnode.loc?.start.line ?? 0,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Object without static property (named route or dynamic path)\n\t\tconst line = node.loc?.start.line ?? 0\n\t\twarnings.push(\n\t\t\t`Object-based navigation at line ${line} cannot be statically analyzed. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t\treturn null\n\t}\n\n\t// Non-object argument - add warning with actionable guidance\n\tconst line = node.loc?.start.line ?? 0\n\twarnings.push(\n\t\t`navigate() at line ${line} expects an object argument with a '${propertyName}' property (e.g., navigate({ ${propertyName}: '/path' })). Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t)\n\n\treturn null\n}\n\n/**\n * Extract path from array argument (Angular Router style)\n * e.g., router.navigate(['/users', userId]) -> extracts '/users'\n */\nfunction extractPathFromArrayArg(\n\t// biome-ignore lint/suspicious/noExplicitAny: Babel AST nodes have complex union types that are impractical to fully type\n\tnode: any,\n\ttype: DetectedNavigation[\"type\"],\n\twarnings: string[],\n): DetectedNavigation | null {\n\tconst firstArg = node.arguments?.[0]\n\n\tif (!firstArg) {\n\t\t// No argument provided\n\t\tconst line = node.loc?.start.line ?? 0\n\t\twarnings.push(\n\t\t\t`Navigation call at line ${line} has no arguments. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t\treturn null\n\t}\n\n\t// Array expression: ['/path', ...]\n\tif (firstArg.type === \"ArrayExpression\") {\n\t\tif (firstArg.elements.length === 0) {\n\t\t\t// Empty array\n\t\t\tconst line = node.loc?.start.line ?? 0\n\t\t\twarnings.push(\n\t\t\t\t`Navigation call at line ${line} has an empty array. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t\t)\n\t\t\treturn null\n\t\t}\n\t\tconst firstElement = firstArg.elements[0]\n\n\t\t// String literal as first element\n\t\tif (firstElement?.type === \"StringLiteral\") {\n\t\t\tconst path = firstElement.value\n\t\t\tif (isValidInternalPath(path)) {\n\t\t\t\treturn createDetectedNavigation(path, type, node.loc?.start.line ?? 0)\n\t\t\t}\n\t\t}\n\n\t\t// Static template literal as first element\n\t\tif (\n\t\t\tfirstElement?.type === \"TemplateLiteral\" &&\n\t\t\tfirstElement.expressions.length === 0 &&\n\t\t\tfirstElement.quasis.length === 1\n\t\t) {\n\t\t\tconst path = firstElement.quasis[0].value.cooked\n\t\t\tif (path && isValidInternalPath(path)) {\n\t\t\t\treturn createDetectedNavigation(path, type, node.loc?.start.line ?? 0)\n\t\t\t}\n\t\t}\n\n\t\t// Dynamic first element\n\t\tconst line = node.loc?.start.line ?? 0\n\t\twarnings.push(\n\t\t\t`Dynamic navigation path at line ${line} cannot be statically analyzed. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t\treturn null\n\t}\n\n\t// Non-array argument (dynamic)\n\tconst line = node.loc?.start.line ?? 0\n\twarnings.push(\n\t\t`Dynamic navigation path at line ${line} cannot be statically analyzed. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t)\n\treturn null\n}\n\n/**\n * Check if a path is a valid internal path (not external URL or hash link)\n */\nexport function isValidInternalPath(path: string): boolean {\n\t// Skip external URLs\n\tif (\n\t\tpath.startsWith(\"http://\") ||\n\t\tpath.startsWith(\"https://\") ||\n\t\tpath.startsWith(\"//\")\n\t) {\n\t\treturn false\n\t}\n\t// Skip hash-only links\n\tif (path.startsWith(\"#\")) {\n\t\treturn false\n\t}\n\t// Skip mailto and tel links\n\tif (path.startsWith(\"mailto:\") || path.startsWith(\"tel:\")) {\n\t\treturn false\n\t}\n\t// Must start with /\n\treturn path.startsWith(\"/\")\n}\n\n/**\n * Walk AST node recursively\n */\nfunction walkNode(\n\t// biome-ignore lint/suspicious/noExplicitAny: Babel AST nodes have complex union types that are impractical to fully type\n\tnode: any,\n\t// biome-ignore lint/suspicious/noExplicitAny: Babel AST nodes have complex union types that are impractical to fully type\n\tcallback: (node: any) => void,\n): void {\n\tif (!node || typeof node !== \"object\") return\n\n\tcallback(node)\n\n\tfor (const key of Object.keys(node)) {\n\t\tconst child = node[key]\n\t\tif (Array.isArray(child)) {\n\t\t\tfor (const item of child) {\n\t\t\t\twalkNode(item, callback)\n\t\t\t}\n\t\t} else if (child && typeof child === \"object\" && child.type) {\n\t\t\twalkNode(child, callback)\n\t\t}\n\t}\n}\n\n/**\n * Merge detected navigation targets with existing `next` array.\n * Removes duplicates and preserves manual entries.\n *\n * @param existing - Existing next array (may include manual entries)\n * @param detected - Newly detected navigation targets\n * @returns Merged array without duplicates\n */\nexport function mergeNext(\n\texisting: string[],\n\tdetected: DetectedNavigation[],\n): string[] {\n\tconst merged = new Set(existing)\n\n\tfor (const nav of detected) {\n\t\tmerged.add(nav.screenId)\n\t}\n\n\treturn Array.from(merged).sort()\n}\n\n/**\n * Detect navigation framework from file content.\n * Returns both the framework and whether it was explicitly detected.\n */\nexport function detectNavigationFramework(\n\tcontent: string,\n): FrameworkDetectionResult {\n\t// Check for Next.js patterns\n\tif (\n\t\tcontent.includes(\"next/link\") ||\n\t\tcontent.includes(\"next/navigation\") ||\n\t\tcontent.includes(\"next/router\")\n\t) {\n\t\treturn { framework: \"nextjs\", detected: true }\n\t}\n\n\t// Check for Vue Router patterns\n\tif (content.includes(\"vue-router\")) {\n\t\treturn { framework: \"vue-router\", detected: true }\n\t}\n\n\t// Check for Angular Router patterns\n\tif (content.includes(\"@angular/router\")) {\n\t\treturn { framework: \"angular\", detected: true }\n\t}\n\n\t// Check for Solid Router patterns\n\tif (content.includes(\"@solidjs/router\")) {\n\t\treturn { framework: \"solid-router\", detected: true }\n\t}\n\n\t// Check for TanStack Router patterns\n\tif (content.includes(\"@tanstack/react-router\")) {\n\t\treturn { framework: \"tanstack-router\", detected: true }\n\t}\n\n\t// Check for React Router patterns\n\tif (\n\t\tcontent.includes(\"react-router\") ||\n\t\tcontent.includes(\"@remix-run/react\")\n\t) {\n\t\treturn { framework: \"react-router\", detected: true }\n\t}\n\n\t// Check for useNavigate (React Router)\n\tif (content.includes(\"useNavigate\")) {\n\t\treturn { framework: \"react-router\", detected: true }\n\t}\n\n\t// Default to Next.js (more common in file-based routing projects)\n\treturn { framework: \"nextjs\", detected: false }\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { Parser } from \"htmlparser2\"\nimport {\n\tanalyzeNavigation,\n\ttype ComponentAnalysisResult,\n\tcreateDetectedNavigation,\n\ttype DetectedNavigation,\n\tdeduplicateByScreenId,\n\tisValidInternalPath,\n} from \"./navigationAnalyzer.js\"\n\n/**\n * Result of analyzing an Angular component for navigation.\n * Type alias for the shared ComponentAnalysisResult.\n */\nexport type AngularComponentAnalysisResult = ComponentAnalysisResult\n\n/**\n * Analyze an Angular component for navigation patterns.\n *\n * Detects:\n * - Template: `<a routerLink=\"/path\">`, `<a [routerLink]=\"'/path'\">`, `<a [routerLink]=\"['/path']\">`\n * - Script: `router.navigate(['/path'])`, `router.navigateByUrl('/path')`\n *\n * Results are deduplicated by screenId, keeping the first occurrence.\n *\n * @param content - The Angular component content to analyze\n * @param filePath - The file path (used for error messages and resolving templateUrl)\n * @param _cwd - The current working directory (unused, kept for API consistency with other analyzers)\n * @returns Analysis result with detected navigations and warnings\n *\n * @throws Never - All errors are caught and converted to warnings\n */\nexport function analyzeAngularComponent(\n\tcontent: string,\n\tfilePath: string,\n\t_cwd: string,\n): AngularComponentAnalysisResult {\n\tconst templateNavigations: DetectedNavigation[] = []\n\tconst scriptNavigations: DetectedNavigation[] = []\n\tconst warnings: string[] = []\n\n\ttry {\n\t\t// Extract template content from @Component decorator\n\t\tconst templateResult = extractTemplateContent(content, filePath)\n\n\t\tif (templateResult.warning) {\n\t\t\twarnings.push(templateResult.warning)\n\t\t}\n\n\t\tif (templateResult.content) {\n\t\t\tconst templateNavs = analyzeTemplateHTML(templateResult.content, warnings)\n\t\t\ttemplateNavigations.push(...templateNavs)\n\t\t}\n\n\t\t// Analyze script with existing navigation analyzer\n\t\tconst scriptResult = analyzeNavigation(content, \"angular\")\n\t\tscriptNavigations.push(...scriptResult.navigations)\n\t\twarnings.push(...scriptResult.warnings)\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\twarnings.push(`Syntax error in ${filePath}: ${error.message}`)\n\t\t} else if (error instanceof RangeError) {\n\t\t\twarnings.push(\n\t\t\t\t`${filePath}: File too complex for navigation analysis. Consider simplifying the component structure.`,\n\t\t\t)\n\t\t} else {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\twarnings.push(\n\t\t\t\t`${filePath}: Unexpected error during analysis: ${message}. Please report this as a bug.`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn {\n\t\ttemplateNavigations: deduplicateByScreenId(templateNavigations),\n\t\tscriptNavigations: deduplicateByScreenId(scriptNavigations),\n\t\twarnings,\n\t}\n}\n\n/**\n * Result of template extraction.\n */\ninterface TemplateExtractionResult {\n\t/** The extracted template content, or null if not found */\n\tcontent: string | null\n\t/** Warning message if extraction failed */\n\twarning: string | null\n}\n\n/**\n * Extract template content from @Component decorator.\n *\n * Handles both inline `template` and external `templateUrl` properties.\n * Uses regex-based extraction for reliability with Angular's decorator syntax.\n *\n * Limitations:\n * - Does not handle templates containing unescaped backticks\n * - Does not handle escaped quotes within template strings\n * - Returns warning if templateUrl file cannot be read\n *\n * @param content - The component TypeScript content\n * @param filePath - The file path for resolving relative templateUrl\n * @returns Extraction result with content and optional warning\n */\nfunction extractTemplateContent(\n\tcontent: string,\n\tfilePath: string,\n): TemplateExtractionResult {\n\t// Try to extract inline template using regex\n\t// Pattern 1: template: `...` (template literal)\n\t// Note: This simple pattern does not handle templates containing backticks\n\tconst backtickPattern = /template\\s*:\\s*`([^`]*)`/\n\tconst templateLiteralMatch = content.match(backtickPattern)\n\tif (templateLiteralMatch?.[1] !== undefined) {\n\t\treturn { content: templateLiteralMatch[1], warning: null }\n\t}\n\n\t// Pattern 2: template: '...' (single-quoted string)\n\tconst singleQuoteMatch = content.match(/template\\s*:\\s*'([^']*)'/)\n\tif (singleQuoteMatch?.[1] !== undefined) {\n\t\treturn { content: singleQuoteMatch[1], warning: null }\n\t}\n\n\t// Pattern 3: template: \"...\" (double-quoted string)\n\tconst doubleQuoteMatch = content.match(/template\\s*:\\s*\"([^\"]*)\"/)\n\tif (doubleQuoteMatch?.[1] !== undefined) {\n\t\treturn { content: doubleQuoteMatch[1], warning: null }\n\t}\n\n\t// Pattern 4: templateUrl: '...' or templateUrl: \"...\" (external file)\n\tconst templateUrlMatch = content.match(/templateUrl\\s*:\\s*['\"]([^'\"]+)['\"]/)\n\tif (templateUrlMatch?.[1]) {\n\t\tconst templatePath = resolve(dirname(filePath), templateUrlMatch[1])\n\t\ttry {\n\t\t\treturn { content: readFileSync(templatePath, \"utf-8\"), warning: null }\n\t\t} catch (error) {\n\t\t\tconst errorCode = (error as NodeJS.ErrnoException).code\n\t\t\tlet warning: string\n\n\t\t\tif (errorCode === \"ENOENT\") {\n\t\t\t\twarning =\n\t\t\t\t\t`Template file not found: ${templatePath}. ` +\n\t\t\t\t\t\"Navigation in this template will not be detected. \" +\n\t\t\t\t\t\"Add navigation targets manually to the 'next' field in screen.meta.ts.\"\n\t\t\t} else if (errorCode === \"EACCES\") {\n\t\t\t\twarning =\n\t\t\t\t\t`Permission denied reading template: ${templatePath}. ` +\n\t\t\t\t\t\"Check file permissions to enable template analysis.\"\n\t\t\t} else {\n\t\t\t\tconst msg = error instanceof Error ? error.message : String(error)\n\t\t\t\twarning = `Failed to read template file ${templatePath}: ${msg}`\n\t\t\t}\n\n\t\t\treturn { content: null, warning }\n\t\t}\n\t}\n\n\treturn { content: null, warning: null }\n}\n\n/**\n * Analyze HTML template for routerLink directives.\n *\n * Note: Line numbers are approximate as htmlparser2 only tracks newlines in text nodes,\n * not in tags or attributes. Line numbers may drift from actual positions.\n *\n * @param html - The HTML template content\n * @param warnings - Array to collect warnings (mutated)\n * @returns Array of detected navigation targets\n */\nfunction analyzeTemplateHTML(\n\thtml: string,\n\twarnings: string[],\n): DetectedNavigation[] {\n\tconst navigations: DetectedNavigation[] = []\n\tlet currentLine = 1\n\tconst parserErrors: string[] = []\n\n\tconst parser = new Parser({\n\t\tonopentag(_name, attribs) {\n\t\t\t// Note: htmlparser2 lowercases attribute names\n\t\t\t// Check for static routerLink (becomes routerlink)\n\t\t\tif (\"routerlink\" in attribs) {\n\t\t\t\tconst value = attribs.routerlink\n\t\t\t\tif (value) {\n\t\t\t\t\tif (isValidInternalPath(value)) {\n\t\t\t\t\t\tnavigations.push(\n\t\t\t\t\t\t\tcreateDetectedNavigation(value, \"link\", currentLine),\n\t\t\t\t\t\t)\n\t\t\t\t\t} else if (!value.startsWith(\"/\") && !value.includes(\"://\")) {\n\t\t\t\t\t\t// Looks like a relative path - warn user\n\t\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t\t`routerLink at line ~${currentLine} has relative path \"${value}\". ` +\n\t\t\t\t\t\t\t\t\"Angular routerLink paths should start with '/' for absolute routing. \" +\n\t\t\t\t\t\t\t\t\"Navigation will not be detected for this link.\",\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\t// External URLs are intentionally skipped without warning\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check for property binding [routerLink] (becomes [routerlink])\n\t\t\tif (\"[routerlink]\" in attribs) {\n\t\t\t\tconst expression = attribs[\"[routerlink]\"]\n\t\t\t\tconst nav = extractRouterLinkPath(expression, currentLine, warnings)\n\t\t\t\tif (nav) {\n\t\t\t\t\tnavigations.push(nav)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tontext(text) {\n\t\t\t// Track approximate line numbers by counting newlines in text nodes.\n\t\t\t// Note: Line numbers may be approximate as newlines in tags/attributes are not counted.\n\t\t\tcurrentLine += (text.match(/\\n/g) || []).length\n\t\t},\n\t\tonerror(error) {\n\t\t\tparserErrors.push(\n\t\t\t\t`HTML parsing error at line ~${currentLine}: ${error.message}. ` +\n\t\t\t\t\t\"Some navigation targets may not be detected.\",\n\t\t\t)\n\t\t},\n\t})\n\n\tparser.write(html)\n\tparser.end()\n\n\t// Add any parser errors to warnings\n\twarnings.push(...parserErrors)\n\n\treturn navigations\n}\n\n/**\n * Extract path from [routerLink] binding expression.\n *\n * Handles:\n * - String literals: `[routerLink]=\"'/path'\"` or `[routerLink]='\"/path\"'`\n * - Array literals: `[routerLink]=\"['/path']\"` or `[routerLink]=\"['/path', param]\"`\n * - Warns on dynamic expressions: `[routerLink]=\"dynamicPath\"`\n *\n * Note: Does not handle escaped quotes (e.g., \\' or \\\"). This is acceptable\n * because Angular routerLink paths should not contain quotes.\n *\n * @param expression - The binding expression value\n * @param line - Line number for warning messages (approximate)\n * @param warnings - Array to collect warnings (mutated)\n * @returns Detected navigation or null if path cannot be extracted\n */\nfunction extractRouterLinkPath(\n\texpression: string,\n\tline: number,\n\twarnings: string[],\n): DetectedNavigation | null {\n\tif (!expression) {\n\t\twarnings.push(\n\t\t\t`Empty [routerLink] binding at line ~${line}. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t\treturn null\n\t}\n\n\tconst trimmed = expression.trim()\n\n\t// Check for string literal: '/path' or \"/path\"\n\tif (\n\t\t(trimmed.startsWith(\"'\") && trimmed.endsWith(\"'\")) ||\n\t\t(trimmed.startsWith('\"') && trimmed.endsWith('\"'))\n\t) {\n\t\tconst path = trimmed.slice(1, -1)\n\t\tif (isValidInternalPath(path)) {\n\t\t\treturn createDetectedNavigation(path, \"link\", line)\n\t\t}\n\t\tif (!path.startsWith(\"/\") && !path.includes(\"://\")) {\n\t\t\twarnings.push(\n\t\t\t\t`[routerLink] at line ~${line} has relative path \"${path}\". ` +\n\t\t\t\t\t\"Angular routerLink paths should start with '/' for absolute routing. \" +\n\t\t\t\t\t\"Navigation will not be detected for this link.\",\n\t\t\t)\n\t\t}\n\t\treturn null\n\t}\n\n\t// Check for array literal: ['/path'] or ['/path', param]\n\tif (trimmed.startsWith(\"[\") && trimmed.endsWith(\"]\")) {\n\t\tconst arrayContent = trimmed.slice(1, -1).trim()\n\n\t\t// Find the first element (path) - handle comma separation\n\t\tconst firstComma = findFirstCommaOutsideQuotes(arrayContent)\n\t\tconst firstElement =\n\t\t\tfirstComma >= 0\n\t\t\t\t? arrayContent.slice(0, firstComma).trim()\n\t\t\t\t: arrayContent.trim()\n\n\t\t// Check if first element is a string literal\n\t\tif (\n\t\t\t(firstElement.startsWith(\"'\") && firstElement.endsWith(\"'\")) ||\n\t\t\t(firstElement.startsWith('\"') && firstElement.endsWith('\"'))\n\t\t) {\n\t\t\tconst path = firstElement.slice(1, -1)\n\t\t\tif (isValidInternalPath(path)) {\n\t\t\t\treturn createDetectedNavigation(path, \"link\", line)\n\t\t\t}\n\t\t\tif (!path.startsWith(\"/\") && !path.includes(\"://\")) {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`[routerLink] at line ~${line} has relative path \"${path}\". ` +\n\t\t\t\t\t\t\"Angular routerLink paths should start with '/' for absolute routing. \" +\n\t\t\t\t\t\t\"Navigation will not be detected for this link.\",\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\treturn null\n\t}\n\n\t// Dynamic expression - cannot be statically analyzed\n\twarnings.push(\n\t\t`Dynamic [routerLink] binding at line ~${line} cannot be statically analyzed. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t)\n\treturn null\n}\n\n/**\n * Find the index of the first comma outside of quotes.\n *\n * Note: Does not handle escaped quotes (e.g., \\' or \\\"). This is acceptable\n * because Angular routerLink paths should not contain quotes.\n *\n * @param str - The string to search\n * @returns Index of first comma, or -1 if not found\n */\nfunction findFirstCommaOutsideQuotes(str: string): number {\n\tlet inSingleQuote = false\n\tlet inDoubleQuote = false\n\n\tfor (let i = 0; i < str.length; i++) {\n\t\tconst char = str[i]\n\n\t\tif (char === \"'\" && !inDoubleQuote) {\n\t\t\tinSingleQuote = !inSingleQuote\n\t\t} else if (char === '\"' && !inSingleQuote) {\n\t\t\tinDoubleQuote = !inDoubleQuote\n\t\t} else if (char === \",\" && !inSingleQuote && !inDoubleQuote) {\n\t\t\treturn i\n\t\t}\n\t}\n\n\treturn -1\n}\n","import { parse } from \"@babel/parser\"\nimport type { ApiIntegrationConfig } from \"@screenbook/core\"\n\n/**\n * Detected API import from a source file\n */\nexport interface DetectedApiImport {\n\t/** The imported name (function, hook, or type) */\n\timportName: string\n\t/** The source package */\n\tpackageName: string\n\t/** Transformed name for dependsOn field (packageName/importName or custom) */\n\tdependsOnName: string\n\t/** Line number of the import */\n\tline: number\n}\n\n/**\n * Result of analyzing a file for API imports\n */\nexport interface ApiAnalysisResult {\n\t/** Detected API imports */\n\timports: DetectedApiImport[]\n\t/** Any warnings during analysis */\n\twarnings: string[]\n}\n\n/**\n * Analyze a file's content for API client imports.\n *\n * Supports:\n * - Named imports: `import { getUsers, createUser } from \"@api/client\"`\n * - Default imports: `import api from \"@api/client\"` (with warning)\n * - Namespace imports: `import * as api from \"@api/client\"` (with warning)\n *\n * @param content - The file content to analyze\n * @param config - API integration configuration\n * @returns Analysis result with detected imports and warnings\n */\nexport function analyzeApiImports(\n\tcontent: string,\n\tconfig: ApiIntegrationConfig,\n): ApiAnalysisResult {\n\tconst imports: DetectedApiImport[] = []\n\tconst warnings: string[] = []\n\n\t// Parse with Babel\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", \"jsx\"],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\twarnings.push(`Syntax error during import analysis: ${error.message}`)\n\t\t\treturn { imports, warnings }\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\twarnings.push(`Failed to parse file for import analysis: ${message}`)\n\t\treturn { imports, warnings }\n\t}\n\n\t// Normalize client packages for matching\n\tconst clientPackages = new Set(config.clientPackages)\n\n\t// Find import declarations\n\tfor (const node of ast.program.body) {\n\t\tif (node.type !== \"ImportDeclaration\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tconst source = node.source.value\n\t\tconst line = node.loc?.start.line ?? 0\n\n\t\t// Check if this import is from a configured client package\n\t\tif (!isMatchingPackage(source, clientPackages)) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Skip type-only imports\n\t\tif (node.importKind === \"type\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor (const specifier of node.specifiers) {\n\t\t\t// Skip type-only specifiers within a regular import\n\t\t\tif (\n\t\t\t\tspecifier.type === \"ImportSpecifier\" &&\n\t\t\t\tspecifier.importKind === \"type\"\n\t\t\t) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (specifier.type === \"ImportSpecifier\") {\n\t\t\t\t// Named import: import { getUsers } from \"@api/client\"\n\t\t\t\tconst importedName =\n\t\t\t\t\tspecifier.imported.type === \"Identifier\"\n\t\t\t\t\t\t? specifier.imported.name\n\t\t\t\t\t\t: specifier.imported.value\n\n\t\t\t\tconst transformedName = config.extractApiName\n\t\t\t\t\t? config.extractApiName(importedName)\n\t\t\t\t\t: importedName\n\n\t\t\t\timports.push({\n\t\t\t\t\timportName: importedName,\n\t\t\t\t\tpackageName: source,\n\t\t\t\t\tdependsOnName: `${source}/${transformedName}`,\n\t\t\t\t\tline,\n\t\t\t\t})\n\t\t\t} else if (specifier.type === \"ImportDefaultSpecifier\") {\n\t\t\t\t// Default import: import api from \"@api/client\"\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Default import from \"${source}\" at line ${line} cannot be statically analyzed. Consider using named imports.`,\n\t\t\t\t)\n\t\t\t} else if (specifier.type === \"ImportNamespaceSpecifier\") {\n\t\t\t\t// Namespace import: import * as api from \"@api/client\"\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Namespace import from \"${source}\" at line ${line} cannot be statically analyzed. Consider using named imports.`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { imports, warnings }\n}\n\n/**\n * Check if a source path matches any of the configured client packages.\n * Supports exact match and path prefix matching.\n */\nfunction isMatchingPackage(\n\tsource: string,\n\tclientPackages: Set<string>,\n): boolean {\n\t// Exact match\n\tif (clientPackages.has(source)) {\n\t\treturn true\n\t}\n\n\t// Check if source starts with any client package (for subpath imports)\n\t// e.g., \"@api/client\" should match \"@api/client/users\"\n\tfor (const pkg of clientPackages) {\n\t\tif (source.startsWith(`${pkg}/`)) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n/**\n * Merge detected API dependencies with existing ones.\n * Removes duplicates and preserves manual entries.\n *\n * @param existing - Existing dependsOn array (may include manual entries)\n * @param detected - Newly detected API dependencies\n * @returns Merged array without duplicates\n */\nexport function mergeDependsOn(\n\texisting: string[],\n\tdetected: DetectedApiImport[],\n): string[] {\n\tconst merged = new Set(existing)\n\n\tfor (const dep of detected) {\n\t\tmerged.add(dep.dependsOnName)\n\t}\n\n\treturn Array.from(merged).sort()\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { parse } from \"@babel/parser\"\nimport {\n\ttype ParsedRoute,\n\ttype ParseResult,\n\tresolveImportPath,\n} from \"./routeParserUtils.js\"\n\n// Re-export shared types\nexport type { ParsedRoute, ParseResult }\n\n/**\n * Parse Solid Router configuration file and extract routes.\n * Supports various export patterns including `export const routes`, `export default`,\n * and TypeScript's `satisfies` operator.\n *\n * @param filePath - Path to the router configuration file\n * @param preloadedContent - Optional pre-read file content to avoid duplicate file reads\n * @returns ParseResult containing extracted routes and any warnings\n * @throws Error if the file cannot be read or contains syntax errors\n */\nexport function parseSolidRouterConfig(\n\tfilePath: string,\n\tpreloadedContent?: string,\n): ParseResult {\n\tconst absolutePath = resolve(filePath)\n\tconst routesFileDir = dirname(absolutePath)\n\tconst warnings: string[] = []\n\n\t// Read file with proper error handling (skip if content is preloaded)\n\tlet content: string\n\tif (preloadedContent !== undefined) {\n\t\tcontent = preloadedContent\n\t} else {\n\t\ttry {\n\t\t\tcontent = readFileSync(absolutePath, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to read routes file \"${absolutePath}\": ${message}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t// Parse with Babel - wrap for better error messages\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", \"jsx\"],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Syntax error in routes file \"${absolutePath}\": ${error.message}`,\n\t\t\t)\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tthrow new Error(`Failed to parse routes file \"${absolutePath}\": ${message}`)\n\t}\n\n\tconst routes: ParsedRoute[] = []\n\n\t// Find routes array in the AST\n\tfor (const node of ast.program.body) {\n\t\t// Handle: const routes = [...]\n\t\tif (node.type === \"VariableDeclaration\") {\n\t\t\tfor (const decl of node.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export const routes = [...]\n\t\tif (\n\t\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t\t) {\n\t\t\tfor (const decl of node.declaration.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export default [...]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(node.declaration, routesFileDir, warnings)\n\t\t\troutes.push(...parsed)\n\t\t}\n\n\t\t// Handle: export default [...] satisfies RouteDefinition[]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"TSSatisfiesExpression\" &&\n\t\t\tnode.declaration.expression.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(\n\t\t\t\tnode.declaration.expression,\n\t\t\t\troutesFileDir,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t\troutes.push(...parsed)\n\t\t}\n\t}\n\n\t// Warn if no routes were found\n\tif (routes.length === 0) {\n\t\twarnings.push(\n\t\t\t\"No routes found. Supported patterns: 'export const routes = [...]' or 'export default [...]'\",\n\t\t)\n\t}\n\n\treturn { routes, warnings }\n}\n\n/**\n * Parse an array expression containing route objects\n */\nfunction parseRoutesArray(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tarrayNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const element of arrayNode.elements) {\n\t\tif (!element) continue\n\n\t\t// Handle spread elements\n\t\tif (element.type === \"SpreadElement\") {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Spread operator detected${loc}. Routes from spread cannot be statically analyzed.`,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (element.type === \"ObjectExpression\") {\n\t\t\tconst parsedRoutes = parseRouteObject(element, baseDir, warnings)\n\t\t\troutes.push(...parsedRoutes)\n\t\t} else {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Non-object route element (${element.type})${loc}. Only object literals can be statically analyzed.`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Parse a single route object expression\n * Returns array to handle multiple paths case: path: [\"/a\", \"/b\"]\n */\nfunction parseRouteObject(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tobjectNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tlet paths: string[] = []\n\tlet component: string | undefined\n\tlet children: ParsedRoute[] | undefined\n\tlet hasPath = false\n\n\tfor (const prop of objectNode.properties) {\n\t\tif (prop.type !== \"ObjectProperty\") continue\n\t\tif (prop.key.type !== \"Identifier\") continue\n\n\t\tconst key = prop.key.name\n\n\t\tswitch (key) {\n\t\t\tcase \"path\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\tpaths = [prop.value.value]\n\t\t\t\t\thasPath = true\n\t\t\t\t} else if (prop.value.type === \"ArrayExpression\") {\n\t\t\t\t\t// Solid Router supports path arrays: path: [\"/login\", \"/register\"]\n\t\t\t\t\tconst arrayElementCount = prop.value.elements.filter(Boolean).length\n\t\t\t\t\tpaths = extractPathArray(prop.value, warnings)\n\t\t\t\t\thasPath = paths.length > 0\n\t\t\t\t\t// Warn if path array had elements but none were extractable\n\t\t\t\t\tif (arrayElementCount > 0 && paths.length === 0) {\n\t\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t\t`Path array contains only dynamic values${loc}. No static paths could be extracted.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t`Dynamic path value (${prop.value.type})${loc}. Only string literal paths can be statically analyzed.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"component\":\n\t\t\t\tcomponent = extractComponent(prop.value, baseDir, warnings)\n\t\t\t\tbreak\n\n\t\t\tcase \"children\":\n\t\t\t\tif (prop.value.type === \"ArrayExpression\") {\n\t\t\t\t\tchildren = parseRoutesArray(prop.value, baseDir, warnings)\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\t// preload and matchFilters are ignored (not relevant for screen detection)\n\t\t}\n\t}\n\n\t// Skip routes without path (abstract layout wrappers)\n\tif (!hasPath) {\n\t\t// If it has children, process them with empty parent path contribution\n\t\tif (children && children.length > 0) {\n\t\t\treturn [{ path: \"\", component, children }]\n\t\t}\n\t\treturn []\n\t}\n\n\t// Create routes for each path (handles path arrays)\n\treturn paths.map((path) => ({\n\t\tpath,\n\t\tcomponent,\n\t\tchildren,\n\t}))\n}\n\n/**\n * Extract paths from array expression\n * path: [\"/login\", \"/register\"] -> [\"/login\", \"/register\"]\n */\nfunction extractPathArray(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tarrayNode: any,\n\twarnings: string[],\n): string[] {\n\tconst paths: string[] = []\n\n\tfor (const element of arrayNode.elements) {\n\t\tif (!element) continue\n\n\t\tif (element.type === \"StringLiteral\") {\n\t\t\tpaths.push(element.value)\n\t\t} else {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Non-string path in array (${element.type})${loc}. Only string literal paths can be analyzed.`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn paths\n}\n\n/**\n * Extract component from various patterns\n * - Direct identifier: component: Home\n * - Lazy component: component: lazy(() => import(\"./Home\"))\n */\nfunction extractComponent(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Direct component reference: component: Home\n\tif (node.type === \"Identifier\") {\n\t\treturn node.name\n\t}\n\n\t// Lazy component: component: lazy(() => import(\"./path\"))\n\tif (node.type === \"CallExpression\") {\n\t\tconst callee = node.callee\n\t\tif (callee.type === \"Identifier\" && callee.name === \"lazy\") {\n\t\t\tconst lazyArg = node.arguments[0]\n\t\t\tif (lazyArg) {\n\t\t\t\treturn extractLazyImportPath(lazyArg, baseDir, warnings)\n\t\t\t}\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`lazy() called without arguments${loc}. Expected arrow function with import().`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// Other call expressions not supported - add warning\n\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\tconst calleeName = callee.type === \"Identifier\" ? callee.name : \"unknown\"\n\t\twarnings.push(\n\t\t\t`Unrecognized component pattern: ${calleeName}(...)${loc}. Only 'lazy(() => import(...))' is supported.`,\n\t\t)\n\t\treturn undefined\n\t}\n\n\t// Arrow function component: component: () => <Home />\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tif (node.body.type === \"JSXElement\") {\n\t\t\tconst openingElement = node.body.openingElement\n\t\t\tif (openingElement?.name?.type === \"JSXIdentifier\") {\n\t\t\t\treturn openingElement.name.name\n\t\t\t}\n\t\t\t// JSXMemberExpression (namespaced components like <UI.Button />)\n\t\t\tif (openingElement?.name?.type === \"JSXMemberExpression\") {\n\t\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Namespaced JSX component (e.g., <UI.Button />)${loc}. Component extraction not supported for member expressions. Consider using a direct component reference or create a wrapper component.`,\n\t\t\t\t)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t}\n\t\t// Block body arrow functions\n\t\tif (node.body.type === \"BlockStatement\") {\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Arrow function with block body${loc}. Only concise arrow functions returning JSX directly can be analyzed.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// JSX Fragments\n\t\tif (node.body.type === \"JSXFragment\") {\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`JSX Fragment detected${loc}. Cannot extract component name from fragments.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// Conditional expressions\n\t\tif (node.body.type === \"ConditionalExpression\") {\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\t// Try to extract component names from both branches for context\n\t\t\tlet componentInfo = \"\"\n\t\t\tconst consequent = node.body.consequent\n\t\t\tconst alternate = node.body.alternate\n\t\t\tif (\n\t\t\t\tconsequent?.type === \"JSXElement\" &&\n\t\t\t\talternate?.type === \"JSXElement\"\n\t\t\t) {\n\t\t\t\tconst consName = consequent.openingElement?.name?.name || \"unknown\"\n\t\t\t\tconst altName = alternate.openingElement?.name?.name || \"unknown\"\n\t\t\t\tcomponentInfo = ` (${consName} or ${altName})`\n\t\t\t}\n\t\t\twarnings.push(\n\t\t\t\t`Conditional component${componentInfo}${loc}. Only static JSX elements can be analyzed. Consider extracting to a separate component.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// Unrecognized arrow function body\n\t\tconst arrowLoc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\twarnings.push(\n\t\t\t`Unrecognized arrow function body (${node.body.type})${arrowLoc}. Component will not be extracted.`,\n\t\t)\n\t\treturn undefined\n\t}\n\n\t// Catch-all for unrecognized component patterns\n\tif (node) {\n\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\twarnings.push(\n\t\t\t`Unrecognized component pattern (${node.type})${loc}. Component will not be extracted.`,\n\t\t)\n\t}\n\treturn undefined\n}\n\n/**\n * Extract import path from lazy function argument\n * () => import(\"./pages/Dashboard\") -> resolved path\n */\nfunction extractLazyImportPath(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Arrow function: () => import('./path')\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\tif (body.type === \"CallExpression\" && body.callee.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t\t// Dynamic import argument\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Lazy import with dynamic path${loc}. Only string literal imports can be analyzed.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\t// Unrecognized lazy pattern\n\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\twarnings.push(\n\t\t`Unrecognized lazy pattern (${node.type})${loc}. Expected arrow function with import().`,\n\t)\n\treturn undefined\n}\n\n/**\n * Detect if content is Solid Router based on patterns.\n * Note: Called by detectRouterType() in reactRouterParser.ts before React Router detection\n * because Solid Router and React Router share similar syntax patterns (both use `path` and `component`).\n * The detection order matters: TanStack Router -> Solid Router -> Angular Router -> React Router -> Vue Router.\n */\nexport function isSolidRouterContent(content: string): boolean {\n\t// Check for Solid Router specific import\n\tif (content.includes(\"@solidjs/router\")) {\n\t\treturn true\n\t}\n\n\t// Check for old package name\n\tif (content.includes(\"solid-app-router\")) {\n\t\treturn true\n\t}\n\n\t// Check for Solid.js lazy with route pattern\n\t// This is a weaker check, so we need to be more specific\n\tif (\n\t\tcontent.includes(\"solid-js\") &&\n\t\t/\\blazy\\s*\\(/.test(content) &&\n\t\t/\\bcomponent\\s*:/.test(content) &&\n\t\t/\\bpath\\s*:/.test(content)\n\t) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { parse } from \"@babel/parser\"\nimport {\n\ttype ParsedRoute,\n\ttype ParseResult,\n\tresolveImportPath,\n} from \"./routeParserUtils.js\"\n\n// Re-export shared types\nexport type { ParsedRoute, ParseResult }\n\n/**\n * Internal route definition collected from createRoute/createRootRoute calls\n */\ninterface RouteDefinition {\n\tvariableName: string\n\tpath?: string\n\tcomponent?: string\n\tparentVariableName?: string\n\tisRoot: boolean\n\tchildren?: string[] // Variable names of child routes from addChildren\n}\n\n/**\n * Parse TanStack Router configuration file and extract routes\n */\nexport function parseTanStackRouterConfig(\n\tfilePath: string,\n\tpreloadedContent?: string,\n): ParseResult {\n\tconst absolutePath = resolve(filePath)\n\tconst routesFileDir = dirname(absolutePath)\n\tconst warnings: string[] = []\n\n\t// Read file with proper error handling (skip if content is preloaded)\n\tlet content: string\n\tif (preloadedContent !== undefined) {\n\t\tcontent = preloadedContent\n\t} else {\n\t\ttry {\n\t\t\tcontent = readFileSync(absolutePath, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to read routes file \"${absolutePath}\": ${message}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t// Parse with Babel - wrap for better error messages\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", \"jsx\"],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Syntax error in routes file \"${absolutePath}\": ${error.message}`,\n\t\t\t)\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tthrow new Error(`Failed to parse routes file \"${absolutePath}\": ${message}`)\n\t}\n\n\t// Collect all route definitions from the AST\n\tconst routeMap = new Map<string, RouteDefinition>()\n\n\t// Two-pass AST processing is required because TanStack Router uses a function-based API\n\t// where routes are defined as variables and then composed using .addChildren().\n\t// Pass 1 must collect all route definitions first, so Pass 2 can resolve variable references.\n\n\t// First pass: collect all createRoute/createRootRoute calls\n\tfor (const node of ast.program.body) {\n\t\tcollectRouteDefinitions(node, routeMap, routesFileDir, warnings)\n\t}\n\n\t// Second pass: process addChildren calls to build parent-child relationships\n\tfor (const node of ast.program.body) {\n\t\tprocessAddChildrenCalls(node, routeMap, warnings)\n\t}\n\n\t// Build the route tree from collected definitions\n\tconst routes = buildRouteTree(routeMap, warnings)\n\n\t// Warn if no routes were found\n\tif (routes.length === 0) {\n\t\twarnings.push(\n\t\t\t\"No routes found. Supported patterns: 'createRootRoute()', 'createRoute()', and '.addChildren([...])'\",\n\t\t)\n\t}\n\n\treturn { routes, warnings }\n}\n\n/**\n * Collect route definitions from AST nodes\n */\nfunction collectRouteDefinitions(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\trouteMap: Map<string, RouteDefinition>,\n\tbaseDir: string,\n\twarnings: string[],\n): void {\n\t// Handle: const xxxRoute = createRoute({ ... }) or createRootRoute({ ... })\n\tif (node.type === \"VariableDeclaration\") {\n\t\tfor (const decl of node.declarations) {\n\t\t\tif (decl.id.type !== \"Identifier\") continue\n\n\t\t\tconst variableName = decl.id.name\n\n\t\t\t// Handle direct createRoute/createRootRoute call\n\t\t\tif (decl.init?.type === \"CallExpression\") {\n\t\t\t\tconst routeDef = extractRouteFromCallExpression(\n\t\t\t\t\tdecl.init,\n\t\t\t\t\tvariableName,\n\t\t\t\t\tbaseDir,\n\t\t\t\t\twarnings,\n\t\t\t\t)\n\t\t\t\tif (routeDef) {\n\t\t\t\t\trouteMap.set(variableName, routeDef)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle createRoute(...).lazy(...) pattern\n\t\t\tif (\n\t\t\t\tdecl.init?.type === \"CallExpression\" &&\n\t\t\t\tdecl.init.callee?.type === \"MemberExpression\" &&\n\t\t\t\tdecl.init.callee.property?.type === \"Identifier\" &&\n\t\t\t\tdecl.init.callee.property.name === \"lazy\"\n\t\t\t) {\n\t\t\t\t// The createRoute call is in callee.object\n\t\t\t\tconst createRouteCall = decl.init.callee.object\n\t\t\t\tif (createRouteCall?.type === \"CallExpression\") {\n\t\t\t\t\tconst routeDef = extractRouteFromCallExpression(\n\t\t\t\t\t\tcreateRouteCall,\n\t\t\t\t\t\tvariableName,\n\t\t\t\t\t\tbaseDir,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t)\n\t\t\t\t\tif (routeDef) {\n\t\t\t\t\t\t// Extract lazy import path\n\t\t\t\t\t\tconst lazyArg = decl.init.arguments[0]\n\t\t\t\t\t\tif (lazyArg) {\n\t\t\t\t\t\t\tconst lazyPath = extractLazyImportPath(lazyArg, baseDir, warnings)\n\t\t\t\t\t\t\tif (lazyPath) {\n\t\t\t\t\t\t\t\trouteDef.component = lazyPath\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\trouteMap.set(variableName, routeDef)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Handle: export const xxxRoute = createRoute({ ... })\n\tif (\n\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t) {\n\t\tfor (const decl of node.declaration.declarations) {\n\t\t\tif (decl.id.type !== \"Identifier\") continue\n\n\t\t\tconst variableName = decl.id.name\n\n\t\t\tif (decl.init?.type === \"CallExpression\") {\n\t\t\t\tconst routeDef = extractRouteFromCallExpression(\n\t\t\t\t\tdecl.init,\n\t\t\t\t\tvariableName,\n\t\t\t\t\tbaseDir,\n\t\t\t\t\twarnings,\n\t\t\t\t)\n\t\t\t\tif (routeDef) {\n\t\t\t\t\trouteMap.set(variableName, routeDef)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Extract route definition from a CallExpression (createRoute or createRootRoute)\n */\nfunction extractRouteFromCallExpression(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tcallNode: any,\n\tvariableName: string,\n\tbaseDir: string,\n\twarnings: string[],\n): RouteDefinition | null {\n\tconst callee = callNode.callee\n\n\t// Check if it's createRoute or createRootRoute\n\tlet isRoot = false\n\tlet optionsArg = callNode.arguments[0]\n\n\tif (callee.type === \"Identifier\") {\n\t\tif (callee.name === \"createRootRoute\") {\n\t\t\tisRoot = true\n\t\t} else if (callee.name === \"createRootRouteWithContext\") {\n\t\t\tisRoot = true\n\t\t} else if (callee.name !== \"createRoute\") {\n\t\t\treturn null\n\t\t}\n\t} else if (callee.type === \"CallExpression\") {\n\t\t// Handle curried form: createRootRouteWithContext<T>()({...})\n\t\tconst innerCallee = callee.callee\n\t\tif (\n\t\t\tinnerCallee?.type === \"Identifier\" &&\n\t\t\tinnerCallee.name === \"createRootRouteWithContext\"\n\t\t) {\n\t\t\tisRoot = true\n\t\t\t// Options are in the outer call's arguments\n\t\t\toptionsArg = callNode.arguments[0]\n\t\t} else {\n\t\t\treturn null\n\t\t}\n\t} else {\n\t\treturn null\n\t}\n\n\tconst routeDef: RouteDefinition = {\n\t\tvariableName,\n\t\tisRoot,\n\t}\n\tif (optionsArg?.type === \"ObjectExpression\") {\n\t\tfor (const prop of optionsArg.properties) {\n\t\t\tif (prop.type !== \"ObjectProperty\") continue\n\t\t\tif (prop.key.type !== \"Identifier\") continue\n\n\t\t\tconst key = prop.key.name\n\n\t\t\tswitch (key) {\n\t\t\t\tcase \"path\":\n\t\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\t\t// Normalize TanStack Router path: $param -> :param\n\t\t\t\t\t\trouteDef.path = normalizeTanStackPath(prop.value.value)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t\t`Dynamic path value (${prop.value.type})${loc}. Only string literal paths can be statically analyzed.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\n\t\t\t\tcase \"component\":\n\t\t\t\t\trouteDef.component = extractComponentValue(\n\t\t\t\t\t\tprop.value,\n\t\t\t\t\t\tbaseDir,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\n\t\t\t\tcase \"getParentRoute\":\n\t\t\t\t\t// Extract parent route variable name from arrow function\n\t\t\t\t\tif (prop.value.type === \"ArrowFunctionExpression\") {\n\t\t\t\t\t\tconst body = prop.value.body\n\t\t\t\t\t\tif (body.type === \"Identifier\") {\n\t\t\t\t\t\t\trouteDef.parentVariableName = body.name\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t\t\t`Dynamic getParentRoute${loc}. Only static route references can be analyzed.`,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\treturn routeDef\n}\n\n/**\n * Extract component value from different patterns\n * Returns undefined with a warning for unrecognized patterns\n */\nfunction extractComponentValue(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Direct component reference: component: Home\n\tif (node.type === \"Identifier\") {\n\t\treturn node.name\n\t}\n\n\t// lazyRouteComponent(() => import('./path'))\n\tif (node.type === \"CallExpression\") {\n\t\tconst callee = node.callee\n\t\tif (callee.type === \"Identifier\" && callee.name === \"lazyRouteComponent\") {\n\t\t\tconst importArg = node.arguments[0]\n\t\t\tif (importArg) {\n\t\t\t\treturn extractLazyImportPath(importArg, baseDir, warnings)\n\t\t\t}\n\t\t\t// lazyRouteComponent called without arguments\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`lazyRouteComponent called without arguments${loc}. Expected arrow function with import().`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// Other call expressions are not supported\n\t\treturn undefined\n\t}\n\n\t// Arrow function component: component: () => <Home />\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\t// Check if body is JSX\n\t\tif (node.body.type === \"JSXElement\") {\n\t\t\tconst openingElement = node.body.openingElement\n\t\t\tif (openingElement?.name?.type === \"JSXIdentifier\") {\n\t\t\t\treturn openingElement.name.name\n\t\t\t}\n\t\t\t// Handle JSXMemberExpression like <Namespace.Component />\n\t\t\tif (openingElement?.name?.type === \"JSXMemberExpression\") {\n\t\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Namespaced JSX component${loc}. Component extraction not fully supported for member expressions.`,\n\t\t\t\t)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t}\n\t\t// Block body arrow functions: () => { return <Home /> }\n\t\tif (node.body.type === \"BlockStatement\") {\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Arrow function with block body${loc}. Only concise arrow functions returning JSX directly can be analyzed.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// JSX Fragment: () => <>...</>\n\t\tif (node.body.type === \"JSXFragment\") {\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`JSX Fragment detected${loc}. Cannot extract component name from fragments.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\treturn undefined\n}\n\n/**\n * Extract import path from lazy function patterns\n */\nfunction extractLazyImportPath(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Arrow function: () => import('./path')\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\t// Direct import: () => import('./path')\n\t\tif (body.type === \"CallExpression\" && body.callee.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t}\n\n\t\t// Chained: () => import('./path').then(d => d.Route)\n\t\tif (\n\t\t\tbody.type === \"CallExpression\" &&\n\t\t\tbody.callee.type === \"MemberExpression\" &&\n\t\t\tbody.callee.object?.type === \"CallExpression\" &&\n\t\t\tbody.callee.object.callee?.type === \"Import\"\n\t\t) {\n\t\t\tconst importCall = body.callee.object\n\t\t\tif (importCall.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(importCall.arguments[0].value, baseDir)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\twarnings.push(\n\t\t`Unrecognized lazy pattern (${node.type})${loc}. Expected arrow function with import().`,\n\t)\n\treturn undefined\n}\n\n/**\n * Process addChildren calls to establish parent-child relationships\n */\nfunction processAddChildrenCalls(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\trouteMap: Map<string, RouteDefinition>,\n\twarnings: string[],\n): void {\n\t// Handle: const routeTree = rootRoute.addChildren([...])\n\t// or: const routeTree = rootRoute.addChildren([indexRoute, aboutRoute.addChildren([...])])\n\tif (node.type === \"VariableDeclaration\") {\n\t\tfor (const decl of node.declarations) {\n\t\t\tprocessAddChildrenExpression(decl.init, routeMap, warnings)\n\t\t}\n\t}\n\n\t// Handle: export const routeTree = rootRoute.addChildren([...])\n\tif (\n\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t) {\n\t\tfor (const decl of node.declaration.declarations) {\n\t\t\tprocessAddChildrenExpression(decl.init, routeMap, warnings)\n\t\t}\n\t}\n}\n\n/**\n * Recursively process addChildren expressions\n */\nfunction processAddChildrenExpression(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\texpr: any,\n\trouteMap: Map<string, RouteDefinition>,\n\twarnings: string[],\n): string | undefined {\n\tif (!expr) return undefined\n\n\t// Handle: parentRoute.addChildren([...])\n\tif (\n\t\texpr.type === \"CallExpression\" &&\n\t\texpr.callee?.type === \"MemberExpression\" &&\n\t\texpr.callee.property?.type === \"Identifier\" &&\n\t\texpr.callee.property.name === \"addChildren\"\n\t) {\n\t\t// Get parent route variable name\n\t\tlet parentVarName: string | undefined\n\t\tif (expr.callee.object?.type === \"Identifier\") {\n\t\t\tparentVarName = expr.callee.object.name\n\t\t} else if (expr.callee.object?.type === \"CallExpression\") {\n\t\t\t// Nested addChildren: parentRoute.addChildren([...]).addChildren([...])\n\t\t\t// This is rare but handle recursively\n\t\t\tparentVarName = processAddChildrenExpression(\n\t\t\t\texpr.callee.object,\n\t\t\t\trouteMap,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t}\n\n\t\tif (!parentVarName) return undefined\n\n\t\tconst parentDef = routeMap.get(parentVarName)\n\t\tif (!parentDef) {\n\t\t\tconst loc = expr.loc ? ` at line ${expr.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Parent route \"${parentVarName}\" not found${loc}. Ensure it's defined with createRoute/createRootRoute.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\n\t\t// Process children array\n\t\tconst childrenArg = expr.arguments[0]\n\t\tif (childrenArg?.type === \"ArrayExpression\") {\n\t\t\tconst childNames: string[] = []\n\n\t\t\tfor (const element of childrenArg.elements) {\n\t\t\t\tif (!element) continue\n\n\t\t\t\t// Direct reference: indexRoute\n\t\t\t\tif (element.type === \"Identifier\") {\n\t\t\t\t\tchildNames.push(element.name)\n\t\t\t\t}\n\t\t\t\t// Nested addChildren: aboutRoute.addChildren([...])\n\t\t\t\telse if (element.type === \"CallExpression\") {\n\t\t\t\t\tconst nestedParent = processAddChildrenExpression(\n\t\t\t\t\t\telement,\n\t\t\t\t\t\trouteMap,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t)\n\t\t\t\t\tif (nestedParent) {\n\t\t\t\t\t\tchildNames.push(nestedParent)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Spread operator\n\t\t\t\telse if (element.type === \"SpreadElement\") {\n\t\t\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t`Spread operator detected${loc}. Routes from spread cannot be statically analyzed.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tparentDef.children = childNames\n\t\t}\n\n\t\treturn parentVarName\n\t}\n\n\treturn undefined\n}\n\n/**\n * Build the route tree from collected definitions\n */\nfunction buildRouteTree(\n\trouteMap: Map<string, RouteDefinition>,\n\twarnings: string[],\n): ParsedRoute[] {\n\t// Find root routes\n\tconst rootDefs = Array.from(routeMap.values()).filter((def) => def.isRoot)\n\n\tif (rootDefs.length === 0) {\n\t\t// No explicit root route, try to build from parent relationships\n\t\treturn buildTreeFromParentRelations(routeMap, warnings)\n\t}\n\n\t// Build tree starting from root routes\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const rootDef of rootDefs) {\n\t\t// Use a Set to track visited routes and detect circular references\n\t\tconst visited = new Set<string>()\n\t\tconst rootRoute = buildRouteFromDefinition(\n\t\t\trootDef,\n\t\t\trouteMap,\n\t\t\twarnings,\n\t\t\tvisited,\n\t\t)\n\t\tif (rootRoute) {\n\t\t\t// If root has children, return only the children (root is typically just a layout)\n\t\t\t// because the root route in TanStack Router serves as a layout wrapper\n\t\t\tif (rootRoute.children && rootRoute.children.length > 0) {\n\t\t\t\troutes.push(...rootRoute.children)\n\t\t\t} else if (rootRoute.path) {\n\t\t\t\troutes.push(rootRoute)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Build tree when there's no explicit root route\n * Falls back to finding routes that have no parent relationship defined\n */\nfunction buildTreeFromParentRelations(\n\trouteMap: Map<string, RouteDefinition>,\n\twarnings: string[],\n): ParsedRoute[] {\n\t// Find routes without parents (top-level routes)\n\tconst topLevelDefs = Array.from(routeMap.values()).filter(\n\t\t(def) => !def.parentVariableName && !def.isRoot,\n\t)\n\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const def of topLevelDefs) {\n\t\tconst visited = new Set<string>()\n\t\tconst route = buildRouteFromDefinition(def, routeMap, warnings, visited)\n\t\tif (route) {\n\t\t\troutes.push(route)\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Build a ParsedRoute from a RouteDefinition\n * @param visited - Set of visited variable names for circular reference detection\n */\nfunction buildRouteFromDefinition(\n\tdef: RouteDefinition,\n\trouteMap: Map<string, RouteDefinition>,\n\twarnings: string[],\n\tvisited: Set<string>,\n): ParsedRoute | null {\n\t// Circular reference detection\n\tif (visited.has(def.variableName)) {\n\t\twarnings.push(\n\t\t\t`Circular reference detected: route \"${def.variableName}\" references itself in the route tree.`,\n\t\t)\n\t\treturn null\n\t}\n\tvisited.add(def.variableName)\n\n\tconst route: ParsedRoute = {\n\t\tpath: def.path ?? \"\",\n\t\tcomponent: def.component,\n\t}\n\n\t// Process children\n\tif (def.children && def.children.length > 0) {\n\t\tconst children: ParsedRoute[] = []\n\t\tfor (const childName of def.children) {\n\t\t\tconst childDef = routeMap.get(childName)\n\t\t\tif (childDef) {\n\t\t\t\tconst childRoute = buildRouteFromDefinition(\n\t\t\t\t\tchildDef,\n\t\t\t\t\trouteMap,\n\t\t\t\t\twarnings,\n\t\t\t\t\tvisited,\n\t\t\t\t)\n\t\t\t\tif (childRoute) {\n\t\t\t\t\tchildren.push(childRoute)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Child route \"${childName}\" not found. Ensure it's defined with createRoute.`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tif (children.length > 0) {\n\t\t\troute.children = children\n\t\t}\n\t}\n\n\treturn route\n}\n\n/**\n * Normalize TanStack Router path syntax to standard format\n * /$ (trailing catch-all) -> /*\n * $ (standalone splat) -> *\n * $param (dynamic segment) -> :param\n */\nfunction normalizeTanStackPath(path: string): string {\n\treturn (\n\t\tpath\n\t\t\t// Convert catch-all: $ at end -> *\n\t\t\t.replace(/\\/\\$$/, \"/*\")\n\t\t\t// Convert single $ to * (splat route)\n\t\t\t.replace(/^\\$$/, \"*\")\n\t\t\t// Convert $param to :param\n\t\t\t.replace(/\\$([a-zA-Z_][a-zA-Z0-9_]*)/g, \":$1\")\n\t)\n}\n\n/**\n * Detect if content is TanStack Router based on patterns\n */\nexport function isTanStackRouterContent(content: string): boolean {\n\t// Check for TanStack Router specific imports\n\tif (content.includes(\"@tanstack/react-router\")) {\n\t\treturn true\n\t}\n\n\t// Check for createRootRoute pattern\n\tif (content.includes(\"createRootRoute\")) {\n\t\treturn true\n\t}\n\n\t// Check for createRoute with getParentRoute (TanStack Router specific)\n\tif (content.includes(\"createRoute\") && content.includes(\"getParentRoute\")) {\n\t\treturn true\n\t}\n\n\t// Check for lazyRouteComponent (TanStack Router specific)\n\tif (content.includes(\"lazyRouteComponent\")) {\n\t\treturn true\n\t}\n\n\t// Check for addChildren pattern (TanStack Router specific)\n\tif (/\\.addChildren\\s*\\(/.test(content)) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { parse } from \"@babel/parser\"\nimport { isAngularRouterContent } from \"./angularRouterParser.js\"\nimport {\n\ttype ParsedRoute,\n\ttype ParseResult,\n\ttype RouterType,\n\tresolveImportPath,\n} from \"./routeParserUtils.js\"\nimport { isSolidRouterContent } from \"./solidRouterParser.js\"\nimport { isTanStackRouterContent } from \"./tanstackRouterParser.js\"\n\n// Re-export shared types\nexport type { ParsedRoute, ParseResult, RouterType }\n// Re-export router detection for convenience\nexport { isAngularRouterContent, isSolidRouterContent, isTanStackRouterContent }\n\n/**\n * Router factory function names to detect\n */\nconst ROUTER_FACTORY_NAMES = [\n\t\"createBrowserRouter\",\n\t\"createHashRouter\",\n\t\"createMemoryRouter\",\n]\n\n/**\n * Parse React Router configuration file and extract routes\n */\nexport function parseReactRouterConfig(\n\tfilePath: string,\n\tpreloadedContent?: string,\n): ParseResult {\n\tconst absolutePath = resolve(filePath)\n\tconst routesFileDir = dirname(absolutePath)\n\tconst warnings: string[] = []\n\n\t// Read file with proper error handling (skip if content is preloaded)\n\tlet content: string\n\tif (preloadedContent !== undefined) {\n\t\tcontent = preloadedContent\n\t} else {\n\t\ttry {\n\t\t\tcontent = readFileSync(absolutePath, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to read routes file \"${absolutePath}\": ${message}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t// Parse with Babel - wrap for better error messages\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", \"jsx\"],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Syntax error in routes file \"${absolutePath}\": ${error.message}`,\n\t\t\t)\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tthrow new Error(`Failed to parse routes file \"${absolutePath}\": ${message}`)\n\t}\n\n\tconst routes: ParsedRoute[] = []\n\n\t// Find routes array in the AST\n\tfor (const node of ast.program.body) {\n\t\t// Handle: const router = createBrowserRouter([...])\n\t\tif (node.type === \"VariableDeclaration\") {\n\t\t\tfor (const decl of node.declarations) {\n\t\t\t\tif (decl.init?.type === \"CallExpression\") {\n\t\t\t\t\tconst callee = decl.init.callee\n\t\t\t\t\tif (\n\t\t\t\t\t\tcallee.type === \"Identifier\" &&\n\t\t\t\t\t\tROUTER_FACTORY_NAMES.includes(callee.name)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst firstArg = decl.init.arguments[0]\n\t\t\t\t\t\tif (firstArg?.type === \"ArrayExpression\") {\n\t\t\t\t\t\t\tconst parsed = parseRoutesArray(firstArg, routesFileDir, warnings)\n\t\t\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export const router = createBrowserRouter([...])\n\t\t// and: export const routes = [...]\n\t\tif (\n\t\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t\t) {\n\t\t\tfor (const decl of node.declaration.declarations) {\n\t\t\t\t// Handle: export const router = createBrowserRouter([...])\n\t\t\t\tif (decl.init?.type === \"CallExpression\") {\n\t\t\t\t\tconst callee = decl.init.callee\n\t\t\t\t\tif (\n\t\t\t\t\t\tcallee.type === \"Identifier\" &&\n\t\t\t\t\t\tROUTER_FACTORY_NAMES.includes(callee.name)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst firstArg = decl.init.arguments[0]\n\t\t\t\t\t\tif (firstArg?.type === \"ArrayExpression\") {\n\t\t\t\t\t\t\tconst parsed = parseRoutesArray(firstArg, routesFileDir, warnings)\n\t\t\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Handle: export const routes = [...]\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: const routes = [...]; (for later export)\n\t\tif (node.type === \"VariableDeclaration\") {\n\t\t\tfor (const decl of node.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export default [...]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(node.declaration, routesFileDir, warnings)\n\t\t\troutes.push(...parsed)\n\t\t}\n\n\t\t// Handle: export default [...] satisfies RouteObject[]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"TSSatisfiesExpression\" &&\n\t\t\tnode.declaration.expression.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(\n\t\t\t\tnode.declaration.expression,\n\t\t\t\troutesFileDir,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t\troutes.push(...parsed)\n\t\t}\n\t}\n\n\t// Warn if no routes were found\n\tif (routes.length === 0) {\n\t\twarnings.push(\n\t\t\t\"No routes found. Supported patterns: 'createBrowserRouter([...])', 'export const routes = [...]', or 'export default [...]'\",\n\t\t)\n\t}\n\n\treturn { routes, warnings }\n}\n\n/**\n * Parse an array expression containing route objects\n */\nfunction parseRoutesArray(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tarrayNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const element of arrayNode.elements) {\n\t\tif (!element) continue\n\n\t\t// Handle spread elements\n\t\tif (element.type === \"SpreadElement\") {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Spread operator detected${loc}. Routes from spread cannot be statically analyzed.`,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (element.type === \"ObjectExpression\") {\n\t\t\tconst route = parseRouteObject(element, baseDir, warnings)\n\t\t\tif (route) {\n\t\t\t\troutes.push(route)\n\t\t\t}\n\t\t} else {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Non-object route element (${element.type})${loc}. Only object literals can be statically analyzed.`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Parse a single route object expression\n */\nfunction parseRouteObject(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tobjectNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute | null {\n\tconst route: ParsedRoute = {\n\t\tpath: \"\",\n\t}\n\tlet isIndexRoute = false\n\tlet hasPath = false\n\n\tfor (const prop of objectNode.properties) {\n\t\tif (prop.type !== \"ObjectProperty\") continue\n\t\tif (prop.key.type !== \"Identifier\") continue\n\n\t\tconst key = prop.key.name\n\n\t\tswitch (key) {\n\t\t\tcase \"path\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\troute.path = prop.value.value\n\t\t\t\t\thasPath = true\n\t\t\t\t} else {\n\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t`Dynamic path value (${prop.value.type})${loc}. Only string literal paths can be statically analyzed.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"index\":\n\t\t\t\tif (prop.value.type === \"BooleanLiteral\" && prop.value.value === true) {\n\t\t\t\t\tisIndexRoute = true\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"element\":\n\t\t\t\troute.component = extractComponentFromJSX(prop.value, warnings)\n\t\t\t\tbreak\n\n\t\t\tcase \"Component\":\n\t\t\t\tif (prop.value.type === \"Identifier\") {\n\t\t\t\t\troute.component = prop.value.name\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"lazy\": {\n\t\t\t\tconst lazyPath = extractLazyImportPath(prop.value, baseDir, warnings)\n\t\t\t\tif (lazyPath) {\n\t\t\t\t\troute.component = lazyPath\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase \"children\":\n\t\t\t\tif (prop.value.type === \"ArrayExpression\") {\n\t\t\t\t\troute.children = parseRoutesArray(prop.value, baseDir, warnings)\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t// Handle index routes\n\tif (isIndexRoute) {\n\t\troute.path = \"\"\n\t\treturn route\n\t}\n\n\t// Skip routes without path (layout wrappers without path)\n\t// These are parent routes that only serve as layout containers\n\tif (!hasPath && !isIndexRoute) {\n\t\t// If it has children, process them with empty parent path contribution\n\t\tif (route.children && route.children.length > 0) {\n\t\t\t// Return a route with empty path to act as layout\n\t\t\troute.path = \"\"\n\t\t\treturn route\n\t\t}\n\t\treturn null\n\t}\n\n\treturn route\n}\n\n/**\n * Extract component name from JSX element\n * element: <Dashboard /> -> \"Dashboard\"\n */\nfunction extractComponentFromJSX(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\twarnings: string[],\n): string | undefined {\n\tif (node.type === \"JSXElement\") {\n\t\tconst openingElement = node.openingElement\n\t\tif (openingElement?.name?.type === \"JSXIdentifier\") {\n\t\t\treturn openingElement.name.name\n\t\t}\n\t\tif (openingElement?.name?.type === \"JSXMemberExpression\") {\n\t\t\t// Handle <Namespace.Component />\n\t\t\tconst parts: string[] = []\n\t\t\tlet current = openingElement.name\n\t\t\twhile (current) {\n\t\t\t\tif (current.type === \"JSXIdentifier\") {\n\t\t\t\t\tparts.unshift(current.name)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif (current.type === \"JSXMemberExpression\") {\n\t\t\t\t\tif (current.property?.type === \"JSXIdentifier\") {\n\t\t\t\t\t\tparts.unshift(current.property.name)\n\t\t\t\t\t}\n\t\t\t\t\tcurrent = current.object\n\t\t\t\t} else {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn parts.join(\".\")\n\t\t}\n\n\t\t// Check if it has children (wrapper component)\n\t\tif (\n\t\t\tnode.children &&\n\t\t\tnode.children.length > 0 &&\n\t\t\topeningElement?.name?.type === \"JSXIdentifier\"\n\t\t) {\n\t\t\tconst wrapperName = openingElement.name.name\n\t\t\t// Find the first JSX child\n\t\t\tfor (const child of node.children) {\n\t\t\t\tif (child.type === \"JSXElement\") {\n\t\t\t\t\tconst childComponent = extractComponentFromJSX(child, warnings)\n\t\t\t\t\tif (childComponent) {\n\t\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t\t`Wrapper component detected: <${wrapperName}>. Using wrapper name for screen.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn wrapperName\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Handle JSXFragment\n\tif (node.type === \"JSXFragment\") {\n\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\twarnings.push(\n\t\t\t`JSX Fragment detected${loc}. Cannot extract component name from fragments. Consider wrapping in a named component.`,\n\t\t)\n\t\treturn undefined\n\t}\n\n\t// Catch-all for unrecognized element patterns\n\tif (node) {\n\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\twarnings.push(\n\t\t\t`Unrecognized element pattern (${node.type})${loc}. Component will not be extracted.`,\n\t\t)\n\t}\n\treturn undefined\n}\n\n/**\n * Extract import path from lazy function\n * lazy: () => import(\"./pages/Dashboard\") -> resolved path\n */\nfunction extractLazyImportPath(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Arrow function: () => import('./path')\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\tif (body.type === \"CallExpression\" && body.callee.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t\t// Dynamic import argument\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Lazy import with dynamic path${loc}. Only string literal imports can be statically analyzed.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\t// Unrecognized lazy pattern\n\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\twarnings.push(\n\t\t`Unrecognized lazy pattern (${node.type})${loc}. Expected arrow function with import().`,\n\t)\n\treturn undefined\n}\n\n/**\n * Detect if content is React Router based on patterns\n */\nexport function isReactRouterContent(content: string): boolean {\n\t// Check for React Router specific patterns\n\tif (\n\t\tcontent.includes(\"createBrowserRouter\") ||\n\t\tcontent.includes(\"createHashRouter\") ||\n\t\tcontent.includes(\"createMemoryRouter\") ||\n\t\tcontent.includes(\"RouteObject\")\n\t) {\n\t\treturn true\n\t}\n\n\t// Check for JSX element pattern in routes\n\tif (/element:\\s*</.test(content)) {\n\t\treturn true\n\t}\n\n\t// Check for Component property pattern (uppercase)\n\tif (/Component:\\s*[A-Z]/.test(content)) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n/**\n * Detect if content is Vue Router based on patterns\n */\nexport function isVueRouterContent(content: string): boolean {\n\t// Check for Vue Router specific patterns\n\tif (\n\t\tcontent.includes(\"RouteRecordRaw\") ||\n\t\tcontent.includes(\"vue-router\") ||\n\t\tcontent.includes(\".vue\")\n\t) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n/**\n * Detect router type from file content.\n * Detection order: TanStack Router -> Solid Router -> Angular Router -> React Router -> Vue Router.\n * This order ensures more specific patterns are checked before more generic ones.\n */\nexport function detectRouterType(content: string): RouterType {\n\t// Check TanStack Router first (more specific patterns)\n\tif (isTanStackRouterContent(content)) {\n\t\treturn \"tanstack-router\"\n\t}\n\t// Check Solid Router before React Router (both use similar patterns)\n\tif (isSolidRouterContent(content)) {\n\t\treturn \"solid-router\"\n\t}\n\t// Check Angular Router before React Router (distinct patterns)\n\tif (isAngularRouterContent(content)) {\n\t\treturn \"angular-router\"\n\t}\n\tif (isReactRouterContent(content)) {\n\t\treturn \"react-router\"\n\t}\n\tif (isVueRouterContent(content)) {\n\t\treturn \"vue-router\"\n\t}\n\treturn \"unknown\"\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { parse } from \"@babel/parser\"\nimport {\n\ttype FlatRoute,\n\tflattenRoutes,\n\ttype ParsedRoute,\n\ttype ParseResult,\n\tpathToScreenId,\n\tpathToScreenTitle,\n\tresolveImportPath,\n} from \"./routeParserUtils.js\"\n\n// Re-export shared types and utilities\nexport type { ParsedRoute, FlatRoute, ParseResult }\nexport { flattenRoutes, pathToScreenId, pathToScreenTitle }\n\n/**\n * Parse Vue Router configuration file and extract routes\n */\nexport function parseVueRouterConfig(\n\tfilePath: string,\n\tpreloadedContent?: string,\n): ParseResult {\n\tconst absolutePath = resolve(filePath)\n\tconst routesFileDir = dirname(absolutePath)\n\tconst warnings: string[] = []\n\n\t// Read file with proper error handling (skip if content is preloaded)\n\tlet content: string\n\tif (preloadedContent !== undefined) {\n\t\tcontent = preloadedContent\n\t} else {\n\t\ttry {\n\t\t\tcontent = readFileSync(absolutePath, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to read routes file \"${absolutePath}\": ${message}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t// Parse with Babel - wrap for better error messages\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", \"jsx\"],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Syntax error in routes file \"${absolutePath}\": ${error.message}`,\n\t\t\t)\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tthrow new Error(`Failed to parse routes file \"${absolutePath}\": ${message}`)\n\t}\n\n\tconst routes: ParsedRoute[] = []\n\n\t// Find routes array in the AST\n\tfor (const node of ast.program.body) {\n\t\t// Handle: export const routes = [...]\n\t\tif (\n\t\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t\t) {\n\t\t\tfor (const decl of node.declaration.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: const routes = [...]; export { routes }\n\t\tif (node.type === \"VariableDeclaration\") {\n\t\t\tfor (const decl of node.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export default [...]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(node.declaration, routesFileDir, warnings)\n\t\t\troutes.push(...parsed)\n\t\t}\n\n\t\t// Handle: export default [...] satisfies RouteRecordRaw[]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"TSSatisfiesExpression\" &&\n\t\t\tnode.declaration.expression.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(\n\t\t\t\tnode.declaration.expression,\n\t\t\t\troutesFileDir,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t\troutes.push(...parsed)\n\t\t}\n\t}\n\n\t// Warn if no routes were found\n\tif (routes.length === 0) {\n\t\twarnings.push(\n\t\t\t\"No routes array found. Supported patterns: 'export const routes = [...]', 'export default [...]', or 'export default [...] satisfies RouteRecordRaw[]'\",\n\t\t)\n\t}\n\n\treturn { routes, warnings }\n}\n\n/**\n * Parse an array expression containing route objects\n */\nfunction parseRoutesArray(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tarrayNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const element of arrayNode.elements) {\n\t\tif (!element) continue\n\n\t\t// Handle spread elements\n\t\tif (element.type === \"SpreadElement\") {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Spread operator detected${loc}. Routes from spread cannot be statically analyzed.`,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (element.type === \"ObjectExpression\") {\n\t\t\tconst route = parseRouteObject(element, baseDir, warnings)\n\t\t\tif (route) {\n\t\t\t\troutes.push(route)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Parse a single route object expression\n */\nfunction parseRouteObject(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tobjectNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute | null {\n\tconst route: ParsedRoute = {\n\t\tpath: \"\",\n\t}\n\n\tfor (const prop of objectNode.properties) {\n\t\tif (prop.type !== \"ObjectProperty\") continue\n\t\tif (prop.key.type !== \"Identifier\") continue\n\n\t\tconst key = prop.key.name\n\n\t\tswitch (key) {\n\t\t\tcase \"path\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\troute.path = prop.value.value\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"name\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\troute.name = prop.value.value\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"redirect\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\troute.redirect = prop.value.value\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"component\":\n\t\t\t\troute.component = extractComponentPath(prop.value, baseDir)\n\t\t\t\tbreak\n\n\t\t\tcase \"children\":\n\t\t\t\tif (prop.value.type === \"ArrayExpression\") {\n\t\t\t\t\troute.children = parseRoutesArray(prop.value, baseDir, warnings)\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t// Skip routes without path\n\tif (!route.path) {\n\t\treturn null\n\t}\n\n\treturn route\n}\n\n/**\n * Extract component path from various component definitions\n */\n// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\nfunction extractComponentPath(node: any, baseDir: string): string | undefined {\n\t// Direct identifier: component: HomeView\n\tif (node.type === \"Identifier\") {\n\t\treturn undefined // Can't resolve without tracking imports\n\t}\n\n\t// Arrow function with import: () => import('./views/Home.vue')\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\t// () => import('./path')\n\t\tif (body.type === \"CallExpression\" && body.callee.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t}\n\n\t\t// () => import(/* webpackChunkName */ './path')\n\t\tif (body.type === \"CallExpression\" && body.callee.type === \"Import\") {\n\t\t\tfor (const arg of body.arguments) {\n\t\t\t\tif (arg.type === \"StringLiteral\") {\n\t\t\t\t\treturn resolveImportPath(arg.value, baseDir)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn undefined\n}\n","import type { ElementNode, TemplateChildNode } from \"@vue/compiler-core\"\nimport { NodeTypes } from \"@vue/compiler-core\"\nimport { parse } from \"@vue/compiler-sfc\"\nimport {\n\tanalyzeNavigation,\n\ttype ComponentAnalysisResult,\n\tcreateDetectedNavigation,\n\ttype DetectedNavigation,\n\tdeduplicateByScreenId,\n\tisValidInternalPath,\n} from \"./navigationAnalyzer.js\"\n\n/**\n * Result of analyzing a Vue SFC for navigation.\n * Type alias for the shared ComponentAnalysisResult.\n */\nexport type VueSFCAnalysisResult = ComponentAnalysisResult\n\n/**\n * Analyze a Vue Single File Component for navigation patterns.\n *\n * Detects:\n * - Template: `<RouterLink to=\"/path\">`, `<router-link to=\"/path\">`\n * - Template: `:to=\"'/path'\"` (static string binding)\n * - Script: `router.push(\"/path\")`, `router.replace(\"/path\")`\n * - Script: `router.push({ path: \"/path\" })` (object-based navigation)\n *\n * @param content - The Vue SFC content to analyze\n * @param filePath - The file path (used for error messages)\n * @returns Analysis result with detected navigations and warnings\n */\nexport function analyzeVueSFC(\n\tcontent: string,\n\tfilePath: string,\n): VueSFCAnalysisResult {\n\tconst templateNavigations: DetectedNavigation[] = []\n\tconst scriptNavigations: DetectedNavigation[] = []\n\tconst warnings: string[] = []\n\n\ttry {\n\t\tconst { descriptor, errors } = parse(content, {\n\t\t\tfilename: filePath,\n\t\t\tsourceMap: false,\n\t\t})\n\n\t\tfor (const error of errors) {\n\t\t\twarnings.push(`SFC parse error: ${error.message}`)\n\t\t}\n\n\t\tif (descriptor.template?.ast) {\n\t\t\tconst templateResult = analyzeTemplateAST(\n\t\t\t\tdescriptor.template.ast.children,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t\ttemplateNavigations.push(...templateResult)\n\t\t}\n\n\t\tconst scriptContent =\n\t\t\tdescriptor.scriptSetup?.content || descriptor.script?.content\n\t\tif (scriptContent) {\n\t\t\tconst scriptResult = analyzeNavigation(scriptContent, \"vue-router\")\n\t\t\tscriptNavigations.push(...scriptResult.navigations)\n\t\t\twarnings.push(...scriptResult.warnings)\n\t\t}\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\twarnings.push(`SFC syntax error in ${filePath}: ${error.message}`)\n\t\t} else if (error instanceof RangeError) {\n\t\t\twarnings.push(\n\t\t\t\t`${filePath}: File too complex for navigation analysis. Consider simplifying the template structure.`,\n\t\t\t)\n\t\t} else {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\twarnings.push(\n\t\t\t\t`${filePath}: Unexpected error during analysis: ${message}. Please report this as a bug.`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn {\n\t\ttemplateNavigations: deduplicateByScreenId(templateNavigations),\n\t\tscriptNavigations: deduplicateByScreenId(scriptNavigations),\n\t\twarnings,\n\t}\n}\n\n/**\n * Analyze template AST for RouterLink components.\n *\n * @param nodes - Array of template child nodes to traverse\n * @param warnings - Array to collect warnings during analysis (mutated)\n * @returns Array of detected navigation targets from RouterLink components\n */\nfunction analyzeTemplateAST(\n\tnodes: TemplateChildNode[],\n\twarnings: string[],\n): DetectedNavigation[] {\n\tconst navigations: DetectedNavigation[] = []\n\n\twalkTemplateNodes(nodes, (node) => {\n\t\tif (node.type === NodeTypes.ELEMENT) {\n\t\t\tconst elementNode = node as ElementNode\n\t\t\tif (isRouterLinkComponent(elementNode.tag)) {\n\t\t\t\tconst nav = extractRouterLinkNavigation(elementNode, warnings)\n\t\t\t\tif (nav) {\n\t\t\t\t\tnavigations.push(nav)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t})\n\n\treturn navigations\n}\n\n/**\n * Check if a tag is a RouterLink component\n */\nfunction isRouterLinkComponent(tag: string): boolean {\n\treturn tag === \"RouterLink\" || tag === \"router-link\"\n}\n\n/**\n * Extract navigation from RouterLink component.\n *\n * @param node - The element node representing a RouterLink component\n * @param warnings - Array to collect warnings (mutated)\n * @returns Detected navigation or null if no valid path found\n */\nfunction extractRouterLinkNavigation(\n\tnode: ElementNode,\n\twarnings: string[],\n): DetectedNavigation | null {\n\tfor (const prop of node.props) {\n\t\t// Static attribute: to=\"/path\"\n\t\tif (prop.type === NodeTypes.ATTRIBUTE && prop.name === \"to\") {\n\t\t\tif (prop.value) {\n\t\t\t\tconst path = prop.value.content\n\t\t\t\tif (isValidInternalPath(path)) {\n\t\t\t\t\treturn createDetectedNavigation(path, \"link\", prop.loc.start.line)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Dynamic binding: :to=\"'/path'\" or v-bind:to=\"'/path'\"\n\t\tif (prop.type === NodeTypes.DIRECTIVE && prop.name === \"bind\") {\n\t\t\t// Check if this is binding the \"to\" prop\n\t\t\tif (\n\t\t\t\tprop.arg?.type === NodeTypes.SIMPLE_EXPRESSION &&\n\t\t\t\tprop.arg.content === \"to\"\n\t\t\t) {\n\t\t\t\treturn extractDynamicToBinding(prop, node.loc.start.line, warnings)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null\n}\n\n/**\n * Extract path from dynamic :to binding.\n *\n * Handles expressions like `:to=\"'/path'\"` or `v-bind:to=\"'/path'\"`.\n * Complex expressions that cannot be statically analyzed generate warnings.\n *\n * @param directive - The Vue directive AST node\n * @param line - Line number for warning messages\n * @param warnings - Array to collect warnings (mutated)\n * @returns Detected navigation or null if path cannot be extracted\n */\nfunction extractDynamicToBinding(\n\t// biome-ignore lint/suspicious/noExplicitAny: Vue compiler types are complex\n\tdirective: any,\n\tline: number,\n\twarnings: string[],\n): DetectedNavigation | null {\n\tconst exp = directive.exp\n\n\tif (!exp) {\n\t\twarnings.push(\n\t\t\t`Empty :to binding at line ${line}. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t\treturn null\n\t}\n\n\tif (exp.type === NodeTypes.SIMPLE_EXPRESSION) {\n\t\tconst content = exp.content.trim()\n\n\t\t// Check if it's a static string literal\n\t\tif (isStaticStringLiteral(content)) {\n\t\t\tconst path = extractStringValue(content)\n\t\t\tif (path && isValidInternalPath(path)) {\n\t\t\t\treturn createDetectedNavigation(path, \"link\", line)\n\t\t\t}\n\t\t}\n\n\t\t// Dynamic variable or complex expression\n\t\twarnings.push(\n\t\t\t`Dynamic :to binding at line ${line} cannot be statically analyzed. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t} else {\n\t\t// Complex expression type (COMPOUND_EXPRESSION, etc.)\n\t\twarnings.push(\n\t\t\t`Complex :to binding at line ${line} uses an unsupported expression type. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t}\n\n\treturn null\n}\n\n/**\n * Check if expression content is a static string literal.\n *\n * Recognizes single quotes, double quotes, and template literals without interpolation.\n *\n * @param content - The expression content (e.g., \"'/path'\" or \"`/home`\")\n * @returns true if the content is a statically analyzable string literal\n */\nfunction isStaticStringLiteral(content: string): boolean {\n\t// Single quotes: '/path'\n\tif (content.startsWith(\"'\") && content.endsWith(\"'\")) {\n\t\treturn true\n\t}\n\t// Double quotes: \"/path\"\n\tif (content.startsWith('\"') && content.endsWith('\"')) {\n\t\treturn true\n\t}\n\t// Template literal without interpolation: `/path`\n\tif (\n\t\tcontent.startsWith(\"`\") &&\n\t\tcontent.endsWith(\"`\") &&\n\t\t!content.includes(\"${\")\n\t) {\n\t\treturn true\n\t}\n\treturn false\n}\n\n/**\n * Extract string value from a quoted string\n */\nfunction extractStringValue(content: string): string {\n\treturn content.slice(1, -1)\n}\n\n/**\n * Walk template AST nodes recursively in pre-order traversal.\n *\n * Traverses element nodes, v-if branches, and v-for loop bodies.\n *\n * @param nodes - Array of template child nodes to traverse\n * @param callback - Function called for each node visited\n */\nfunction walkTemplateNodes(\n\tnodes: TemplateChildNode[],\n\tcallback: (node: TemplateChildNode) => void,\n): void {\n\tfor (const node of nodes) {\n\t\tcallback(node)\n\n\t\t// Element nodes have children\n\t\tif (node.type === NodeTypes.ELEMENT) {\n\t\t\tconst elementNode = node as ElementNode\n\t\t\tif (elementNode.children) {\n\t\t\t\twalkTemplateNodes(elementNode.children, callback)\n\t\t\t}\n\t\t}\n\n\t\t// IF nodes have branches with children\n\t\tif (node.type === NodeTypes.IF) {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: Vue compiler types are complex\n\t\t\tconst ifNode = node as any\n\t\t\tfor (const branch of ifNode.branches || []) {\n\t\t\t\tif (branch.children) {\n\t\t\t\t\twalkTemplateNodes(branch.children, callback)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// FOR nodes have children\n\t\tif (node.type === NodeTypes.FOR) {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: Vue compiler types are complex\n\t\t\tconst forNode = node as any\n\t\t\tif (forNode.children) {\n\t\t\t\twalkTemplateNodes(forNode.children, callback)\n\t\t\t}\n\t\t}\n\t}\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { dirname, join, relative, resolve } from \"node:path\"\nimport type { ApiIntegrationConfig, Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport prompts from \"prompts\"\nimport { glob } from \"tinyglobby\"\nimport { parseAngularRouterConfig } from \"../utils/angularRouterParser.js\"\nimport { analyzeAngularComponent } from \"../utils/angularTemplateAnalyzer.js\"\nimport { analyzeApiImports } from \"../utils/apiImportAnalyzer.js\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger, setVerbose } from \"../utils/logger.js\"\nimport {\n\tanalyzeNavigation,\n\tdetectNavigationFramework,\n} from \"../utils/navigationAnalyzer.js\"\nimport {\n\tdetectRouterType,\n\tparseReactRouterConfig,\n} from \"../utils/reactRouterParser.js\"\nimport {\n\ttype FlatRoute,\n\tflattenRoutes,\n\ttype ParseResult,\n} from \"../utils/routeParserUtils.js\"\nimport { parseSolidRouterConfig } from \"../utils/solidRouterParser.js\"\nimport { parseTanStackRouterConfig } from \"../utils/tanstackRouterParser.js\"\nimport { parseVueRouterConfig } from \"../utils/vueRouterParser.js\"\nimport { analyzeVueSFC } from \"../utils/vueSFCTemplateAnalyzer.js\"\n\nexport const generateCommand = define({\n\tname: \"generate\",\n\tdescription: \"Auto-generate screen.meta.ts files from route files\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tdryRun: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"n\",\n\t\t\tdescription: \"Show what would be generated without writing files\",\n\t\t\tdefault: false,\n\t\t},\n\t\tforce: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Overwrite existing screen.meta.ts files\",\n\t\t\tdefault: false,\n\t\t},\n\t\tinteractive: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"i\",\n\t\t\tdescription: \"Interactively confirm or modify each screen\",\n\t\t\tdefault: false,\n\t\t},\n\t\tdetectApi: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"a\",\n\t\t\tdescription:\n\t\t\t\t\"Detect API dependencies from imports (requires apiIntegration config)\",\n\t\t\tdefault: false,\n\t\t},\n\t\tdetectNavigation: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"N\",\n\t\t\tdescription:\n\t\t\t\t\"Detect navigation targets from code (Link, router.push, navigate)\",\n\t\t\tdefault: false,\n\t\t},\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output including stack traces\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tsetVerbose(ctx.values.verbose)\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst cwd = process.cwd()\n\t\tconst dryRun = ctx.values.dryRun ?? false\n\t\tconst force = ctx.values.force ?? false\n\t\tconst interactive = ctx.values.interactive ?? false\n\t\tconst detectApi = ctx.values.detectApi ?? false\n\t\tconst detectNavigation = ctx.values.detectNavigation ?? false\n\n\t\t// Check for routes configuration\n\t\tif (!config.routesPattern && !config.routesFile) {\n\t\t\tlogger.errorWithHelp(ERRORS.ROUTES_PATTERN_MISSING)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Validate --detect-api requires apiIntegration config\n\t\tif (detectApi && !config.apiIntegration) {\n\t\t\tlogger.error(\n\t\t\t\t`${logger.bold(\"--detect-api\")} requires ${logger.code(\"apiIntegration\")} configuration`,\n\t\t\t)\n\t\t\tlogger.blank()\n\t\t\tlogger.log(\"Add to your screenbook.config.ts:\")\n\t\t\tlogger.blank()\n\t\t\tlogger.log(\n\t\t\t\tlogger.dim(` export default defineConfig({\n apiIntegration: {\n clientPackages: [\"@/api/generated\"],\n },\n })`),\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Use routesFile mode (config-based routing)\n\t\tif (config.routesFile) {\n\t\t\tawait generateFromRoutesFile(config.routesFile, cwd, {\n\t\t\t\tdryRun,\n\t\t\t\tforce,\n\t\t\t\tinteractive,\n\t\t\t\tdetectApi,\n\t\t\t\tdetectNavigation,\n\t\t\t\tapiIntegration: config.apiIntegration,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\t// Use routesPattern mode (file-based routing)\n\t\tawait generateFromRoutesPattern(config.routesPattern as string, cwd, {\n\t\t\tdryRun,\n\t\t\tforce,\n\t\t\tinteractive,\n\t\t\tignore: config.ignore,\n\t\t\tdetectApi,\n\t\t\tdetectNavigation,\n\t\t\tapiIntegration: config.apiIntegration,\n\t\t})\n\t},\n})\n\ninterface GenerateFromRoutesFileOptions {\n\treadonly dryRun: boolean\n\treadonly force: boolean\n\treadonly interactive: boolean\n\treadonly detectApi: boolean\n\treadonly detectNavigation: boolean\n\treadonly apiIntegration?: ApiIntegrationConfig\n}\n\n/**\n * Generate screen.meta.ts files from a router config file (Vue Router or React Router)\n */\nasync function generateFromRoutesFile(\n\troutesFile: string,\n\tcwd: string,\n\toptions: GenerateFromRoutesFileOptions,\n): Promise<void> {\n\tconst {\n\t\tdryRun,\n\t\tforce,\n\t\tinteractive,\n\t\tdetectApi,\n\t\tdetectNavigation,\n\t\tapiIntegration,\n\t} = options\n\tconst absoluteRoutesFile = resolve(cwd, routesFile)\n\n\t// Check if routes file exists\n\tif (!existsSync(absoluteRoutesFile)) {\n\t\tlogger.errorWithHelp(ERRORS.ROUTES_FILE_NOT_FOUND(routesFile))\n\t\tprocess.exit(1)\n\t}\n\n\t// Detect router type\n\tconst content = readFileSync(absoluteRoutesFile, \"utf-8\")\n\tconst routerType = detectRouterType(content)\n\n\tconst routerTypeDisplay =\n\t\trouterType === \"tanstack-router\"\n\t\t\t? \"TanStack Router\"\n\t\t\t: routerType === \"solid-router\"\n\t\t\t\t? \"Solid Router\"\n\t\t\t\t: routerType === \"angular-router\"\n\t\t\t\t\t? \"Angular Router\"\n\t\t\t\t\t: routerType === \"react-router\"\n\t\t\t\t\t\t? \"React Router\"\n\t\t\t\t\t\t: routerType === \"vue-router\"\n\t\t\t\t\t\t\t? \"Vue Router\"\n\t\t\t\t\t\t\t: \"unknown\"\n\n\tlogger.info(\n\t\t`Parsing routes from ${logger.path(routesFile)} (${routerTypeDisplay})...`,\n\t)\n\tlogger.blank()\n\n\t// Parse the routes file with the appropriate parser\n\tlet parseResult: ParseResult\n\ttry {\n\t\tif (routerType === \"tanstack-router\") {\n\t\t\tparseResult = parseTanStackRouterConfig(absoluteRoutesFile)\n\t\t} else if (routerType === \"solid-router\") {\n\t\t\tparseResult = parseSolidRouterConfig(absoluteRoutesFile)\n\t\t} else if (routerType === \"angular-router\") {\n\t\t\tparseResult = parseAngularRouterConfig(absoluteRoutesFile)\n\t\t} else if (routerType === \"react-router\") {\n\t\t\tparseResult = parseReactRouterConfig(absoluteRoutesFile)\n\t\t} else if (routerType === \"vue-router\") {\n\t\t\tparseResult = parseVueRouterConfig(absoluteRoutesFile)\n\t\t} else {\n\t\t\t// Unknown router type - warn user and attempt Vue Router parser as fallback\n\t\t\tlogger.warn(\n\t\t\t\t`Could not auto-detect router type for ${logger.path(routesFile)}. Attempting to parse as Vue Router.`,\n\t\t\t)\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"If parsing fails, check that your router imports are explicit.\")}`,\n\t\t\t)\n\t\t\tparseResult = parseVueRouterConfig(absoluteRoutesFile)\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tlogger.errorWithHelp(ERRORS.ROUTES_FILE_PARSE_ERROR(routesFile, message))\n\t\tprocess.exit(1)\n\t}\n\n\t// Show warnings\n\tfor (const warning of parseResult.warnings) {\n\t\tlogger.warn(warning)\n\t}\n\n\t// Flatten routes\n\tconst flatRoutes = flattenRoutes(parseResult.routes)\n\n\tif (flatRoutes.length === 0) {\n\t\tlogger.warn(\"No routes found in the config file\")\n\t\treturn\n\t}\n\n\tlogger.log(`Found ${flatRoutes.length} routes`)\n\tlogger.blank()\n\n\tlet created = 0\n\tlet skipped = 0\n\n\tfor (const route of flatRoutes) {\n\t\t// Determine where to place screen.meta.ts\n\t\tconst metaPath = determineMetaPath(route, cwd)\n\t\tconst absoluteMetaPath = resolve(cwd, metaPath)\n\n\t\tif (!force && existsSync(absoluteMetaPath)) {\n\t\t\tif (!interactive) {\n\t\t\t\tskipped++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlogger.itemWarn(\n\t\t\t\t`Exists: ${logger.path(metaPath)} (use --force to overwrite)`,\n\t\t\t)\n\t\t\tskipped++\n\t\t\tcontinue\n\t\t}\n\n\t\tconst screenMeta: InferredScreenMeta = {\n\t\t\tid: route.screenId,\n\t\t\ttitle: route.screenTitle,\n\t\t\troute: route.fullPath,\n\t\t}\n\n\t\t// Detect API dependencies if enabled\n\t\tlet detectedApis: string[] = []\n\t\tif (detectApi && apiIntegration && route.componentPath) {\n\t\t\tconst componentAbsPath = resolve(cwd, route.componentPath)\n\t\t\tif (existsSync(componentAbsPath)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst componentContent = readFileSync(componentAbsPath, \"utf-8\")\n\t\t\t\t\tconst result = analyzeApiImports(componentContent, apiIntegration)\n\t\t\t\t\tdetectedApis = result.imports.map((i) => i.dependsOnName)\n\t\t\t\t\tfor (const warning of result.warnings) {\n\t\t\t\t\t\tlogger.warn(`${logger.path(route.componentPath)}: ${warning}`)\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`${logger.path(route.componentPath)}: Could not analyze for API imports: ${message}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Detect navigation targets if enabled\n\t\tlet detectedNext: string[] = []\n\t\tif (detectNavigation && route.componentPath) {\n\t\t\tconst componentAbsPath = resolve(cwd, route.componentPath)\n\t\t\tif (existsSync(componentAbsPath)) {\n\t\t\t\tlet componentContent: string\n\t\t\t\ttry {\n\t\t\t\t\tcomponentContent = readFileSync(componentAbsPath, \"utf-8\")\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`${logger.path(route.componentPath)}: Could not read file for navigation analysis: ${message}`,\n\t\t\t\t\t)\n\t\t\t\t\tcomponentContent = \"\"\n\t\t\t\t}\n\n\t\t\t\tif (componentContent) {\n\t\t\t\t\t// Use Vue SFC analyzer for .vue files to detect RouterLink in templates\n\t\t\t\t\tif (route.componentPath.endsWith(\".vue\")) {\n\t\t\t\t\t\tconst result = analyzeVueSFC(componentContent, route.componentPath)\n\t\t\t\t\t\tdetectedNext = [\n\t\t\t\t\t\t\t...result.templateNavigations.map((n) => n.screenId),\n\t\t\t\t\t\t\t...result.scriptNavigations.map((n) => n.screenId),\n\t\t\t\t\t\t]\n\t\t\t\t\t\tfor (const warning of result.warnings) {\n\t\t\t\t\t\t\tlogger.warn(`${logger.path(route.componentPath)}: ${warning}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (route.componentPath.endsWith(\".component.ts\")) {\n\t\t\t\t\t\t// Use Angular component analyzer for .component.ts files\n\t\t\t\t\t\tconst result = analyzeAngularComponent(\n\t\t\t\t\t\t\tcomponentContent,\n\t\t\t\t\t\t\troute.componentPath,\n\t\t\t\t\t\t\tcwd,\n\t\t\t\t\t\t)\n\t\t\t\t\t\tdetectedNext = [\n\t\t\t\t\t\t\t...result.templateNavigations.map((n) => n.screenId),\n\t\t\t\t\t\t\t...result.scriptNavigations.map((n) => n.screenId),\n\t\t\t\t\t\t]\n\t\t\t\t\t\tfor (const warning of result.warnings) {\n\t\t\t\t\t\t\tlogger.warn(`${logger.path(route.componentPath)}: ${warning}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Use standard analyzer for non-Vue files\n\t\t\t\t\t\tconst { framework, detected } =\n\t\t\t\t\t\t\tdetectNavigationFramework(componentContent)\n\t\t\t\t\t\tif (!detected) {\n\t\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t\t`${logger.path(route.componentPath)}: Could not detect navigation framework, defaulting to Next.js patterns. Navigation detection may be incomplete.`,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst result = analyzeNavigation(componentContent, framework)\n\t\t\t\t\t\tdetectedNext = result.navigations.map((n) => n.screenId)\n\t\t\t\t\t\tfor (const warning of result.warnings) {\n\t\t\t\t\t\t\tlogger.warn(`${logger.path(route.componentPath)}: ${warning}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (interactive) {\n\t\t\tconst result = await promptForScreen(route.fullPath, screenMeta)\n\n\t\t\tif (result.skip) {\n\t\t\t\tlogger.itemWarn(`Skipped: ${logger.path(metaPath)}`)\n\t\t\t\tskipped++\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst content = generateScreenMetaContent(result.meta, {\n\t\t\t\towner: result.owner,\n\t\t\t\ttags: result.tags,\n\t\t\t\tdependsOn: detectedApis,\n\t\t\t\tnext: detectedNext,\n\t\t\t})\n\n\t\t\tif (dryRun) {\n\t\t\t\tlogDryRunOutput(\n\t\t\t\t\tmetaPath,\n\t\t\t\t\tresult.meta,\n\t\t\t\t\tresult.owner,\n\t\t\t\t\tresult.tags,\n\t\t\t\t\tdetectedApis,\n\t\t\t\t\tdetectedNext,\n\t\t\t\t)\n\t\t\t\tcreated++\n\t\t\t} else if (safeWriteFile(absoluteMetaPath, metaPath, content)) {\n\t\t\t\tcreated++\n\t\t\t}\n\t\t} else {\n\t\t\tconst content = generateScreenMetaContent(screenMeta, {\n\t\t\t\tdependsOn: detectedApis,\n\t\t\t\tnext: detectedNext,\n\t\t\t})\n\n\t\t\tif (dryRun) {\n\t\t\t\tlogDryRunOutput(\n\t\t\t\t\tmetaPath,\n\t\t\t\t\tscreenMeta,\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\tdetectedApis,\n\t\t\t\t\tdetectedNext,\n\t\t\t\t)\n\t\t\t\tcreated++\n\t\t\t} else if (safeWriteFile(absoluteMetaPath, metaPath, content)) {\n\t\t\t\tcreated++\n\t\t\t}\n\t\t}\n\t}\n\n\tlogSummary(created, skipped, dryRun)\n}\n\nexport interface GenerateFromRoutesPatternOptions {\n\treadonly dryRun: boolean\n\treadonly force: boolean\n\treadonly interactive: boolean\n\treadonly ignore: readonly string[]\n\treadonly detectApi: boolean\n\treadonly detectNavigation: boolean\n\treadonly apiIntegration?: ApiIntegrationConfig\n}\n\n/**\n * Generate screen.meta.ts files from route files matching a glob pattern\n */\nexport async function generateFromRoutesPattern(\n\troutesPattern: string,\n\tcwd: string,\n\toptions: GenerateFromRoutesPatternOptions,\n): Promise<void> {\n\tconst {\n\t\tdryRun,\n\t\tforce,\n\t\tinteractive,\n\t\tignore,\n\t\tdetectApi,\n\t\tdetectNavigation,\n\t\tapiIntegration,\n\t} = options\n\n\tlogger.info(\"Scanning for route files...\")\n\tlogger.blank()\n\n\t// Find all route files\n\tconst routeFiles = await glob(routesPattern, {\n\t\tcwd,\n\t\tignore,\n\t})\n\n\tif (routeFiles.length === 0) {\n\t\tlogger.warn(`No route files found matching: ${routesPattern}`)\n\t\treturn\n\t}\n\n\tlogger.log(`Found ${routeFiles.length} route files`)\n\tlogger.blank()\n\n\tlet created = 0\n\tlet skipped = 0\n\n\tfor (const routeFile of routeFiles) {\n\t\tconst routeDir = dirname(routeFile)\n\t\tconst metaPath = join(routeDir, \"screen.meta.ts\")\n\t\tconst absoluteMetaPath = join(cwd, metaPath)\n\n\t\tif (!force && existsSync(absoluteMetaPath)) {\n\t\t\tif (!interactive) {\n\t\t\t\tskipped++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlogger.itemWarn(\n\t\t\t\t`Exists: ${logger.path(metaPath)} (use --force to overwrite)`,\n\t\t\t)\n\t\t\tskipped++\n\t\t\tcontinue\n\t\t}\n\n\t\t// Generate screen metadata from path\n\t\tconst screenMeta = inferScreenMeta(routeDir, routesPattern)\n\n\t\t// Detect API dependencies if enabled\n\t\tlet detectedApis: string[] = []\n\t\tif (detectApi && apiIntegration) {\n\t\t\tconst absoluteRouteFile = join(cwd, routeFile)\n\t\t\tif (existsSync(absoluteRouteFile)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst routeContent = readFileSync(absoluteRouteFile, \"utf-8\")\n\t\t\t\t\tconst result = analyzeApiImports(routeContent, apiIntegration)\n\t\t\t\t\tdetectedApis = result.imports.map((i) => i.dependsOnName)\n\t\t\t\t\tfor (const warning of result.warnings) {\n\t\t\t\t\t\tlogger.warn(`${logger.path(routeFile)}: ${warning}`)\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`${logger.path(routeFile)}: Could not analyze for API imports: ${message}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Detect navigation targets if enabled\n\t\tlet detectedNext: string[] = []\n\t\tif (detectNavigation) {\n\t\t\tconst absoluteRouteFile = join(cwd, routeFile)\n\t\t\tif (existsSync(absoluteRouteFile)) {\n\t\t\t\tlet routeContent: string\n\t\t\t\ttry {\n\t\t\t\t\trouteContent = readFileSync(absoluteRouteFile, \"utf-8\")\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`${logger.path(routeFile)}: Could not read file for navigation analysis: ${message}`,\n\t\t\t\t\t)\n\t\t\t\t\trouteContent = \"\"\n\t\t\t\t}\n\n\t\t\t\tif (routeContent) {\n\t\t\t\t\tconst { framework, detected } =\n\t\t\t\t\t\tdetectNavigationFramework(routeContent)\n\t\t\t\t\tif (!detected) {\n\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t`${logger.path(routeFile)}: Could not detect navigation framework, defaulting to Next.js patterns. Navigation detection may be incomplete.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tconst result = analyzeNavigation(routeContent, framework)\n\t\t\t\t\tdetectedNext = result.navigations.map((n) => n.screenId)\n\t\t\t\t\tfor (const warning of result.warnings) {\n\t\t\t\t\t\tlogger.warn(`${logger.path(routeFile)}: ${warning}`)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (interactive) {\n\t\t\tconst result = await promptForScreen(routeFile, screenMeta)\n\n\t\t\tif (result.skip) {\n\t\t\t\tlogger.itemWarn(`Skipped: ${logger.path(metaPath)}`)\n\t\t\t\tskipped++\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst content = generateScreenMetaContent(result.meta, {\n\t\t\t\towner: result.owner,\n\t\t\t\ttags: result.tags,\n\t\t\t\tdependsOn: detectedApis,\n\t\t\t\tnext: detectedNext,\n\t\t\t})\n\n\t\t\tif (dryRun) {\n\t\t\t\tlogDryRunOutput(\n\t\t\t\t\tmetaPath,\n\t\t\t\t\tresult.meta,\n\t\t\t\t\tresult.owner,\n\t\t\t\t\tresult.tags,\n\t\t\t\t\tdetectedApis,\n\t\t\t\t\tdetectedNext,\n\t\t\t\t)\n\t\t\t\tcreated++\n\t\t\t} else if (safeWriteFile(absoluteMetaPath, metaPath, content)) {\n\t\t\t\tcreated++\n\t\t\t}\n\t\t} else {\n\t\t\tconst content = generateScreenMetaContent(screenMeta, {\n\t\t\t\tdependsOn: detectedApis,\n\t\t\t\tnext: detectedNext,\n\t\t\t})\n\n\t\t\tif (dryRun) {\n\t\t\t\tlogDryRunOutput(\n\t\t\t\t\tmetaPath,\n\t\t\t\t\tscreenMeta,\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\tdetectedApis,\n\t\t\t\t\tdetectedNext,\n\t\t\t\t)\n\t\t\t\tcreated++\n\t\t\t} else if (safeWriteFile(absoluteMetaPath, metaPath, content)) {\n\t\t\t\tcreated++\n\t\t\t}\n\t\t}\n\t}\n\n\tlogSummary(created, skipped, dryRun)\n}\n\n/**\n * Determine where to place screen.meta.ts for a route\n */\nfunction determineMetaPath(route: FlatRoute, cwd: string): string {\n\t// If component path is available, place screen.meta.ts next to it\n\tif (route.componentPath) {\n\t\tconst componentDir = dirname(route.componentPath)\n\t\tconst relativePath = relative(cwd, componentDir)\n\t\t// Ensure path doesn't escape cwd\n\t\tif (!relativePath.startsWith(\"..\")) {\n\t\t\treturn join(relativePath, \"screen.meta.ts\")\n\t\t}\n\t}\n\n\t// Fall back to src/screens/{screenId}/screen.meta.ts\n\tconst screenDir = route.screenId.replace(/\\./g, \"/\")\n\treturn join(\"src\", \"screens\", screenDir, \"screen.meta.ts\")\n}\n\n/**\n * Ensure the directory for a file exists\n */\nfunction ensureDirectoryExists(filePath: string): void {\n\tconst dir = dirname(filePath)\n\tif (!existsSync(dir)) {\n\t\ttry {\n\t\t\tmkdirSync(dir, { recursive: true })\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(`Failed to create directory \"${dir}\": ${message}`)\n\t\t}\n\t}\n}\n\n/**\n * Safely write a file with error handling\n * Returns true if successful, false if failed\n */\nfunction safeWriteFile(\n\tabsolutePath: string,\n\trelativePath: string,\n\tcontent: string,\n): boolean {\n\ttry {\n\t\tensureDirectoryExists(absolutePath)\n\t\twriteFileSync(absolutePath, content)\n\t\tlogger.itemSuccess(`Created: ${logger.path(relativePath)}`)\n\t\treturn true\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tlogger.itemError(\n\t\t\t`Failed to create ${logger.path(relativePath)}: ${message}`,\n\t\t)\n\t\treturn false\n\t}\n}\n\n/**\n * Log dry run output for a screen\n */\nfunction logDryRunOutput(\n\tmetaPath: string,\n\tmeta: InferredScreenMeta,\n\towner?: string[],\n\ttags?: string[],\n\tdependsOn?: string[],\n\tnext?: string[],\n): void {\n\tlogger.step(`Would create: ${logger.path(metaPath)}`)\n\tlogger.log(` ${logger.dim(`id: \"${meta.id}\"`)}`)\n\tlogger.log(` ${logger.dim(`title: \"${meta.title}\"`)}`)\n\tlogger.log(` ${logger.dim(`route: \"${meta.route}\"`)}`)\n\tif (owner && owner.length > 0) {\n\t\tlogger.log(\n\t\t\t` ${logger.dim(`owner: [${owner.map((o) => `\"${o}\"`).join(\", \")}]`)}`,\n\t\t)\n\t}\n\tif (tags && tags.length > 0) {\n\t\tlogger.log(\n\t\t\t` ${logger.dim(`tags: [${tags.map((t) => `\"${t}\"`).join(\", \")}]`)}`,\n\t\t)\n\t}\n\tif (dependsOn && dependsOn.length > 0) {\n\t\tlogger.log(\n\t\t\t` ${logger.dim(`dependsOn: [${dependsOn.map((d) => `\"${d}\"`).join(\", \")}]`)}`,\n\t\t)\n\t}\n\tif (next && next.length > 0) {\n\t\tlogger.log(\n\t\t\t` ${logger.dim(`next: [${next.map((n) => `\"${n}\"`).join(\", \")}]`)}`,\n\t\t)\n\t}\n\tlogger.blank()\n}\n\n/**\n * Log summary after generation\n */\nfunction logSummary(created: number, skipped: number, dryRun: boolean): void {\n\tlogger.blank()\n\tif (dryRun) {\n\t\tlogger.info(`Would create ${created} files (${skipped} already exist)`)\n\t\tlogger.blank()\n\t\tlogger.log(`Run without ${logger.code(\"--dry-run\")} to create files`)\n\t} else {\n\t\tlogger.done(`Created ${created} files (${skipped} skipped)`)\n\t\tif (created > 0) {\n\t\t\tlogger.blank()\n\t\t\tlogger.log(logger.bold(\"Next steps:\"))\n\t\t\tlogger.log(\" 1. Review and customize the generated screen.meta.ts files\")\n\t\t\tlogger.log(\n\t\t\t\t` 2. Run ${logger.code(\"screenbook dev\")} to view your screen catalog`,\n\t\t\t)\n\t\t}\n\t}\n}\n\n/**\n * Subset of Screen containing only auto-inferred fields.\n * Uses Pick to ensure type alignment with the core Screen type.\n */\ntype InferredScreenMeta = Pick<Screen, \"id\" | \"title\" | \"route\">\n\ninterface InteractiveResult {\n\tskip: boolean\n\tmeta: InferredScreenMeta\n\towner: string[]\n\ttags: string[]\n}\n\n/**\n * Parse comma-separated string into array\n */\nexport function parseCommaSeparated(input: string): string[] {\n\tif (!input.trim()) return []\n\treturn input\n\t\t.split(\",\")\n\t\t.map((s) => s.trim())\n\t\t.filter(Boolean)\n}\n\n/**\n * Prompt user for screen metadata in interactive mode\n */\nasync function promptForScreen(\n\trouteFile: string,\n\tinferred: InferredScreenMeta,\n): Promise<InteractiveResult> {\n\tlogger.blank()\n\tlogger.info(`Found: ${logger.path(routeFile)}`)\n\tlogger.blank()\n\tlogger.log(\n\t\t` ${logger.dim(\"ID:\")} ${inferred.id} ${logger.dim(\"(inferred)\")}`,\n\t)\n\tlogger.log(\n\t\t` ${logger.dim(\"Title:\")} ${inferred.title} ${logger.dim(\"(inferred)\")}`,\n\t)\n\tlogger.log(\n\t\t` ${logger.dim(\"Route:\")} ${inferred.route} ${logger.dim(\"(inferred)\")}`,\n\t)\n\tlogger.blank()\n\n\tconst response = await prompts([\n\t\t{\n\t\t\ttype: \"confirm\",\n\t\t\tname: \"proceed\",\n\t\t\tmessage: \"Generate this screen?\",\n\t\t\tinitial: true,\n\t\t},\n\t\t{\n\t\t\ttype: (prev) => (prev ? \"text\" : null),\n\t\t\tname: \"id\",\n\t\t\tmessage: \"ID\",\n\t\t\tinitial: inferred.id,\n\t\t},\n\t\t{\n\t\t\ttype: (_prev, values) => (values.proceed ? \"text\" : null),\n\t\t\tname: \"title\",\n\t\t\tmessage: \"Title\",\n\t\t\tinitial: inferred.title,\n\t\t},\n\t\t{\n\t\t\ttype: (_prev, values) => (values.proceed ? \"text\" : null),\n\t\t\tname: \"owner\",\n\t\t\tmessage: \"Owner (comma-separated)\",\n\t\t\tinitial: \"\",\n\t\t},\n\t\t{\n\t\t\ttype: (_prev, values) => (values.proceed ? \"text\" : null),\n\t\t\tname: \"tags\",\n\t\t\tmessage: \"Tags (comma-separated)\",\n\t\t\tinitial: inferred.id.split(\".\")[0] || \"\",\n\t\t},\n\t])\n\n\tif (!response.proceed) {\n\t\treturn { skip: true, meta: inferred, owner: [], tags: [] }\n\t}\n\n\treturn {\n\t\tskip: false,\n\t\tmeta: {\n\t\t\tid: response.id || inferred.id,\n\t\t\ttitle: response.title || inferred.title,\n\t\t\troute: inferred.route,\n\t\t},\n\t\towner: parseCommaSeparated(response.owner || \"\"),\n\t\ttags: parseCommaSeparated(response.tags || \"\"),\n\t}\n}\n\n/**\n * Infer screen metadata from the route file path\n */\nfunction inferScreenMeta(\n\trouteDir: string,\n\troutesPattern: string,\n): InferredScreenMeta {\n\t// Extract base directory from pattern (e.g., \"src/pages\" from \"src/pages/**/page.tsx\")\n\tconst patternBase = routesPattern.split(\"*\")[0]?.replace(/\\/$/, \"\") ?? \"\"\n\n\t// Get relative path from pattern base\n\tconst relativePath = relative(patternBase, routeDir)\n\n\t// Handle root route\n\tif (!relativePath || relativePath === \".\") {\n\t\treturn {\n\t\t\tid: \"home\",\n\t\t\ttitle: \"Home\",\n\t\t\troute: \"/\",\n\t\t}\n\t}\n\n\t// Clean up path segments (remove route groups like (marketing), handle dynamic segments)\n\tconst segments = relativePath\n\t\t.split(\"/\")\n\t\t.filter((s) => s && !s.startsWith(\"(\") && !s.endsWith(\")\"))\n\t\t.map((s) =>\n\t\t\ts.replace(/^\\[\\.\\.\\..*\\]$/, \"catchall\").replace(/^\\[(.+)\\]$/, \"$1\"),\n\t\t)\n\n\t// Generate ID from segments (e.g., \"billing.invoice.detail\")\n\tconst id = segments.join(\".\")\n\n\t// Generate title from last segment (e.g., \"Invoice Detail\" from \"invoice-detail\")\n\tconst lastSegment = segments[segments.length - 1] || \"home\"\n\tconst title = lastSegment\n\t\t.split(/[-_]/)\n\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n\t\t.join(\" \")\n\n\t// Generate route from path (e.g., \"/billing/invoice/:id\")\n\tconst routeSegments = relativePath\n\t\t.split(\"/\")\n\t\t.filter((s) => s && !s.startsWith(\"(\") && !s.endsWith(\")\"))\n\t\t.map((s) => {\n\t\t\t// Convert [id] to :id, [...slug] to *\n\t\t\tif (s.startsWith(\"[...\") && s.endsWith(\"]\")) {\n\t\t\t\treturn \"*\"\n\t\t\t}\n\t\t\tif (s.startsWith(\"[\") && s.endsWith(\"]\")) {\n\t\t\t\treturn `:${s.slice(1, -1)}`\n\t\t\t}\n\t\t\treturn s\n\t\t})\n\tconst route = `/${routeSegments.join(\"/\")}`\n\n\treturn { id, title, route }\n}\n\ntype GenerateOptions = Partial<\n\tPick<Screen, \"owner\" | \"tags\" | \"dependsOn\" | \"next\">\n>\n\n/**\n * Generate screen.meta.ts file content\n */\nfunction generateScreenMetaContent(\n\tmeta: InferredScreenMeta,\n\toptions?: GenerateOptions,\n): string {\n\t// Use provided values or infer defaults\n\tconst owner = options?.owner ?? []\n\tconst tags =\n\t\toptions?.tags && options.tags.length > 0\n\t\t\t? options.tags\n\t\t\t: [meta.id.split(\".\")[0] || \"general\"]\n\tconst dependsOn = options?.dependsOn ?? []\n\tconst next = options?.next ?? []\n\n\tconst ownerStr =\n\t\towner.length > 0 ? `[${owner.map((o) => `\"${o}\"`).join(\", \")}]` : \"[]\"\n\tconst tagsStr = `[${tags.map((t) => `\"${t}\"`).join(\", \")}]`\n\tconst dependsOnStr =\n\t\tdependsOn.length > 0\n\t\t\t? `[${dependsOn.map((d) => `\"${d}\"`).join(\", \")}]`\n\t\t\t: \"[]\"\n\tconst nextStr =\n\t\tnext.length > 0 ? `[${next.map((n) => `\"${n}\"`).join(\", \")}]` : \"[]\"\n\n\t// Generate dependsOn comment based on whether APIs were detected\n\tconst dependsOnComment =\n\t\tdependsOn.length > 0\n\t\t\t? \"// Auto-detected API dependencies (add more as needed)\"\n\t\t\t: `// APIs/services this screen depends on (for impact analysis)\n\t// Example: [\"UserAPI.getProfile\", \"PaymentService.checkout\"]`\n\n\t// Generate next comment based on whether navigation was detected\n\tconst nextComment =\n\t\tnext.length > 0\n\t\t\t? \"// Auto-detected navigation targets (add more as needed)\"\n\t\t\t: \"// Screen IDs this screen can navigate to\"\n\n\treturn `import { defineScreen } from \"@screenbook/core\"\n\nexport const screen = defineScreen({\n\tid: \"${meta.id}\",\n\ttitle: \"${meta.title}\",\n\troute: \"${meta.route}\",\n\n\t// Team or individual responsible for this screen\n\towner: ${ownerStr},\n\n\t// Tags for filtering in the catalog\n\ttags: ${tagsStr},\n\n\t${dependsOnComment}\n\tdependsOn: ${dependsOnStr},\n\n\t// Screen IDs that can navigate to this screen\n\tentryPoints: [],\n\n\t${nextComment}\n\tnext: ${nextStr},\n})\n`\n}\n","import type { Screen } from \"@screenbook/core\"\n\nexport interface TransitiveDependency {\n\tscreen: Screen\n\tpath: string[]\n}\n\nexport interface ImpactResult {\n\tapi: string\n\tdirect: Screen[]\n\ttransitive: TransitiveDependency[]\n\ttotalCount: number\n}\n\n/**\n * Check if a screen's dependsOn matches the API name (supports partial matching).\n * - \"InvoiceAPI\" matches \"InvoiceAPI.getDetail\"\n * - \"InvoiceAPI.getDetail\" matches \"InvoiceAPI.getDetail\"\n */\nfunction matchesDependency(dependency: string, apiName: string): boolean {\n\t// Exact match\n\tif (dependency === apiName) {\n\t\treturn true\n\t}\n\t// Partial match: apiName is a prefix of dependency\n\tif (dependency.startsWith(`${apiName}.`)) {\n\t\treturn true\n\t}\n\t// Partial match: dependency is a prefix of apiName\n\tif (apiName.startsWith(`${dependency}.`)) {\n\t\treturn true\n\t}\n\treturn false\n}\n\n/**\n * Find screens that directly depend on the given API.\n */\nfunction findDirectDependents(screens: Screen[], apiName: string): Screen[] {\n\treturn screens.filter((screen) =>\n\t\tscreen.dependsOn?.some((dep) => matchesDependency(dep, apiName)),\n\t)\n}\n\n/**\n * Build a reverse navigation graph: screenId -> screens that can navigate to it.\n * This is built from the `entryPoints` field.\n */\nfunction _buildReverseNavigationGraph(\n\tscreens: Screen[],\n): Map<string, Set<string>> {\n\tconst graph = new Map<string, Set<string>>()\n\n\tfor (const screen of screens) {\n\t\t// entryPoints lists screens that can navigate TO this screen\n\t\t// So we want to find screens that navigate FROM here\n\t\t// Actually, we need to reverse: if A.entryPoints includes B,\n\t\t// then B can navigate to A, so B is affected if A is affected\n\n\t\t// For transitive analysis:\n\t\t// If screen A depends on API, and screen B has entryPoints: [\"A\"],\n\t\t// then B can navigate to A, meaning users can reach A from B\n\t\t// So if A is impacted, we should show B as transitively impacted\n\n\t\tif (!screen.entryPoints) continue\n\n\t\tfor (const entryPoint of screen.entryPoints) {\n\t\t\tif (!graph.has(screen.id)) {\n\t\t\t\tgraph.set(screen.id, new Set())\n\t\t\t}\n\t\t\tgraph.get(screen.id)?.add(entryPoint)\n\t\t}\n\t}\n\n\treturn graph\n}\n\n/**\n * Build a navigation graph from `next` field: screenId -> screens it can navigate to.\n */\nfunction buildNavigationGraph(screens: Screen[]): Map<string, Set<string>> {\n\tconst graph = new Map<string, Set<string>>()\n\n\tfor (const screen of screens) {\n\t\tif (!screen.next) continue\n\n\t\tif (!graph.has(screen.id)) {\n\t\t\tgraph.set(screen.id, new Set())\n\t\t}\n\t\tfor (const nextId of screen.next) {\n\t\t\tgraph.get(screen.id)?.add(nextId)\n\t\t}\n\t}\n\n\treturn graph\n}\n\n/**\n * Find all transitive dependents using BFS.\n * A screen is transitively dependent if it can navigate to a directly dependent screen.\n */\nfunction findTransitiveDependents(\n\tscreens: Screen[],\n\tdirectDependentIds: Set<string>,\n\tmaxDepth: number,\n): TransitiveDependency[] {\n\tconst _screenMap = new Map(screens.map((s) => [s.id, s]))\n\tconst navigationGraph = buildNavigationGraph(screens)\n\tconst transitive: TransitiveDependency[] = []\n\tconst visited = new Set<string>()\n\n\t// For each screen, check if it can reach a directly dependent screen\n\tfor (const screen of screens) {\n\t\tif (directDependentIds.has(screen.id)) {\n\t\t\tcontinue // Skip direct dependents\n\t\t}\n\n\t\tconst path = findPathToDirectDependent(\n\t\t\tscreen.id,\n\t\t\tdirectDependentIds,\n\t\t\tnavigationGraph,\n\t\t\tmaxDepth,\n\t\t\tnew Set(),\n\t\t)\n\n\t\tif (path && !visited.has(screen.id)) {\n\t\t\tvisited.add(screen.id)\n\t\t\ttransitive.push({\n\t\t\t\tscreen,\n\t\t\t\tpath,\n\t\t\t})\n\t\t}\n\t}\n\n\treturn transitive\n}\n\n/**\n * Find a path from a screen to any directly dependent screen using BFS.\n */\nfunction findPathToDirectDependent(\n\tstartId: string,\n\ttargetIds: Set<string>,\n\tgraph: Map<string, Set<string>>,\n\tmaxDepth: number,\n\tvisited: Set<string>,\n): string[] | null {\n\tif (visited.has(startId)) {\n\t\treturn null\n\t}\n\n\tconst queue: Array<{ id: string; path: string[] }> = [\n\t\t{ id: startId, path: [startId] },\n\t]\n\tconst localVisited = new Set<string>([startId])\n\n\twhile (queue.length > 0) {\n\t\tconst current = queue.shift()\n\t\tif (!current) break\n\n\t\tif (current.path.length > maxDepth + 1) {\n\t\t\tcontinue\n\t\t}\n\n\t\tconst neighbors = graph.get(current.id)\n\t\tif (!neighbors) {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor (const neighborId of neighbors) {\n\t\t\tif (localVisited.has(neighborId)) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst newPath = [...current.path, neighborId]\n\n\t\t\t// Check if path exceeds maxDepth (path includes start, so maxDepth+1 is the max length)\n\t\t\tif (newPath.length > maxDepth + 1) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (targetIds.has(neighborId)) {\n\t\t\t\treturn newPath\n\t\t\t}\n\n\t\t\tlocalVisited.add(neighborId)\n\t\t\tqueue.push({ id: neighborId, path: newPath })\n\t\t}\n\t}\n\n\treturn null\n}\n\n/**\n * Analyze the impact of a change to an API on the screen catalog.\n */\nexport function analyzeImpact(\n\tscreens: Screen[],\n\tapiName: string,\n\tmaxDepth = 3,\n): ImpactResult {\n\t// Find direct dependents\n\tconst direct = findDirectDependents(screens, apiName)\n\tconst directIds = new Set(direct.map((s) => s.id))\n\n\t// Find transitive dependents\n\tconst transitive = findTransitiveDependents(screens, directIds, maxDepth)\n\n\treturn {\n\t\tapi: apiName,\n\t\tdirect,\n\t\ttransitive,\n\t\ttotalCount: direct.length + transitive.length,\n\t}\n}\n\n/**\n * Format the impact result as text output.\n */\nexport function formatImpactText(result: ImpactResult): string {\n\tconst lines: string[] = []\n\n\tlines.push(`Impact Analysis: ${result.api}`)\n\tlines.push(\"\")\n\n\tif (result.direct.length > 0) {\n\t\tlines.push(\n\t\t\t`Direct (${result.direct.length} screen${result.direct.length > 1 ? \"s\" : \"\"}):`,\n\t\t)\n\t\tfor (const screen of result.direct) {\n\t\t\tconst owner = screen.owner?.length ? ` [${screen.owner.join(\", \")}]` : \"\"\n\t\t\tlines.push(` - ${screen.id} ${screen.route}${owner}`)\n\t\t}\n\t\tlines.push(\"\")\n\t}\n\n\tif (result.transitive.length > 0) {\n\t\tlines.push(\n\t\t\t`Transitive (${result.transitive.length} screen${result.transitive.length > 1 ? \"s\" : \"\"}):`,\n\t\t)\n\t\tfor (const { path } of result.transitive) {\n\t\t\tlines.push(` - ${path.join(\" -> \")}`)\n\t\t}\n\t\tlines.push(\"\")\n\t}\n\n\tif (result.totalCount === 0) {\n\t\tlines.push(\"No screens depend on this API.\")\n\t\tlines.push(\"\")\n\t} else {\n\t\tlines.push(\n\t\t\t`Total: ${result.totalCount} screen${result.totalCount > 1 ? \"s\" : \"\"} affected`,\n\t\t)\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n\n/**\n * Format the impact result as JSON output.\n */\nexport function formatImpactJson(result: ImpactResult): string {\n\treturn JSON.stringify(\n\t\t{\n\t\t\tapi: result.api,\n\t\t\tsummary: {\n\t\t\t\tdirectCount: result.direct.length,\n\t\t\t\ttransitiveCount: result.transitive.length,\n\t\t\t\ttotalCount: result.totalCount,\n\t\t\t},\n\t\t\tdirect: result.direct.map((s) => ({\n\t\t\t\tid: s.id,\n\t\t\t\ttitle: s.title,\n\t\t\t\troute: s.route,\n\t\t\t\towner: s.owner,\n\t\t\t})),\n\t\t\ttransitive: result.transitive.map(({ screen, path }) => ({\n\t\t\t\tid: screen.id,\n\t\t\t\ttitle: screen.title,\n\t\t\t\troute: screen.route,\n\t\t\t\tpath,\n\t\t\t})),\n\t\t},\n\t\tnull,\n\t\t2,\n\t)\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport {\n\tanalyzeImpact,\n\tformatImpactJson,\n\tformatImpactText,\n} from \"../utils/impactAnalysis.js\"\nimport { logger, setVerbose } from \"../utils/logger.js\"\n\nexport const impactCommand = define({\n\tname: \"impact\",\n\tdescription: \"Analyze which screens depend on a specific API/service\",\n\targs: {\n\t\tapi: {\n\t\t\ttype: \"positional\",\n\t\t\tdescription:\n\t\t\t\t\"API or service name to analyze (e.g., InvoiceAPI.getDetail)\",\n\t\t\trequired: true,\n\t\t},\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tformat: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Output format: text (default) or json\",\n\t\t\tdefault: \"text\",\n\t\t},\n\t\tdepth: {\n\t\t\ttype: \"number\",\n\t\t\tshort: \"d\",\n\t\t\tdescription: \"Maximum depth for transitive dependencies\",\n\t\t\tdefault: 3,\n\t\t},\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output including stack traces\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tsetVerbose(ctx.values.verbose)\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst cwd = process.cwd()\n\n\t\tconst apiName = ctx.values.api\n\t\tif (!apiName) {\n\t\t\tlogger.errorWithHelp(ERRORS.API_NAME_REQUIRED)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst format = ctx.values.format ?? \"text\"\n\t\tconst depth = ctx.values.depth ?? 3\n\n\t\t// Load screens.json\n\t\tconst screensPath = join(cwd, config.outDir, \"screens.json\")\n\n\t\tif (!existsSync(screensPath)) {\n\t\t\tlogger.errorWithHelp(ERRORS.SCREENS_NOT_FOUND)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlet screens: Screen[]\n\t\ttry {\n\t\t\tconst content = readFileSync(screensPath, \"utf-8\")\n\t\t\tscreens = JSON.parse(content) as Screen[]\n\t\t} catch (error) {\n\t\t\tlogger.errorWithHelp({\n\t\t\t\t...ERRORS.SCREENS_PARSE_ERROR,\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tif (screens.length === 0) {\n\t\t\tlogger.warn(\"No screens found in the catalog.\")\n\t\t\tlogger.blank()\n\t\t\tlogger.log(\"Run 'screenbook generate' to create screen.meta.ts files,\")\n\t\t\tlogger.log(\"then 'screenbook build' to generate the catalog.\")\n\t\t\treturn\n\t\t}\n\n\t\t// Analyze impact\n\t\tconst result = analyzeImpact(screens, apiName, depth)\n\n\t\t// Output result\n\t\tif (format === \"json\") {\n\t\t\tlogger.log(formatImpactJson(result))\n\t\t} else {\n\t\t\tlogger.log(formatImpactText(result))\n\t\t}\n\t},\n})\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport prompts from \"prompts\"\n\nexport interface FrameworkInfo {\n\tname: string\n\troutesPattern: string\n\tmetaPattern: string\n}\n\ninterface FrameworkDefinition extends FrameworkInfo {\n\tpackages: string[]\n\tconfigFiles: string[]\n\t/**\n\t * Additional check to distinguish variants (e.g., App Router vs Pages Router)\n\t */\n\tcheck?: (cwd: string) => boolean\n}\n\nconst FRAMEWORKS: FrameworkDefinition[] = [\n\t{\n\t\tname: \"Next.js (App Router)\",\n\t\tpackages: [\"next\"],\n\t\tconfigFiles: [\"next.config.js\", \"next.config.mjs\", \"next.config.ts\"],\n\t\troutesPattern: \"app/**/page.tsx\",\n\t\tmetaPattern: \"app/**/screen.meta.ts\",\n\t\tcheck: (cwd) =>\n\t\t\texistsSync(join(cwd, \"app\")) || existsSync(join(cwd, \"src/app\")),\n\t},\n\t{\n\t\tname: \"Next.js (Pages Router)\",\n\t\tpackages: [\"next\"],\n\t\tconfigFiles: [\"next.config.js\", \"next.config.mjs\", \"next.config.ts\"],\n\t\troutesPattern: \"pages/**/*.tsx\",\n\t\tmetaPattern: \"pages/**/screen.meta.ts\",\n\t\tcheck: (cwd) =>\n\t\t\texistsSync(join(cwd, \"pages\")) || existsSync(join(cwd, \"src/pages\")),\n\t},\n\t{\n\t\tname: \"Remix\",\n\t\tpackages: [\"@remix-run/react\", \"remix\"],\n\t\tconfigFiles: [\"remix.config.js\", \"vite.config.ts\"],\n\t\troutesPattern: \"app/routes/**/*.tsx\",\n\t\tmetaPattern: \"app/routes/**/screen.meta.ts\",\n\t},\n\t{\n\t\tname: \"Nuxt\",\n\t\tpackages: [\"nuxt\"],\n\t\tconfigFiles: [\"nuxt.config.ts\", \"nuxt.config.js\", \"nuxt.config.mjs\"],\n\t\troutesPattern: \"pages/**/*.vue\",\n\t\tmetaPattern: \"pages/**/screen.meta.ts\",\n\t\tcheck: (cwd) => {\n\t\t\t// Nuxt 4 uses app/pages, Nuxt 3 uses pages\n\t\t\tif (existsSync(join(cwd, \"app/pages\"))) {\n\t\t\t\treturn false // Will be handled by Nuxt 4 definition\n\t\t\t}\n\t\t\treturn existsSync(join(cwd, \"pages\"))\n\t\t},\n\t},\n\t{\n\t\tname: \"Nuxt 4\",\n\t\tpackages: [\"nuxt\"],\n\t\tconfigFiles: [\"nuxt.config.ts\", \"nuxt.config.js\"],\n\t\troutesPattern: \"app/pages/**/*.vue\",\n\t\tmetaPattern: \"app/pages/**/screen.meta.ts\",\n\t\tcheck: (cwd) => existsSync(join(cwd, \"app/pages\")),\n\t},\n\t{\n\t\tname: \"Astro\",\n\t\tpackages: [\"astro\"],\n\t\tconfigFiles: [\n\t\t\t\"astro.config.mjs\",\n\t\t\t\"astro.config.js\",\n\t\t\t\"astro.config.ts\",\n\t\t\t\"astro.config.cjs\",\n\t\t],\n\t\troutesPattern: \"src/pages/**/*.astro\",\n\t\tmetaPattern: \"src/pages/**/screen.meta.ts\",\n\t},\n\t{\n\t\tname: \"SolidStart\",\n\t\tpackages: [\"@solidjs/start\"],\n\t\tconfigFiles: [\"app.config.ts\", \"app.config.js\"],\n\t\troutesPattern: \"src/routes/**/*.tsx\",\n\t\tmetaPattern: \"src/routes/**/screen.meta.ts\",\n\t\tcheck: (cwd) => existsSync(join(cwd, \"src/routes\")),\n\t},\n\t{\n\t\tname: \"QwikCity\",\n\t\tpackages: [\"@builder.io/qwik-city\"],\n\t\tconfigFiles: [\"vite.config.ts\", \"vite.config.js\", \"vite.config.mjs\"],\n\t\t// QwikCity uses index.tsx files as page components (e.g., about/index.tsx not about.tsx)\n\t\troutesPattern: \"src/routes/**/index.tsx\",\n\t\tmetaPattern: \"src/routes/**/screen.meta.ts\",\n\t\tcheck: (cwd) => existsSync(join(cwd, \"src/routes\")),\n\t},\n\t{\n\t\tname: \"TanStack Start\",\n\t\tpackages: [\"@tanstack/react-start\", \"@tanstack/start\"],\n\t\tconfigFiles: [\"app.config.ts\", \"app.config.js\"],\n\t\troutesPattern: \"src/routes/**/*.tsx\",\n\t\tmetaPattern: \"src/routes/**/screen.meta.ts\",\n\t\t// TanStack Start requires __root.tsx for file-based routing\n\t\tcheck: (cwd) => existsSync(join(cwd, \"src/routes/__root.tsx\")),\n\t},\n\t{\n\t\tname: \"Vite + Vue\",\n\t\tpackages: [\"vite\", \"vue\"],\n\t\tconfigFiles: [\"vite.config.ts\", \"vite.config.js\", \"vite.config.mjs\"],\n\t\troutesPattern: \"src/pages/**/*.vue\",\n\t\tmetaPattern: \"src/pages/**/screen.meta.ts\",\n\t\t// Check that react is NOT present to avoid matching React projects\n\t\tcheck: (cwd) => {\n\t\t\tconst packageJson = readPackageJson(cwd)\n\t\t\tif (!packageJson) return false\n\t\t\treturn !hasPackage(packageJson, \"react\")\n\t\t},\n\t},\n\t{\n\t\tname: \"Vite + React\",\n\t\tpackages: [\"vite\", \"react\"],\n\t\tconfigFiles: [\"vite.config.ts\", \"vite.config.js\", \"vite.config.mjs\"],\n\t\troutesPattern: \"src/pages/**/*.tsx\",\n\t\tmetaPattern: \"src/pages/**/screen.meta.ts\",\n\t},\n]\n\ninterface PackageJson {\n\tdependencies?: Record<string, string>\n\tdevDependencies?: Record<string, string>\n}\n\nfunction readPackageJson(cwd: string): PackageJson | null {\n\tconst packageJsonPath = join(cwd, \"package.json\")\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn null\n\t}\n\ttry {\n\t\tconst content = readFileSync(packageJsonPath, \"utf-8\")\n\t\treturn JSON.parse(content)\n\t} catch (error) {\n\t\tconsole.warn(\n\t\t\t`Warning: Failed to parse package.json at ${packageJsonPath}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t)\n\t\treturn null\n\t}\n}\n\nfunction hasPackage(packageJson: PackageJson, packageName: string): boolean {\n\treturn !!(\n\t\tpackageJson.dependencies?.[packageName] ||\n\t\tpackageJson.devDependencies?.[packageName]\n\t)\n}\n\nfunction hasConfigFile(cwd: string, configFiles: string[]): boolean {\n\treturn configFiles.some((file) => existsSync(join(cwd, file)))\n}\n\n/**\n * Auto-detect the frontend framework in a project directory.\n * Returns framework info if detected, null otherwise.\n */\nexport function detectFramework(cwd: string): FrameworkInfo | null {\n\tconst packageJson = readPackageJson(cwd)\n\tif (!packageJson) {\n\t\treturn null\n\t}\n\n\tfor (const framework of FRAMEWORKS) {\n\t\t// Check if required packages are present\n\t\tconst hasRequiredPackage = framework.packages.some((pkg) =>\n\t\t\thasPackage(packageJson, pkg),\n\t\t)\n\t\tif (!hasRequiredPackage) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Check for config files\n\t\tconst hasConfig = hasConfigFile(cwd, framework.configFiles)\n\t\tif (!hasConfig) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Run additional check if defined\n\t\tif (framework.check && !framework.check(cwd)) {\n\t\t\tcontinue\n\t\t}\n\n\t\treturn {\n\t\t\tname: framework.name,\n\t\t\troutesPattern: framework.routesPattern,\n\t\t\tmetaPattern: framework.metaPattern,\n\t\t}\n\t}\n\n\treturn null\n}\n\n/**\n * Interactive framework selection when auto-detection fails.\n */\nexport async function promptFrameworkSelection(): Promise<FrameworkInfo | null> {\n\tconst choices = FRAMEWORKS.filter(\n\t\t// Remove duplicates (e.g., Nuxt 4 vs Nuxt)\n\t\t(fw, idx, arr) =>\n\t\t\tarr.findIndex((f) => f.routesPattern === fw.routesPattern) === idx,\n\t).map((fw) => ({\n\t\ttitle: fw.name,\n\t\tvalue: fw,\n\t}))\n\n\tchoices.push({\n\t\ttitle: \"Other (manual configuration)\",\n\t\tvalue: null as unknown as FrameworkDefinition,\n\t})\n\n\tconst response = await prompts({\n\t\ttype: \"select\",\n\t\tname: \"framework\",\n\t\tmessage: \"Select your frontend framework:\",\n\t\tchoices,\n\t})\n\n\tif (!response.framework) {\n\t\treturn null\n\t}\n\n\treturn {\n\t\tname: response.framework.name,\n\t\troutesPattern: response.framework.routesPattern,\n\t\tmetaPattern: response.framework.metaPattern,\n\t}\n}\n\n/**\n * Detect framework or prompt user if detection fails.\n */\nexport async function detectOrPromptFramework(\n\tcwd: string,\n): Promise<FrameworkInfo | null> {\n\tconst detected = detectFramework(cwd)\n\tif (detected) {\n\t\treturn detected\n\t}\n\treturn promptFrameworkSelection()\n}\n","/**\n * Check if the current environment supports interactive prompts.\n * Returns false in CI environments or when stdin is not a TTY.\n */\nexport function isInteractive(): boolean {\n\t// Standard CI environment variables (check for any truthy value)\n\tif (process.env.CI || process.env.CONTINUOUS_INTEGRATION) {\n\t\treturn false\n\t}\n\n\t// CI-specific environment variables not covered by the generic CI flag\n\tif (\n\t\tprocess.env.GITHUB_ACTIONS ||\n\t\tprocess.env.GITLAB_CI ||\n\t\tprocess.env.JENKINS_URL\n\t) {\n\t\treturn false\n\t}\n\n\t// TTY check - false if stdin is not a terminal\n\treturn process.stdin.isTTY === true\n}\n","import { spawn } from \"node:child_process\"\nimport {\n\tcopyFileSync,\n\texistsSync,\n\tmkdirSync,\n\treadFileSync,\n\twriteFileSync,\n} from \"node:fs\"\nimport { createRequire } from \"node:module\"\nimport { dirname, join, resolve } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { createJiti } from \"jiti\"\nimport prompts from \"prompts\"\nimport { glob } from \"tinyglobby\"\nimport {\n\tdetectFramework,\n\ttype FrameworkInfo,\n\tpromptFrameworkSelection,\n} from \"../utils/detectFramework.js\"\nimport { isInteractive } from \"../utils/isInteractive.js\"\nimport { logger, setVerbose } from \"../utils/logger.js\"\nimport {\n\ttype GenerateFromRoutesPatternOptions,\n\tgenerateFromRoutesPattern,\n} from \"./generate.js\"\n\nexport function generateConfigTemplate(\n\tframework: FrameworkInfo | null,\n): string {\n\tif (framework) {\n\t\treturn `import { defineConfig } from \"@screenbook/core\"\n\nexport default defineConfig({\n\t// Auto-detected: ${framework.name}\n\tmetaPattern: \"${framework.metaPattern}\",\n\troutesPattern: \"${framework.routesPattern}\",\n\toutDir: \".screenbook\",\n})\n`\n\t}\n\n\t// Fallback template when no framework detected\n\treturn `import { defineConfig } from \"@screenbook/core\"\n\nexport default defineConfig({\n\t// Glob pattern for screen metadata files\n\tmetaPattern: \"src/**/screen.meta.ts\",\n\n\t// Glob pattern for route files (uncomment and adjust for your framework):\n\t// routesPattern: \"src/pages/**/page.tsx\", // Vite/React\n\t// routesPattern: \"app/**/page.tsx\", // Next.js App Router\n\t// routesPattern: \"pages/**/*.tsx\", // Next.js Pages Router\n\t// routesPattern: \"app/routes/**/*.tsx\", // Remix\n\t// routesPattern: \"pages/**/*.vue\", // Nuxt\n\t// routesPattern: \"src/pages/**/*.astro\", // Astro\n\n\toutDir: \".screenbook\",\n})\n`\n}\n\nfunction printValueProposition(): void {\n\tlogger.blank()\n\tlogger.log(logger.bold(\"What Screenbook gives you:\"))\n\tlogger.log(\" - Screen catalog with search & filter\")\n\tlogger.log(\" - Navigation graph visualization\")\n\tlogger.log(\" - Impact analysis (API -> affected screens)\")\n\tlogger.log(\" - CI lint for documentation coverage\")\n}\n\nfunction printNextSteps(hasRoutesPattern: boolean): void {\n\tlogger.blank()\n\tlogger.log(logger.bold(\"Next steps:\"))\n\tif (hasRoutesPattern) {\n\t\tlogger.log(\n\t\t\t` 1. Run ${logger.code(\"screenbook generate\")} to auto-create screen.meta.ts files`,\n\t\t)\n\t\tlogger.log(\n\t\t\t` 2. Run ${logger.code(\"screenbook dev\")} to start the UI server`,\n\t\t)\n\t} else {\n\t\tlogger.log(\" 1. Configure routesPattern in screenbook.config.ts\")\n\t\tlogger.log(\n\t\t\t` 2. Run ${logger.code(\"screenbook generate\")} to auto-create screen.meta.ts files`,\n\t\t)\n\t\tlogger.log(\n\t\t\t` 3. Run ${logger.code(\"screenbook dev\")} to start the UI server`,\n\t\t)\n\t}\n\tlogger.blank()\n\tlogger.log(\"screen.meta.ts files are created alongside your route files:\")\n\tlogger.blank()\n\tlogger.log(logger.dim(\" src/pages/dashboard/\"))\n\tlogger.log(logger.dim(\" page.tsx # Your route file\"))\n\tlogger.log(\n\t\tlogger.dim(\" screen.meta.ts # Auto-generated, customize as needed\"),\n\t)\n}\n\nexport interface ResolveOptionParams {\n\texplicitValue: boolean | undefined\n\tyesAll: boolean\n\tciMode: boolean\n\tciDefault: boolean\n\tpromptMessage: string\n}\n\n/**\n * Resolve a boolean option with priority order:\n * 1. Explicit flag (e.g., --generate or --no-generate) takes precedence\n * 2. -y flag enables all optional features\n * 3. Non-interactive environments (CI mode or no TTY) fall back to ciDefault\n * 4. Otherwise, prompt the user interactively\n */\nexport async function resolveOption(\n\tparams: ResolveOptionParams,\n): Promise<boolean> {\n\tconst { explicitValue, yesAll, ciMode, ciDefault, promptMessage } = params\n\n\t// Priority 1: Explicit flag takes precedence\n\tif (explicitValue !== undefined) {\n\t\treturn explicitValue\n\t}\n\n\t// Priority 2: -y flag answers yes to all\n\tif (yesAll) {\n\t\treturn true\n\t}\n\n\t// Priority 3: Non-interactive environments use ciDefault\n\tif (ciMode || !isInteractive()) {\n\t\treturn ciDefault\n\t}\n\n\t// Priority 4: Interactive prompt\n\tconst response = await prompts({\n\t\ttype: \"confirm\",\n\t\tname: \"value\",\n\t\tmessage: promptMessage,\n\t\tinitial: true,\n\t})\n\n\t// Handle user cancellation (Ctrl+C)\n\tif (response.value === undefined) {\n\t\tlogger.blank()\n\t\tlogger.info(\"Operation cancelled\")\n\t\tprocess.exit(0)\n\t}\n\n\treturn response.value\n}\n\nasync function countRouteFiles(\n\troutesPattern: string,\n\tcwd: string,\n): Promise<number> {\n\tconst files = await glob(routesPattern, { cwd })\n\treturn files.length\n}\n\nasync function runGenerate(routesPattern: string, cwd: string): Promise<void> {\n\tconst options: GenerateFromRoutesPatternOptions = {\n\t\tdryRun: false,\n\t\tforce: false,\n\t\tinteractive: false,\n\t\tignore: [\"**/node_modules/**\"],\n\t\tdetectApi: false,\n\t\tdetectNavigation: false,\n\t\tapiIntegration: undefined,\n\t}\n\n\tawait generateFromRoutesPattern(routesPattern, cwd, options)\n}\n\nasync function buildScreensForDev(\n\tmetaPattern: string,\n\toutDir: string,\n\tcwd: string,\n): Promise<void> {\n\tconst files = await glob(metaPattern, {\n\t\tcwd,\n\t\tignore: [\"**/node_modules/**\"],\n\t})\n\n\tif (files.length === 0) {\n\t\tlogger.warn(`No screen.meta.ts files found matching: ${metaPattern}`)\n\t\treturn\n\t}\n\n\ttype ScreenWithFilePath = Screen & { filePath: string }\n\n\tconst jiti = createJiti(cwd)\n\tconst screens: ScreenWithFilePath[] = []\n\n\tfor (const file of files) {\n\t\tconst absolutePath = resolve(cwd, file)\n\n\t\ttry {\n\t\t\tconst module = (await jiti.import(absolutePath)) as { screen?: Screen }\n\t\t\tif (module.screen) {\n\t\t\t\tscreens.push({ ...module.screen, filePath: absolutePath })\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Log failed files so users can diagnose issues\n\t\t\tlogger.itemWarn(`Failed to load ${file}`)\n\t\t\tif (error instanceof Error) {\n\t\t\t\tlogger.log(` ${logger.dim(error.message)}`)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst outputPath = join(cwd, outDir, \"screens.json\")\n\tconst outputDir = dirname(outputPath)\n\n\tif (!existsSync(outputDir)) {\n\t\tmkdirSync(outputDir, { recursive: true })\n\t}\n\n\twriteFileSync(outputPath, JSON.stringify(screens, null, 2))\n}\n\nfunction resolveUiPackage(): string | null {\n\ttry {\n\t\tconst require = createRequire(import.meta.url)\n\t\tconst uiPackageJson = require.resolve(\"@screenbook/ui/package.json\")\n\t\treturn dirname(uiPackageJson)\n\t} catch {\n\t\tconst possiblePaths = [\n\t\t\tjoin(process.cwd(), \"node_modules\", \"@screenbook\", \"ui\"),\n\t\t\tjoin(process.cwd(), \"..\", \"ui\"),\n\t\t\tjoin(process.cwd(), \"packages\", \"ui\"),\n\t\t]\n\n\t\tfor (const p of possiblePaths) {\n\t\t\tif (existsSync(join(p, \"package.json\"))) {\n\t\t\t\treturn p\n\t\t\t}\n\t\t}\n\n\t\treturn null\n\t}\n}\n\nasync function startDevServer(\n\tmetaPattern: string,\n\toutDir: string,\n\tcwd: string,\n\tport: string,\n): Promise<void> {\n\t// Build screens first\n\tawait buildScreensForDev(metaPattern, outDir, cwd)\n\n\t// Find the UI package\n\tconst uiPackagePath = resolveUiPackage()\n\n\tif (!uiPackagePath) {\n\t\tlogger.warn(\"Could not find @screenbook/ui package\")\n\t\tlogger.log(\n\t\t\t` Run ${logger.code(\"npm install @screenbook/ui\")} to install it`,\n\t\t)\n\t\treturn\n\t}\n\n\t// Copy screens.json to UI package\n\tconst screensJsonPath = join(cwd, outDir, \"screens.json\")\n\tconst uiScreensDir = join(uiPackagePath, \".screenbook\")\n\n\tif (!existsSync(uiScreensDir)) {\n\t\tmkdirSync(uiScreensDir, { recursive: true })\n\t}\n\n\tif (existsSync(screensJsonPath)) {\n\t\tcopyFileSync(screensJsonPath, join(uiScreensDir, \"screens.json\"))\n\t}\n\n\t// Start Astro dev server\n\tlogger.blank()\n\tlogger.info(\n\t\t`Starting UI server on ${logger.highlight(`http://localhost:${port}`)}`,\n\t)\n\tlogger.blank()\n\n\tconst astroProcess = spawn(\"npx\", [\"astro\", \"dev\", \"--port\", port], {\n\t\tcwd: uiPackagePath,\n\t\tstdio: \"inherit\",\n\t\tshell: true,\n\t})\n\n\tastroProcess.on(\"error\", (error) => {\n\t\tlogger.error(`Failed to start server: ${error.message}`)\n\t\tprocess.exit(1)\n\t})\n\n\tastroProcess.on(\"close\", (code) => {\n\t\tprocess.exit(code ?? 0)\n\t})\n\n\t// Handle graceful shutdown\n\tprocess.on(\"SIGINT\", () => {\n\t\tastroProcess.kill(\"SIGINT\")\n\t})\n\n\tprocess.on(\"SIGTERM\", () => {\n\t\tastroProcess.kill(\"SIGTERM\")\n\t})\n}\n\nexport const initCommand = define({\n\tname: \"init\",\n\tdescription: \"Initialize Screenbook in a project\",\n\targs: {\n\t\tforce: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Overwrite existing files\",\n\t\t\tdefault: false,\n\t\t},\n\t\tskipDetect: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Skip framework auto-detection\",\n\t\t\tdefault: false,\n\t\t},\n\t\tgenerate: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Auto-generate screen.meta.ts files (--no-generate to skip)\",\n\t\t\tdefault: undefined,\n\t\t\tnegatable: true,\n\t\t},\n\t\tdev: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Start development server after init (--no-dev to skip)\",\n\t\t\tdefault: undefined,\n\t\t\tnegatable: true,\n\t\t},\n\t\tyes: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"y\",\n\t\t\tdescription: \"Answer yes to all prompts\",\n\t\t\tdefault: false,\n\t\t},\n\t\tci: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"CI mode (no prompts, generate only)\",\n\t\t\tdefault: false,\n\t\t},\n\t\tport: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"p\",\n\t\t\tdescription: \"Port for the dev server\",\n\t\t\tdefault: \"4321\",\n\t\t},\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output including stack traces\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tsetVerbose(ctx.values.verbose)\n\t\tconst cwd = process.cwd()\n\t\tconst force = ctx.values.force ?? false\n\t\tconst skipDetect = ctx.values.skipDetect ?? false\n\t\tconst generateFlag = ctx.values.generate\n\t\tconst devFlag = ctx.values.dev\n\t\tconst yesAll = ctx.values.yes ?? false\n\t\tconst ciMode = ctx.values.ci ?? false\n\t\tconst port = ctx.values.port ?? \"4321\"\n\n\t\tlogger.info(\"Initializing Screenbook...\")\n\t\tlogger.blank()\n\n\t\t// Framework detection\n\t\tlet framework: FrameworkInfo | null = null\n\n\t\tif (!skipDetect) {\n\t\t\tframework = detectFramework(cwd)\n\n\t\t\tif (framework) {\n\t\t\t\tlogger.itemSuccess(`Detected: ${framework.name}`)\n\t\t\t} else {\n\t\t\t\tlogger.log(\" Could not auto-detect framework\")\n\n\t\t\t\t// Only prompt for framework selection in interactive mode\n\t\t\t\tif (!ciMode && isInteractive()) {\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tframework = await promptFrameworkSelection()\n\n\t\t\t\t\tif (framework) {\n\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\tlogger.itemSuccess(`Selected: ${framework.name}`)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Create screenbook.config.ts\n\t\tconst configPath = join(cwd, \"screenbook.config.ts\")\n\t\tif (!force && existsSync(configPath)) {\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"-\")} screenbook.config.ts already exists ${logger.dim(\"(skipped)\")}`,\n\t\t\t)\n\t\t} else {\n\t\t\tconst configContent = generateConfigTemplate(framework)\n\t\t\twriteFileSync(configPath, configContent)\n\t\t\tlogger.itemSuccess(\"Created screenbook.config.ts\")\n\t\t}\n\n\t\t// Update .gitignore\n\t\tconst gitignorePath = join(cwd, \".gitignore\")\n\t\tconst screenbookIgnore = \".screenbook\"\n\n\t\tif (existsSync(gitignorePath)) {\n\t\t\tconst gitignoreContent = readFileSync(gitignorePath, \"utf-8\")\n\t\t\tif (!gitignoreContent.includes(screenbookIgnore)) {\n\t\t\t\tconst newContent = `${gitignoreContent.trimEnd()}\\n\\n# Screenbook\\n${screenbookIgnore}\\n`\n\t\t\t\twriteFileSync(gitignorePath, newContent)\n\t\t\t\tlogger.itemSuccess(\"Added .screenbook to .gitignore\")\n\t\t\t} else {\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(\"-\")} .screenbook already in .gitignore ${logger.dim(\"(skipped)\")}`,\n\t\t\t\t)\n\t\t\t}\n\t\t} else {\n\t\t\twriteFileSync(gitignorePath, `# Screenbook\\n${screenbookIgnore}\\n`)\n\t\t\tlogger.itemSuccess(\"Created .gitignore with .screenbook\")\n\t\t}\n\n\t\tlogger.blank()\n\t\tlogger.done(\"Screenbook initialized successfully!\")\n\n\t\t// If no framework detected or no routesPattern, show traditional next steps\n\t\tif (!framework?.routesPattern) {\n\t\t\tprintValueProposition()\n\t\t\tprintNextSteps(false)\n\t\t\treturn\n\t\t}\n\n\t\t// Count route files\n\t\tconst routeFileCount = await countRouteFiles(framework.routesPattern, cwd)\n\n\t\tif (routeFileCount === 0) {\n\t\t\tprintValueProposition()\n\t\t\tprintNextSteps(true)\n\t\t\treturn\n\t\t}\n\n\t\t// Prompt for generate\n\t\tlogger.blank()\n\t\tconst shouldGenerate = await resolveOption({\n\t\t\texplicitValue: generateFlag,\n\t\t\tyesAll,\n\t\t\tciMode,\n\t\t\tciDefault: true,\n\t\t\tpromptMessage: `Found ${routeFileCount} route files. Generate screen.meta.ts files?`,\n\t\t})\n\n\t\tif (!shouldGenerate) {\n\t\t\tprintValueProposition()\n\t\t\tprintNextSteps(true)\n\t\t\treturn\n\t\t}\n\n\t\t// Run generate\n\t\tlogger.blank()\n\t\tlogger.info(\"Generating screen metadata...\")\n\t\tlogger.blank()\n\n\t\tawait runGenerate(framework.routesPattern, cwd)\n\n\t\t// In CI mode, skip dev server\n\t\tif (ciMode) {\n\t\t\tlogger.blank()\n\t\t\tlogger.done(\"Initialization complete!\")\n\t\t\treturn\n\t\t}\n\n\t\t// Prompt for dev server\n\t\tlogger.blank()\n\t\tconst shouldDev = await resolveOption({\n\t\t\texplicitValue: devFlag,\n\t\t\tyesAll,\n\t\t\tciMode,\n\t\t\tciDefault: false,\n\t\t\tpromptMessage: \"Start the development server?\",\n\t\t})\n\n\t\tif (!shouldDev) {\n\t\t\tlogger.blank()\n\t\t\tlogger.log(logger.bold(\"Next step:\"))\n\t\t\tlogger.log(\n\t\t\t\t` Run ${logger.code(\"screenbook dev\")} to start the UI server`,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\t// Start dev server\n\t\tlogger.blank()\n\t\tlogger.info(\"Starting development server...\")\n\n\t\tawait startDevServer(framework.metaPattern, \".screenbook\", cwd, port)\n\t},\n})\n","import { isAbsolute, resolve } from \"node:path\"\nimport SwaggerParser from \"@apidevtools/swagger-parser\"\nimport type { OpenAPI, OpenAPIV3 } from \"openapi-types\"\n\n/**\n * Parsed OpenAPI specification with extracted API identifiers\n */\nexport interface ParsedOpenApiSpec {\n\t/** Source path/URL of the OpenAPI spec */\n\treadonly source: string\n\t/** Set of valid operationIds (e.g., \"getInvoiceById\") */\n\treadonly operationIds: ReadonlySet<string>\n\t/** Set of valid HTTP method + path combos (e.g., \"GET /api/invoices/{id}\") */\n\treadonly httpEndpoints: ReadonlySet<string>\n\t/** Map from lowercase key to original format for case-insensitive matching */\n\treadonly normalizedToOriginal: ReadonlyMap<string, string>\n}\n\n/**\n * Result of parsing multiple OpenAPI sources\n */\nexport interface OpenApiParseResult {\n\t/** Successfully parsed specifications */\n\treadonly specs: readonly ParsedOpenApiSpec[]\n\t/** Errors encountered during parsing */\n\treadonly errors: readonly OpenApiParseError[]\n}\n\n/**\n * Error encountered while parsing an OpenAPI source\n */\nexport interface OpenApiParseError {\n\t/** Source path/URL that failed to parse */\n\treadonly source: string\n\t/** Error message */\n\treadonly message: string\n}\n\n/**\n * Check if a string looks like a URL\n */\nfunction isUrl(source: string): boolean {\n\treturn source.startsWith(\"http://\") || source.startsWith(\"https://\")\n}\n\n/**\n * Resolve a source path relative to the current working directory\n */\nfunction resolveSource(source: string, cwd: string): string {\n\tif (isUrl(source)) {\n\t\treturn source\n\t}\n\tif (isAbsolute(source)) {\n\t\treturn source\n\t}\n\treturn resolve(cwd, source)\n}\n\n/**\n * HTTP methods to extract from OpenAPI specs\n */\nconst HTTP_METHODS = [\n\t\"get\",\n\t\"post\",\n\t\"put\",\n\t\"delete\",\n\t\"patch\",\n\t\"options\",\n\t\"head\",\n] as const\n\n/**\n * Extract API identifiers from a parsed OpenAPI document\n */\nfunction extractApiIdentifiers(\n\tapi: OpenAPI.Document,\n\tsource: string,\n): ParsedOpenApiSpec {\n\tconst operationIds = new Set<string>()\n\tconst httpEndpoints = new Set<string>()\n\tconst normalizedToOriginal = new Map<string, string>()\n\n\tconst paths = api.paths\n\tif (!paths) {\n\t\treturn { source, operationIds, httpEndpoints, normalizedToOriginal }\n\t}\n\n\tfor (const [path, pathItem] of Object.entries(paths)) {\n\t\tif (!pathItem || typeof pathItem !== \"object\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor (const method of HTTP_METHODS) {\n\t\t\tconst operation = (\n\t\t\t\tpathItem as Record<string, OpenAPIV3.OperationObject | undefined>\n\t\t\t)[method]\n\t\t\tif (!operation) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Extract operationId\n\t\t\tif (operation.operationId) {\n\t\t\t\toperationIds.add(operation.operationId)\n\t\t\t\t// Store lowercase mapping for case-insensitive matching\n\t\t\t\tnormalizedToOriginal.set(\n\t\t\t\t\toperation.operationId.toLowerCase(),\n\t\t\t\t\toperation.operationId,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Generate HTTP endpoint format: \"GET /api/invoices/{id}\"\n\t\t\tconst httpEndpoint = `${method.toUpperCase()} ${path}`\n\t\t\thttpEndpoints.add(httpEndpoint)\n\t\t\t// Store normalized version for matching (lowercase method)\n\t\t\tnormalizedToOriginal.set(httpEndpoint.toLowerCase(), httpEndpoint)\n\t\t}\n\t}\n\n\treturn { source, operationIds, httpEndpoints, normalizedToOriginal }\n}\n\n/**\n * Parse a single OpenAPI source (file or URL)\n */\nasync function parseOpenApiSource(\n\tsource: string,\n\tcwd: string,\n): Promise<ParsedOpenApiSpec> {\n\tconst resolvedSource = resolveSource(source, cwd)\n\tconst api = await SwaggerParser.parse(resolvedSource)\n\treturn extractApiIdentifiers(api, source)\n}\n\n/**\n * Parse multiple OpenAPI specification sources\n *\n * @param sources - Array of file paths or URLs to OpenAPI specifications\n * @param cwd - Current working directory for resolving relative paths\n * @returns Parsed specifications and any errors encountered\n *\n * @example\n * ```ts\n * const result = await parseOpenApiSpecs(\n * [\"./openapi.yaml\", \"https://api.example.com/openapi.json\"],\n * process.cwd()\n * )\n *\n * for (const spec of result.specs) {\n * console.log(`Parsed ${spec.source}:`)\n * console.log(` ${spec.operationIds.size} operation IDs`)\n * console.log(` ${spec.httpEndpoints.size} HTTP endpoints`)\n * }\n *\n * for (const error of result.errors) {\n * console.error(`Failed to parse ${error.source}: ${error.message}`)\n * }\n * ```\n */\nexport async function parseOpenApiSpecs(\n\tsources: readonly string[],\n\tcwd: string,\n): Promise<OpenApiParseResult> {\n\tconst specs: ParsedOpenApiSpec[] = []\n\tconst errors: OpenApiParseError[] = []\n\n\tfor (const source of sources) {\n\t\ttry {\n\t\t\tconst spec = await parseOpenApiSource(source, cwd)\n\t\t\tspecs.push(spec)\n\t\t} catch (error) {\n\t\t\terrors.push({\n\t\t\t\tsource,\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t}\n\t}\n\n\treturn { specs, errors }\n}\n\n/**\n * Get all valid API identifiers from parsed specs (for suggestions)\n */\nexport function getAllApiIdentifiers(\n\tspecs: readonly ParsedOpenApiSpec[],\n): string[] {\n\tconst identifiers: string[] = []\n\n\tfor (const spec of specs) {\n\t\tidentifiers.push(...spec.operationIds)\n\t\tidentifiers.push(...spec.httpEndpoints)\n\t}\n\n\treturn identifiers\n}\n","import type { Screen } from \"@screenbook/core\"\nimport {\n\tgetAllApiIdentifiers,\n\ttype ParsedOpenApiSpec,\n} from \"./openApiParser.js\"\nimport { findBestMatch } from \"./suggestions.js\"\n\n/**\n * Validation error for an invalid dependsOn reference\n */\nexport interface DependsOnValidationError {\n\t/** ID of the screen with the invalid reference */\n\treadonly screenId: string\n\t/** The invalid API reference */\n\treadonly invalidApi: string\n\t/** Suggested correction (if found via fuzzy matching) */\n\treadonly suggestion?: string\n}\n\n/**\n * Successful validation result (all references are valid)\n */\ninterface DependsOnValidationSuccess {\n\treadonly valid: true\n\treadonly errors: readonly []\n}\n\n/**\n * Failed validation result (some references are invalid)\n */\ninterface DependsOnValidationFailure {\n\treadonly valid: false\n\treadonly errors: readonly DependsOnValidationError[]\n}\n\n/**\n * Result of validating dependsOn references (discriminated union)\n */\nexport type DependsOnValidationResult =\n\t| DependsOnValidationSuccess\n\t| DependsOnValidationFailure\n\n/**\n * Normalize a dependsOn value for matching\n * - HTTP format: lowercase the method, keep path as-is\n * - operationId: return as-is for exact match first, then case-insensitive fallback\n */\nfunction normalizeForMatching(value: string): {\n\tnormalized: string\n\tisHttpFormat: boolean\n} {\n\t// Check if it looks like HTTP format: \"GET /path\" or \"POST /path\"\n\tconst httpMatch = value.match(/^(GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD)\\s+/i)\n\tif (httpMatch?.[1]) {\n\t\t// Normalize: lowercase method, keep path\n\t\tconst method = httpMatch[1].toLowerCase()\n\t\tconst path = value.slice(httpMatch[0].length)\n\t\treturn {\n\t\t\tnormalized: `${method} ${path}`,\n\t\t\tisHttpFormat: true,\n\t\t}\n\t}\n\n\t// Treat as operationId - return as-is (exact match attempted first, then case-insensitive)\n\treturn {\n\t\tnormalized: value,\n\t\tisHttpFormat: false,\n\t}\n}\n\n/**\n * Check if a dependsOn value matches any API in the parsed specs\n */\nfunction matchesOpenApiSpec(\n\tdependsOnValue: string,\n\tspecs: readonly ParsedOpenApiSpec[],\n): boolean {\n\tconst { normalized, isHttpFormat } = normalizeForMatching(dependsOnValue)\n\n\tfor (const spec of specs) {\n\t\tif (isHttpFormat) {\n\t\t\t// For HTTP format, check httpEndpoints (case-insensitive method via normalized)\n\t\t\t// We store normalized keys in normalizedToOriginal map\n\t\t\tif (spec.normalizedToOriginal.has(normalized)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t} else {\n\t\t\t// For operationId, exact match first\n\t\t\tif (spec.operationIds.has(dependsOnValue)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\t// Then case-insensitive match\n\t\t\tif (spec.normalizedToOriginal.has(normalized.toLowerCase())) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false\n}\n\n/**\n * Find a suggestion for an invalid API reference using fuzzy matching\n */\nfunction findApiSuggestion(\n\tinvalidApi: string,\n\tspecs: readonly ParsedOpenApiSpec[],\n): string | undefined {\n\tconst allIdentifiers = getAllApiIdentifiers(specs)\n\tif (allIdentifiers.length === 0) {\n\t\treturn undefined\n\t}\n\n\t// Use higher tolerance for API names since they can vary significantly\n\treturn findBestMatch(invalidApi, allIdentifiers, 0.5)\n}\n\n/**\n * Validate dependsOn references against OpenAPI specifications\n *\n * @param screens - Array of screen definitions to validate\n * @param specs - Parsed OpenAPI specifications to validate against\n * @returns Validation result with any errors found\n *\n * @example\n * ```ts\n * const screens = [\n * { id: \"invoice.detail\", dependsOn: [\"getInvoiceById\", \"unknownApi\"] },\n * ]\n * const specs = await parseOpenApiSpecs([\"./openapi.yaml\"], cwd)\n *\n * const result = validateDependsOnReferences(screens, specs.specs)\n * if (!result.valid) {\n * for (const error of result.errors) {\n * console.log(`${error.screenId}: ${error.invalidApi}`)\n * if (error.suggestion) {\n * console.log(` Did you mean \"${error.suggestion}\"?`)\n * }\n * }\n * }\n * ```\n */\nexport function validateDependsOnReferences(\n\tscreens: readonly Screen[],\n\tspecs: readonly ParsedOpenApiSpec[],\n): DependsOnValidationResult {\n\tconst errors: DependsOnValidationError[] = []\n\n\tfor (const screen of screens) {\n\t\tif (!screen.dependsOn || screen.dependsOn.length === 0) {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor (const dep of screen.dependsOn) {\n\t\t\tif (!matchesOpenApiSpec(dep, specs)) {\n\t\t\t\tconst suggestion = findApiSuggestion(dep, specs)\n\t\t\t\terrors.push({\n\t\t\t\t\tscreenId: screen.id,\n\t\t\t\t\tinvalidApi: dep,\n\t\t\t\t\tsuggestion,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\tif (errors.length === 0) {\n\t\treturn { valid: true, errors: [] as const }\n\t}\n\treturn { valid: false, errors }\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { dirname, join, relative, resolve } from \"node:path\"\nimport type { AdoptionConfig, Config, Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { minimatch } from \"minimatch\"\nimport { glob } from \"tinyglobby\"\nimport { parseAngularRouterConfig } from \"../utils/angularRouterParser.js\"\nimport { loadConfig } from \"../utils/config.js\"\nimport {\n\tdetectCycles,\n\tformatCycleWarnings,\n\tgetCycleSummary,\n} from \"../utils/cycleDetection.js\"\nimport { validateDependsOnReferences } from \"../utils/dependsOnValidation.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger, setVerbose } from \"../utils/logger.js\"\nimport { parseOpenApiSpecs } from \"../utils/openApiParser.js\"\nimport {\n\tdetectRouterType,\n\tparseReactRouterConfig,\n} from \"../utils/reactRouterParser.js\"\nimport type { FlatRoute, ParseResult } from \"../utils/routeParserUtils.js\"\nimport { flattenRoutes } from \"../utils/routeParserUtils.js\"\nimport { parseSolidRouterConfig } from \"../utils/solidRouterParser.js\"\nimport { parseTanStackRouterConfig } from \"../utils/tanstackRouterParser.js\"\nimport { parseVueRouterConfig } from \"../utils/vueRouterParser.js\"\n\nexport const lintCommand = define({\n\tname: \"lint\",\n\tdescription: \"Detect routes without screen.meta.ts files\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tallowCycles: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Suppress circular navigation warnings\",\n\t\t\tdefault: false,\n\t\t},\n\t\tstrict: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"s\",\n\t\t\tdescription: \"Fail on disallowed cycles\",\n\t\t\tdefault: false,\n\t\t},\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output including stack traces\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tsetVerbose(ctx.values.verbose)\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst cwd = process.cwd()\n\t\tconst adoption = config.adoption ?? { mode: \"full\" }\n\t\tlet hasWarnings = false\n\n\t\t// Check for routes configuration\n\t\tif (!config.routesPattern && !config.routesFile) {\n\t\t\tlogger.errorWithHelp(ERRORS.ROUTES_PATTERN_MISSING)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlogger.info(\"Linting screen metadata coverage...\")\n\t\tif (adoption.mode === \"progressive\") {\n\t\t\tlogger.log(`Mode: Progressive adoption`)\n\t\t\tif (adoption.includePatterns?.length) {\n\t\t\t\tlogger.log(`Checking: ${adoption.includePatterns.join(\", \")}`)\n\t\t\t}\n\t\t\tif (adoption.minimumCoverage != null) {\n\t\t\t\tlogger.log(`Minimum coverage: ${adoption.minimumCoverage}%`)\n\t\t\t}\n\t\t}\n\t\tlogger.blank()\n\n\t\t// Use routesFile mode (config-based routing)\n\t\tif (config.routesFile) {\n\t\t\tawait lintRoutesFile(\n\t\t\t\tconfig.routesFile,\n\t\t\t\tcwd,\n\t\t\t\tconfig,\n\t\t\t\tadoption,\n\t\t\t\tctx.values.allowCycles ?? false,\n\t\t\t\tctx.values.strict ?? false,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\t// Use routesPattern mode (file-based routing)\n\t\t// Find all route files\n\t\tlet routeFiles = await glob(config.routesPattern as string, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\n\t\t// In progressive mode, filter to only included patterns\n\t\tif (adoption.mode === \"progressive\" && adoption.includePatterns?.length) {\n\t\t\trouteFiles = routeFiles.filter((file) =>\n\t\t\t\tadoption.includePatterns?.some((pattern) => minimatch(file, pattern)),\n\t\t\t)\n\t\t}\n\n\t\tif (routeFiles.length === 0) {\n\t\t\tlogger.warn(`No route files found matching: ${config.routesPattern}`)\n\t\t\tif (adoption.mode === \"progressive\" && adoption.includePatterns?.length) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(`(filtered by includePatterns: ${adoption.includePatterns.join(\", \")})`)}`,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// Find all screen.meta.ts files\n\t\tconst metaFiles = await glob(config.metaPattern, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\n\t\t// Build a set of directories that have screen.meta.ts\n\t\tconst metaDirs = new Set<string>()\n\t\tfor (const metaFile of metaFiles) {\n\t\t\tmetaDirs.add(dirname(metaFile))\n\t\t}\n\n\t\t// Check each route file - simple colocation check\n\t\tconst missingMeta: string[] = []\n\t\tconst covered: string[] = []\n\n\t\tfor (const routeFile of routeFiles) {\n\t\t\tconst routeDir = dirname(routeFile)\n\n\t\t\t// Check if there's a screen.meta.ts in the same directory\n\t\t\tif (metaDirs.has(routeDir)) {\n\t\t\t\tcovered.push(routeFile)\n\t\t\t} else {\n\t\t\t\tmissingMeta.push(routeFile)\n\t\t\t}\n\t\t}\n\n\t\t// Report results\n\t\tconst total = routeFiles.length\n\t\tconst coveredCount = covered.length\n\t\tconst missingCount = missingMeta.length\n\t\tconst coveragePercent = Math.round((coveredCount / total) * 100)\n\n\t\tlogger.log(`Found ${total} route files`)\n\t\tlogger.log(`Coverage: ${coveredCount}/${total} (${coveragePercent}%)`)\n\t\tlogger.blank()\n\n\t\t// Determine if lint should fail\n\t\tconst minimumCoverage = adoption.minimumCoverage ?? 100\n\t\tconst passedCoverage = coveragePercent >= minimumCoverage\n\n\t\tif (missingCount > 0) {\n\t\t\tlogger.log(`Missing screen.meta.ts (${missingCount} files):`)\n\t\t\tlogger.blank()\n\n\t\t\tfor (const file of missingMeta) {\n\t\t\t\tconst suggestedMetaPath = join(dirname(file), \"screen.meta.ts\")\n\t\t\t\tlogger.itemError(file)\n\t\t\t\tlogger.log(` ${logger.dim(\"→\")} ${logger.path(suggestedMetaPath)}`)\n\t\t\t}\n\n\t\t\tlogger.blank()\n\t\t}\n\n\t\tif (!passedCoverage) {\n\t\t\tlogger.error(\n\t\t\t\t`Lint failed: Coverage ${coveragePercent}% is below minimum ${minimumCoverage}%`,\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t} else if (missingCount > 0) {\n\t\t\tlogger.success(\n\t\t\t\t`Coverage ${coveragePercent}% meets minimum ${minimumCoverage}%`,\n\t\t\t)\n\t\t\tif (adoption.mode === \"progressive\") {\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(\"Tip:\")} Increase minimumCoverage in config to gradually improve coverage`,\n\t\t\t\t)\n\t\t\t}\n\t\t} else {\n\t\t\tlogger.done(\"All routes have screen.meta.ts files\")\n\t\t}\n\n\t\t// Check for orphan screens (unreachable screens) and cycles\n\t\tconst screensPath = join(cwd, config.outDir, \"screens.json\")\n\t\tif (existsSync(screensPath)) {\n\t\t\ttry {\n\t\t\t\tconst content = readFileSync(screensPath, \"utf-8\")\n\t\t\t\tconst screens = JSON.parse(content) as Screen[]\n\n\t\t\t\t// Check for orphan screens\n\t\t\t\tconst orphans = findOrphanScreens(screens)\n\n\t\t\t\tif (orphans.length > 0) {\n\t\t\t\t\thasWarnings = true\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.warn(`Orphan screens detected (${orphans.length}):`)\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.log(\" These screens have no entryPoints and are not\")\n\t\t\t\t\tlogger.log(\" referenced in any other screen's 'next' array.\")\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tfor (const orphan of orphans) {\n\t\t\t\t\t\tlogger.itemWarn(`${orphan.id} ${logger.dim(orphan.route)}`)\n\t\t\t\t\t}\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t` ${logger.dim(\"Consider adding entryPoints or removing these screens.\")}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\t// Check for circular navigation\n\t\t\t\tif (!ctx.values.allowCycles) {\n\t\t\t\t\tconst cycleResult = detectCycles(screens)\n\t\t\t\t\tif (cycleResult.hasCycles) {\n\t\t\t\t\t\thasWarnings = true\n\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\tlogger.warn(getCycleSummary(cycleResult))\n\t\t\t\t\t\tlogger.log(formatCycleWarnings(cycleResult.cycles))\n\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\tif (cycleResult.disallowedCycles.length > 0) {\n\t\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t\t` ${logger.dim(\"Use 'allowCycles: true' in screen.meta.ts to allow intentional cycles.\")}`,\n\t\t\t\t\t\t\t)\n\n\t\t\t\t\t\t\tif (ctx.values.strict) {\n\t\t\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\t\t\tlogger.errorWithHelp(\n\t\t\t\t\t\t\t\t\tERRORS.CYCLES_DETECTED(cycleResult.disallowedCycles.length),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tprocess.exit(1)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Check for invalid navigation references\n\t\t\t\tconst invalidNavs = findInvalidNavigations(screens)\n\t\t\t\tif (invalidNavs.length > 0) {\n\t\t\t\t\thasWarnings = true\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.warn(`Invalid navigation targets (${invalidNavs.length}):`)\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\" These navigation references point to non-existent screens.\",\n\t\t\t\t\t)\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tfor (const inv of invalidNavs) {\n\t\t\t\t\t\tlogger.itemWarn(\n\t\t\t\t\t\t\t`${inv.screenId} → ${logger.dim(inv.field)}: \"${inv.target}\"`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t` ${logger.dim(\"Check that these screen IDs exist in your codebase.\")}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\t// Check dependsOn references against OpenAPI specs (if configured)\n\t\t\t\tif (config.apiIntegration?.openapi?.sources?.length) {\n\t\t\t\t\tconst openApiResult = await validateDependsOnAgainstOpenApi(\n\t\t\t\t\t\tscreens,\n\t\t\t\t\t\tconfig.apiIntegration.openapi.sources,\n\t\t\t\t\t\tcwd,\n\t\t\t\t\t\tctx.values.strict ?? false,\n\t\t\t\t\t)\n\t\t\t\t\tif (openApiResult.hasWarnings) {\n\t\t\t\t\t\thasWarnings = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Handle specific error types\n\t\t\t\tif (error instanceof SyntaxError) {\n\t\t\t\t\tlogger.warn(\"Failed to parse screens.json - file may be corrupted\")\n\t\t\t\t\tlogger.log(` ${logger.dim(\"Run 'screenbook build' to regenerate.\")}`)\n\t\t\t\t\thasWarnings = true\n\t\t\t\t} else if (error instanceof Error) {\n\t\t\t\t\tlogger.warn(`Failed to analyze screens.json: ${error.message}`)\n\t\t\t\t\thasWarnings = true\n\t\t\t\t} else {\n\t\t\t\t\tlogger.warn(`Failed to analyze screens.json: ${String(error)}`)\n\t\t\t\t\thasWarnings = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (hasWarnings) {\n\t\t\tlogger.blank()\n\t\t\tlogger.warn(\"Lint completed with warnings.\")\n\t\t}\n\t},\n})\n\n/**\n * Lint screen.meta.ts coverage for routesFile mode (config-based routing)\n */\nasync function lintRoutesFile(\n\troutesFile: string,\n\tcwd: string,\n\tconfig: Pick<Config, \"metaPattern\" | \"outDir\" | \"ignore\" | \"apiIntegration\">,\n\tadoption: AdoptionConfig,\n\tallowCycles: boolean,\n\tstrict: boolean,\n): Promise<boolean> {\n\tlet hasWarnings = false\n\tconst absoluteRoutesFile = resolve(cwd, routesFile)\n\n\t// Check if routes file exists\n\tif (!existsSync(absoluteRoutesFile)) {\n\t\tlogger.errorWithHelp(ERRORS.ROUTES_FILE_NOT_FOUND(routesFile))\n\t\tprocess.exit(1)\n\t}\n\n\tlogger.log(`Parsing routes from ${logger.path(routesFile)}...`)\n\tlogger.blank()\n\n\t// Parse the routes file with auto-detection\n\tlet flatRoutes: FlatRoute[]\n\ttry {\n\t\tconst content = readFileSync(absoluteRoutesFile, \"utf-8\")\n\t\tconst routerType = detectRouterType(content)\n\n\t\tlet parseResult: ParseResult\n\t\tif (routerType === \"tanstack-router\") {\n\t\t\tparseResult = parseTanStackRouterConfig(absoluteRoutesFile, content)\n\t\t} else if (routerType === \"solid-router\") {\n\t\t\tparseResult = parseSolidRouterConfig(absoluteRoutesFile, content)\n\t\t} else if (routerType === \"angular-router\") {\n\t\t\tparseResult = parseAngularRouterConfig(absoluteRoutesFile, content)\n\t\t} else if (routerType === \"react-router\") {\n\t\t\tparseResult = parseReactRouterConfig(absoluteRoutesFile, content)\n\t\t} else if (routerType === \"vue-router\") {\n\t\t\tparseResult = parseVueRouterConfig(absoluteRoutesFile, content)\n\t\t} else {\n\t\t\t// Unknown router type - warn user and attempt Vue Router parser as fallback\n\t\t\tlogger.warn(\n\t\t\t\t`Could not auto-detect router type for ${logger.path(routesFile)}. Attempting to parse as Vue Router.`,\n\t\t\t)\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"If parsing fails, check that your router imports are explicit.\")}`,\n\t\t\t)\n\t\t\thasWarnings = true\n\t\t\tparseResult = parseVueRouterConfig(absoluteRoutesFile, content)\n\t\t}\n\n\t\t// Show warnings\n\t\tfor (const warning of parseResult.warnings) {\n\t\t\tlogger.warn(warning)\n\t\t\thasWarnings = true\n\t\t}\n\n\t\tflatRoutes = flattenRoutes(parseResult.routes)\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tlogger.errorWithHelp(ERRORS.ROUTES_FILE_PARSE_ERROR(routesFile, message))\n\t\tprocess.exit(1)\n\t}\n\n\tif (flatRoutes.length === 0) {\n\t\tlogger.warn(\"No routes found in the config file\")\n\t\treturn hasWarnings\n\t}\n\n\t// Find all screen.meta.ts files\n\tconst metaFiles = await glob(config.metaPattern, {\n\t\tcwd,\n\t\tignore: config.ignore,\n\t})\n\n\t// Build a set of directories that have screen.meta.ts\n\tconst metaDirs = new Set<string>()\n\t// Also build a map from directory basename to full path for component name matching\n\tconst metaDirsByName = new Map<string, string>()\n\tfor (const metaFile of metaFiles) {\n\t\tconst dir = dirname(metaFile)\n\t\tmetaDirs.add(dir)\n\t\t// Store lowercase basename for case-insensitive matching\n\t\tconst baseName = dir.split(\"/\").pop()?.toLowerCase() || \"\"\n\t\tif (baseName) {\n\t\t\tmetaDirsByName.set(baseName, dir)\n\t\t}\n\t}\n\n\t// Check each route for screen.meta.ts coverage\n\tconst missingMeta: FlatRoute[] = []\n\tconst covered: FlatRoute[] = []\n\n\tfor (const route of flatRoutes) {\n\t\t// Skip layout routes (components ending with \"Layout\" that typically don't need screen.meta)\n\t\tif (route.componentPath?.endsWith(\"Layout\")) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Try multiple matching strategies\n\t\tlet matched = false\n\n\t\t// Strategy 1: Check by determineMetaDir (path-based matching)\n\t\tconst metaPath = determineMetaDir(route, cwd)\n\t\tif (metaDirs.has(metaPath)) {\n\t\t\tmatched = true\n\t\t}\n\n\t\t// Strategy 2: Match by component name (for React Router)\n\t\tif (!matched && route.componentPath) {\n\t\t\tconst componentName = route.componentPath.toLowerCase()\n\t\t\tif (metaDirsByName.has(componentName)) {\n\t\t\t\tmatched = true\n\t\t\t}\n\n\t\t\t// Also try matching the last word of component name\n\t\t\t// e.g., \"UserProfile\" -> check for \"profile\" directory\n\t\t\tif (!matched) {\n\t\t\t\t// Split by uppercase letters to get parts\n\t\t\t\tconst parts = route.componentPath.split(/(?=[A-Z])/)\n\t\t\t\tconst lastPart = parts[parts.length - 1]\n\t\t\t\tif (parts.length > 1 && lastPart) {\n\t\t\t\t\tif (metaDirsByName.has(lastPart.toLowerCase())) {\n\t\t\t\t\t\tmatched = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Strategy 3: Match by screenId path pattern\n\t\tif (!matched) {\n\t\t\tconst screenPath = route.screenId.replace(/\\./g, \"/\")\n\t\t\tfor (const dir of metaDirs) {\n\t\t\t\tif (dir.endsWith(screenPath)) {\n\t\t\t\t\tmatched = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (matched) {\n\t\t\tcovered.push(route)\n\t\t} else {\n\t\t\tmissingMeta.push(route)\n\t\t}\n\t}\n\n\t// Report results\n\tconst total = covered.length + missingMeta.length\n\tconst coveredCount = covered.length\n\tconst missingCount = missingMeta.length\n\tconst coveragePercent = Math.round((coveredCount / total) * 100)\n\n\tlogger.log(`Found ${total} routes`)\n\tlogger.log(`Coverage: ${coveredCount}/${total} (${coveragePercent}%)`)\n\tlogger.blank()\n\n\t// Determine if lint should fail\n\tconst minimumCoverage = adoption.minimumCoverage ?? 100\n\tconst passedCoverage = coveragePercent >= minimumCoverage\n\n\tif (missingCount > 0) {\n\t\tlogger.log(`Missing screen.meta.ts (${missingCount} routes):`)\n\t\tlogger.blank()\n\n\t\tfor (const route of missingMeta) {\n\t\t\tconst suggestedMetaPath = determineSuggestedMetaPath(route, cwd)\n\t\t\tlogger.itemError(\n\t\t\t\t`${route.fullPath} ${logger.dim(`(${route.screenId})`)}`,\n\t\t\t)\n\t\t\tlogger.log(` ${logger.dim(\"→\")} ${logger.path(suggestedMetaPath)}`)\n\t\t}\n\n\t\tlogger.blank()\n\t}\n\n\tif (!passedCoverage) {\n\t\tlogger.error(\n\t\t\t`Lint failed: Coverage ${coveragePercent}% is below minimum ${minimumCoverage}%`,\n\t\t)\n\t\tprocess.exit(1)\n\t} else if (missingCount > 0) {\n\t\tlogger.success(\n\t\t\t`Coverage ${coveragePercent}% meets minimum ${minimumCoverage}%`,\n\t\t)\n\t\tif (adoption.mode === \"progressive\") {\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"Tip:\")} Increase minimumCoverage in config to gradually improve coverage`,\n\t\t\t)\n\t\t}\n\t} else {\n\t\tlogger.done(\"All routes have screen.meta.ts files\")\n\t}\n\n\t// Check for orphan screens and cycles using screens.json\n\tconst screensPath = join(cwd, config.outDir, \"screens.json\")\n\tif (existsSync(screensPath)) {\n\t\ttry {\n\t\t\tconst content = readFileSync(screensPath, \"utf-8\")\n\t\t\tconst screens = JSON.parse(content) as Screen[]\n\n\t\t\t// Check for orphan screens\n\t\t\tconst orphans = findOrphanScreens(screens)\n\n\t\t\tif (orphans.length > 0) {\n\t\t\t\thasWarnings = true\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.warn(`Orphan screens detected (${orphans.length}):`)\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(\" These screens have no entryPoints and are not\")\n\t\t\t\tlogger.log(\" referenced in any other screen's 'next' array.\")\n\t\t\t\tlogger.blank()\n\t\t\t\tfor (const orphan of orphans) {\n\t\t\t\t\tlogger.itemWarn(`${orphan.id} ${logger.dim(orphan.route)}`)\n\t\t\t\t}\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(\"Consider adding entryPoints or removing these screens.\")}`,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Check for circular navigation\n\t\t\tif (!allowCycles) {\n\t\t\t\tconst cycleResult = detectCycles(screens)\n\t\t\t\tif (cycleResult.hasCycles) {\n\t\t\t\t\thasWarnings = true\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.warn(getCycleSummary(cycleResult))\n\t\t\t\t\tlogger.log(formatCycleWarnings(cycleResult.cycles))\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tif (cycleResult.disallowedCycles.length > 0) {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` ${logger.dim(\"Use 'allowCycles: true' in screen.meta.ts to allow intentional cycles.\")}`,\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\tif (strict) {\n\t\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\t\tlogger.errorWithHelp(\n\t\t\t\t\t\t\t\tERRORS.CYCLES_DETECTED(cycleResult.disallowedCycles.length),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\tprocess.exit(1)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check for invalid navigation references\n\t\t\tconst invalidNavs = findInvalidNavigations(screens)\n\t\t\tif (invalidNavs.length > 0) {\n\t\t\t\thasWarnings = true\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.warn(`Invalid navigation targets (${invalidNavs.length}):`)\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(\n\t\t\t\t\t\" These navigation references point to non-existent screens.\",\n\t\t\t\t)\n\t\t\t\tlogger.blank()\n\t\t\t\tfor (const inv of invalidNavs) {\n\t\t\t\t\tlogger.itemWarn(\n\t\t\t\t\t\t`${inv.screenId} → ${logger.dim(inv.field)}: \"${inv.target}\"`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(\"Check that these screen IDs exist in your codebase.\")}`,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Check dependsOn references against OpenAPI specs (if configured)\n\t\t\tif (config.apiIntegration?.openapi?.sources?.length) {\n\t\t\t\tconst openApiResult = await validateDependsOnAgainstOpenApi(\n\t\t\t\t\tscreens,\n\t\t\t\t\tconfig.apiIntegration.openapi.sources,\n\t\t\t\t\tcwd,\n\t\t\t\t\tstrict,\n\t\t\t\t)\n\t\t\t\tif (openApiResult.hasWarnings) {\n\t\t\t\t\thasWarnings = true\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (error instanceof SyntaxError) {\n\t\t\t\tlogger.warn(\"Failed to parse screens.json - file may be corrupted\")\n\t\t\t\tlogger.log(` ${logger.dim(\"Run 'screenbook build' to regenerate.\")}`)\n\t\t\t\thasWarnings = true\n\t\t\t} else if (error instanceof Error) {\n\t\t\t\tlogger.warn(`Failed to analyze screens.json: ${error.message}`)\n\t\t\t\thasWarnings = true\n\t\t\t} else {\n\t\t\t\tlogger.warn(`Failed to analyze screens.json: ${String(error)}`)\n\t\t\t\thasWarnings = true\n\t\t\t}\n\t\t}\n\t}\n\n\tif (hasWarnings) {\n\t\tlogger.blank()\n\t\tlogger.warn(\"Lint completed with warnings.\")\n\t}\n\n\treturn hasWarnings\n}\n\n/**\n * Determine the directory where screen.meta.ts should be for a route\n */\nfunction determineMetaDir(route: FlatRoute, cwd: string): string {\n\t// If component path is available, check relative to component directory\n\tif (route.componentPath) {\n\t\tconst componentDir = dirname(route.componentPath)\n\t\tconst relativePath = relative(cwd, componentDir)\n\t\t// Ensure path doesn't escape cwd\n\t\tif (!relativePath.startsWith(\"..\")) {\n\t\t\treturn relativePath\n\t\t}\n\t}\n\n\t// Fall back to src/screens/{screenId} convention\n\tconst screenDir = route.screenId.replace(/\\./g, \"/\")\n\treturn join(\"src\", \"screens\", screenDir)\n}\n\n/**\n * Determine the suggested screen.meta.ts path for a route\n */\nfunction determineSuggestedMetaPath(route: FlatRoute, cwd: string): string {\n\tconst metaDir = determineMetaDir(route, cwd)\n\treturn join(metaDir, \"screen.meta.ts\")\n}\n\ninterface InvalidNavigation {\n\tscreenId: string\n\tfield: string\n\ttarget: string\n}\n\n/**\n * Find navigation references that point to non-existent screens.\n * Checks `next`, `entryPoints` arrays and mock navigation targets.\n */\nfunction findInvalidNavigations(screens: Screen[]): InvalidNavigation[] {\n\tconst screenIds = new Set(screens.map((s) => s.id))\n\tconst invalid: InvalidNavigation[] = []\n\n\tfor (const screen of screens) {\n\t\t// Check next array\n\t\tif (screen.next) {\n\t\t\tfor (const target of screen.next) {\n\t\t\t\tif (!screenIds.has(target)) {\n\t\t\t\t\tinvalid.push({ screenId: screen.id, field: \"next\", target })\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Check entryPoints array\n\t\tif (screen.entryPoints) {\n\t\t\tfor (const target of screen.entryPoints) {\n\t\t\t\tif (!screenIds.has(target)) {\n\t\t\t\t\tinvalid.push({ screenId: screen.id, field: \"entryPoints\", target })\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn invalid\n}\n\n/**\n * Find screens that are unreachable (orphans).\n * A screen is an orphan if:\n * - It has no entryPoints defined\n * - AND it's not referenced in any other screen's `next` array\n */\nfunction findOrphanScreens(screens: Screen[]): Screen[] {\n\t// Build a set of all screen IDs that are referenced in `next` arrays\n\tconst referencedIds = new Set<string>()\n\tfor (const screen of screens) {\n\t\tif (screen.next) {\n\t\t\tfor (const nextId of screen.next) {\n\t\t\t\treferencedIds.add(nextId)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Find orphan screens\n\tconst orphans: Screen[] = []\n\tfor (const screen of screens) {\n\t\tconst hasEntryPoints = screen.entryPoints && screen.entryPoints.length > 0\n\t\tconst isReferenced = referencedIds.has(screen.id)\n\n\t\t// A screen is an orphan if it has no entry points AND is not referenced\n\t\tif (!hasEntryPoints && !isReferenced) {\n\t\t\torphans.push(screen)\n\t\t}\n\t}\n\n\treturn orphans\n}\n\n/**\n * Validate dependsOn references against OpenAPI specifications\n */\nasync function validateDependsOnAgainstOpenApi(\n\tscreens: readonly Screen[],\n\tsources: readonly string[],\n\tcwd: string,\n\tstrict: boolean,\n): Promise<{ hasWarnings: boolean }> {\n\tlet hasWarnings = false\n\n\t// Parse OpenAPI specs (errors are collected internally, not thrown)\n\tconst parseResult = await parseOpenApiSpecs(sources, cwd)\n\n\t// Report parse errors as warnings\n\tfor (const error of parseResult.errors) {\n\t\thasWarnings = true\n\t\tlogger.blank()\n\t\tlogger.warn(`Failed to parse OpenAPI spec: ${error.source}`)\n\t\tlogger.log(` ${logger.dim(error.message)}`)\n\t}\n\n\t// Skip validation if no specs were parsed successfully\n\tif (parseResult.specs.length === 0) {\n\t\treturn { hasWarnings }\n\t}\n\n\t// Validate dependsOn references\n\tconst validationResult = validateDependsOnReferences(\n\t\tscreens,\n\t\tparseResult.specs,\n\t)\n\n\tif (validationResult.errors.length > 0) {\n\t\thasWarnings = true\n\t\tlogger.blank()\n\t\tlogger.warn(`Invalid API dependencies (${validationResult.errors.length}):`)\n\t\tlogger.blank()\n\t\tlogger.log(\n\t\t\t\" These dependsOn references don't match any OpenAPI operation.\",\n\t\t)\n\t\tlogger.blank()\n\n\t\tfor (const error of validationResult.errors) {\n\t\t\tlogger.itemWarn(`${error.screenId}: \"${error.invalidApi}\"`)\n\t\t\tif (error.suggestion) {\n\t\t\t\tlogger.log(` ${logger.dim(`Did you mean \"${error.suggestion}\"?`)}`)\n\t\t\t}\n\t\t}\n\n\t\t// Fail in strict mode\n\t\tif (strict) {\n\t\t\tlogger.blank()\n\t\t\tlogger.errorWithHelp(\n\t\t\t\tERRORS.INVALID_API_DEPENDENCIES(validationResult.errors.length),\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\t}\n\n\treturn { hasWarnings }\n}\n","import { basename, dirname } from \"node:path\"\nimport type { ImpactResult } from \"./impactAnalysis.js\"\n\n/**\n * Extract potential API names from changed file paths.\n * Looks for common API file patterns.\n */\nexport function extractApiNames(files: string[]): string[] {\n\tconst apis = new Set<string>()\n\n\tfor (const file of files) {\n\t\tconst fileName = basename(file, \".ts\")\n\t\t\t.replace(/\\.tsx?$/, \"\")\n\t\t\t.replace(/\\.js$/, \"\")\n\t\t\t.replace(/\\.jsx?$/, \"\")\n\n\t\tconst dirName = basename(dirname(file))\n\n\t\t// Pattern: src/api/InvoiceAPI.ts -> InvoiceAPI\n\t\tif (\n\t\t\tfile.includes(\"/api/\") ||\n\t\t\tfile.includes(\"/apis/\") ||\n\t\t\tfile.includes(\"/services/\")\n\t\t) {\n\t\t\tif (\n\t\t\t\tfileName.endsWith(\"API\") ||\n\t\t\t\tfileName.endsWith(\"Api\") ||\n\t\t\t\tfileName.endsWith(\"Service\")\n\t\t\t) {\n\t\t\t\tapis.add(fileName)\n\t\t\t}\n\t\t}\n\n\t\t// Pattern: src/services/invoice/index.ts -> InvoiceService\n\t\tif (\n\t\t\tfile.includes(\"/services/\") &&\n\t\t\t(fileName === \"index\" || fileName === dirName)\n\t\t) {\n\t\t\tconst serviceName = `${capitalize(dirName)}Service`\n\t\t\tapis.add(serviceName)\n\t\t}\n\n\t\t// Pattern: src/api/invoice.ts -> InvoiceAPI\n\t\tif (file.includes(\"/api/\") || file.includes(\"/apis/\")) {\n\t\t\tif (!fileName.endsWith(\"API\") && !fileName.endsWith(\"Api\")) {\n\t\t\t\tconst apiName = `${capitalize(fileName)}API`\n\t\t\t\tapis.add(apiName)\n\t\t\t}\n\t\t}\n\n\t\t// Pattern: explicit API/Service file names\n\t\tif (\n\t\t\tfileName.toLowerCase().includes(\"api\") ||\n\t\t\tfileName.toLowerCase().includes(\"service\")\n\t\t) {\n\t\t\tapis.add(fileName)\n\t\t}\n\t}\n\n\treturn Array.from(apis).sort()\n}\n\nexport function capitalize(str: string): string {\n\treturn str.charAt(0).toUpperCase() + str.slice(1)\n}\n\n/**\n * Format the results as Markdown for PR comments.\n */\nexport function formatMarkdown(\n\tchangedFiles: string[],\n\tdetectedApis: string[],\n\tresults: ImpactResult[],\n): string {\n\tconst lines: string[] = []\n\n\tlines.push(\"## Screenbook Impact Analysis\")\n\tlines.push(\"\")\n\n\tif (results.length === 0) {\n\t\tlines.push(\"No screen impacts detected from the API changes in this PR.\")\n\t\tlines.push(\"\")\n\t\tlines.push(\"<details>\")\n\t\tlines.push(\"<summary>Detected APIs (no screen dependencies)</summary>\")\n\t\tlines.push(\"\")\n\t\tfor (const api of detectedApis) {\n\t\t\tlines.push(`- \\`${api}\\``)\n\t\t}\n\t\tlines.push(\"\")\n\t\tlines.push(\"</details>\")\n\t\treturn lines.join(\"\\n\")\n\t}\n\n\t// Summary\n\tconst totalDirect = results.reduce((sum, r) => sum + r.direct.length, 0)\n\tconst totalTransitive = results.reduce(\n\t\t(sum, r) => sum + r.transitive.length,\n\t\t0,\n\t)\n\tconst totalScreens = totalDirect + totalTransitive\n\n\tlines.push(\n\t\t`**${totalScreens} screen${totalScreens > 1 ? \"s\" : \"\"} affected** by changes to ${results.length} API${results.length > 1 ? \"s\" : \"\"}`,\n\t)\n\tlines.push(\"\")\n\n\t// Per-API breakdown\n\tfor (const result of results) {\n\t\tlines.push(`### ${result.api}`)\n\t\tlines.push(\"\")\n\n\t\tif (result.direct.length > 0) {\n\t\t\tlines.push(`**Direct dependencies** (${result.direct.length}):`)\n\t\t\tlines.push(\"\")\n\t\t\tlines.push(\"| Screen | Route | Owner |\")\n\t\t\tlines.push(\"|--------|-------|-------|\")\n\t\t\tfor (const screen of result.direct) {\n\t\t\t\tconst owner = screen.owner?.join(\", \") ?? \"-\"\n\t\t\t\tlines.push(`| ${screen.id} | \\`${screen.route}\\` | ${owner} |`)\n\t\t\t}\n\t\t\tlines.push(\"\")\n\t\t}\n\n\t\tif (result.transitive.length > 0) {\n\t\t\tlines.push(`**Transitive dependencies** (${result.transitive.length}):`)\n\t\t\tlines.push(\"\")\n\t\t\tfor (const { path } of result.transitive) {\n\t\t\t\tlines.push(`- ${path.join(\" → \")}`)\n\t\t\t}\n\t\t\tlines.push(\"\")\n\t\t}\n\t}\n\n\t// Changed files summary\n\tlines.push(\"<details>\")\n\tlines.push(`<summary>Changed files (${changedFiles.length})</summary>`)\n\tlines.push(\"\")\n\tfor (const file of changedFiles.slice(0, 20)) {\n\t\tlines.push(`- \\`${file}\\``)\n\t}\n\tif (changedFiles.length > 20) {\n\t\tlines.push(`- ... and ${changedFiles.length - 20} more`)\n\t}\n\tlines.push(\"\")\n\tlines.push(\"</details>\")\n\n\treturn lines.join(\"\\n\")\n}\n","import { execSync } from \"node:child_process\"\nimport { existsSync, readFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { analyzeImpact, type ImpactResult } from \"../utils/impactAnalysis.js\"\nimport { logger, setVerbose } from \"../utils/logger.js\"\nimport { extractApiNames, formatMarkdown } from \"../utils/prImpact.js\"\n\nexport const prImpactCommand = define({\n\tname: \"pr-impact\",\n\tdescription: \"Analyze impact of changed files in a PR\",\n\targs: {\n\t\tbase: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"b\",\n\t\t\tdescription: \"Base branch to compare against (default: main)\",\n\t\t\tdefault: \"main\",\n\t\t},\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tformat: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Output format: markdown (default) or json\",\n\t\t\tdefault: \"markdown\",\n\t\t},\n\t\tdepth: {\n\t\t\ttype: \"number\",\n\t\t\tshort: \"d\",\n\t\t\tdescription: \"Maximum depth for transitive dependencies\",\n\t\t\tdefault: 3,\n\t\t},\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output including stack traces\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tsetVerbose(ctx.values.verbose)\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst cwd = process.cwd()\n\n\t\tconst baseBranch = ctx.values.base ?? \"main\"\n\t\tconst format = ctx.values.format ?? \"markdown\"\n\t\tconst depth = ctx.values.depth ?? 3\n\n\t\t// Get changed files from git\n\t\tlet changedFiles: string[]\n\t\ttry {\n\t\t\tconst gitOutput = execSync(\n\t\t\t\t`git diff --name-only ${baseBranch}...HEAD 2>/dev/null || git diff --name-only ${baseBranch} HEAD`,\n\t\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t\t)\n\t\t\tchangedFiles = gitOutput\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.map((f) => f.trim())\n\t\t\t\t.filter((f) => f.length > 0)\n\t\t} catch {\n\t\t\tlogger.errorWithHelp(ERRORS.GIT_CHANGED_FILES_ERROR(baseBranch))\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tif (changedFiles.length === 0) {\n\t\t\tlogger.info(\"No changed files found.\")\n\t\t\treturn\n\t\t}\n\n\t\t// Extract potential API names from changed files\n\t\tconst apiNames = extractApiNames(changedFiles)\n\n\t\tif (apiNames.length === 0) {\n\t\t\tif (format === \"markdown\") {\n\t\t\t\tlogger.log(\"## Screenbook Impact Analysis\")\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(\"No API-related changes detected in this PR.\")\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(`Changed files: ${changedFiles.length}`)\n\t\t\t} else {\n\t\t\t\tlogger.log(\n\t\t\t\t\tJSON.stringify({ apis: [], results: [], changedFiles }, null, 2),\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// Load screens.json\n\t\tconst screensPath = join(cwd, config.outDir, \"screens.json\")\n\n\t\tif (!existsSync(screensPath)) {\n\t\t\tlogger.errorWithHelp(ERRORS.SCREENS_NOT_FOUND)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlet screens: Screen[]\n\t\ttry {\n\t\t\tconst content = readFileSync(screensPath, \"utf-8\")\n\t\t\tscreens = JSON.parse(content) as Screen[]\n\t\t} catch (error) {\n\t\t\tlogger.errorWithHelp({\n\t\t\t\t...ERRORS.SCREENS_PARSE_ERROR,\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Analyze impact for each API\n\t\tconst results: ImpactResult[] = []\n\t\tfor (const apiName of apiNames) {\n\t\t\tconst result = analyzeImpact(screens, apiName, depth)\n\t\t\tif (result.totalCount > 0) {\n\t\t\t\tresults.push(result)\n\t\t\t}\n\t\t}\n\n\t\t// Output results\n\t\tif (format === \"json\") {\n\t\t\tlogger.log(\n\t\t\t\tJSON.stringify(\n\t\t\t\t\t{\n\t\t\t\t\t\tchangedFiles,\n\t\t\t\t\t\tdetectedApis: apiNames,\n\t\t\t\t\t\tresults: results.map((r) => ({\n\t\t\t\t\t\t\tapi: r.api,\n\t\t\t\t\t\t\tdirectCount: r.direct.length,\n\t\t\t\t\t\t\ttransitiveCount: r.transitive.length,\n\t\t\t\t\t\t\ttotalCount: r.totalCount,\n\t\t\t\t\t\t\tdirect: r.direct.map((s) => ({\n\t\t\t\t\t\t\t\tid: s.id,\n\t\t\t\t\t\t\t\ttitle: s.title,\n\t\t\t\t\t\t\t\troute: s.route,\n\t\t\t\t\t\t\t\towner: s.owner,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\ttransitive: r.transitive.map(({ screen, path }) => ({\n\t\t\t\t\t\t\t\tid: screen.id,\n\t\t\t\t\t\t\t\ttitle: screen.title,\n\t\t\t\t\t\t\t\troute: screen.route,\n\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t})),\n\t\t\t\t\t},\n\t\t\t\t\tnull,\n\t\t\t\t\t2,\n\t\t\t\t),\n\t\t\t)\n\t\t} else {\n\t\t\tlogger.log(formatMarkdown(changedFiles, apiNames, results))\n\t\t}\n\t},\n})\n","#!/usr/bin/env node\n\nimport { readFileSync } from \"node:fs\"\nimport { dirname, join } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport { cli, define } from \"gunshi\"\nimport { badgeCommand } from \"./commands/badge.js\"\nimport { buildCommand } from \"./commands/build.js\"\nimport { devCommand } from \"./commands/dev.js\"\nimport { doctorCommand } from \"./commands/doctor.js\"\nimport { generateCommand } from \"./commands/generate.js\"\nimport { impactCommand } from \"./commands/impact.js\"\nimport { initCommand } from \"./commands/init.js\"\nimport { lintCommand } from \"./commands/lint.js\"\nimport { prImpactCommand } from \"./commands/pr-impact.js\"\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst packageJson = JSON.parse(\n\treadFileSync(join(__dirname, \"..\", \"package.json\"), \"utf-8\"),\n)\nconst version: string = packageJson.version\n\nconst mainCommand = define({\n\tname: \"screenbook\",\n\tdescription: \"Screen catalog and navigation graph generator\",\n\trun: () => {\n\t\tconsole.log(\"Usage: screenbook <command>\")\n\t\tconsole.log(\"\")\n\t\tconsole.log(\"Commands:\")\n\t\tconsole.log(\" init Initialize Screenbook in a project\")\n\t\tconsole.log(\" generate Auto-generate screen.meta.ts from routes\")\n\t\tconsole.log(\" build Build screen metadata JSON\")\n\t\tconsole.log(\" dev Start the development server\")\n\t\tconsole.log(\" lint Detect routes without screen.meta\")\n\t\tconsole.log(\" impact Analyze API dependency impact\")\n\t\tconsole.log(\" pr-impact Analyze PR changes impact\")\n\t\tconsole.log(\" badge Generate coverage badge for README\")\n\t\tconsole.log(\" doctor Diagnose common setup issues\")\n\t\tconsole.log(\"\")\n\t\tconsole.log(\"Run 'screenbook <command> --help' for more information\")\n\t},\n})\n\nawait cli(process.argv.slice(2), mainCommand, {\n\tname: \"screenbook\",\n\tversion,\n\tsubCommands: {\n\t\tinit: initCommand,\n\t\tgenerate: generateCommand,\n\t\tbuild: buildCommand,\n\t\tdev: devCommand,\n\t\tlint: lintCommand,\n\t\timpact: impactCommand,\n\t\t\"pr-impact\": prImpactCommand,\n\t\tbadge: badgeCommand,\n\t\tdoctor: doctorCommand,\n\t},\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["CONFIG_FILES","config: Awaited<ReturnType<typeof loadConfig>>","coverage: CoverageResult","content: string","extension: string","duplicateIds: string[]","cycles: CycleInfo[]","path: string[]","current: string | null | undefined","lines: string[]","matches: Array<{ candidate: string; distance: number }>","matrix: number[][]","errors: ValidationError[]","lines: string[]","screens: ScreenWithFilePath[]","routeFiles: string[]","missing: CoverageData[\"missing\"]","byOwner: CoverageData[\"byOwner\"]","byTag: CoverageData[\"byTag\"]","lines: string[]","resolveUiPackage","screens: ScreenWithFilePath[]","results: CheckResult[]","version","summary: string[]","result: FlatRoute[]","fullPath: string","warnings: string[]","content: string","ast: ReturnType<typeof parse>","routes: ParsedRoute[]","parseRoutesArray","classNode: any","parseRouteObject","path: string | undefined","component: string | undefined","children: ParsedRoute[] | undefined","redirectTo: string | undefined","loc","navigations: DetectedNavigation[]","warnings: string[]","ast: ReturnType<typeof parse>","line","templateNavigations: DetectedNavigation[]","scriptNavigations: DetectedNavigation[]","warnings: string[]","warning: string","navigations: DetectedNavigation[]","parserErrors: string[]","imports: DetectedApiImport[]","warnings: string[]","ast: ReturnType<typeof parse>","warnings: string[]","content: string","ast: ReturnType<typeof parse>","routes: ParsedRoute[]","parseRoutesArray","parseRouteObject","paths: string[]","component: string | undefined","children: ParsedRoute[] | undefined","extractLazyImportPath","loc","warnings: string[]","content: string","ast: ReturnType<typeof parse>","extractLazyImportPath","routeDef: RouteDefinition","parentVarName: string | undefined","childNames: string[]","routes: ParsedRoute[]","route: ParsedRoute","children: ParsedRoute[]","warnings: string[]","content: string","ast: ReturnType<typeof parse>","routes: ParsedRoute[]","parseRoutesArray","parseRouteObject","route: ParsedRoute","parts: string[]","loc","warnings: string[]","content: string","ast: ReturnType<typeof parse>","routes: ParsedRoute[]","importMap: ImportMap","route: Partial<ParsedRoute>","templateNavigations: DetectedNavigation[]","scriptNavigations: DetectedNavigation[]","warnings: string[]","parse","navigations: DetectedNavigation[]","parseResult: ParseResult","screenMeta: InferredScreenMeta","detectedApis: string[]","detectedNext: string[]","componentContent: string","routeContent: string","transitive: TransitiveDependency[]","queue: Array<{ id: string; path: string[] }>","lines: string[]","screens: Screen[]","FRAMEWORKS: FrameworkDefinition[]","screens: ScreenWithFilePath[]","framework: FrameworkInfo | null","specs: ParsedOpenApiSpec[]","errors: OpenApiParseError[]","identifiers: string[]","errors: DependsOnValidationError[]","missingMeta: string[]","covered: string[]","flatRoutes: FlatRoute[]","parseResult: ParseResult","missingMeta: FlatRoute[]","covered: FlatRoute[]","invalid: InvalidNavigation[]","orphans: Screen[]","lines: string[]","changedFiles: string[]","screens: Screen[]","results: ImpactResult[]","version: string"],"sources":["../src/utils/config.ts","../src/utils/errors.ts","../src/utils/logger.ts","../src/commands/badge.ts","../src/utils/cycleDetection.ts","../src/utils/suggestions.ts","../src/utils/validation.ts","../src/commands/build.ts","../src/commands/dev.ts","../src/commands/doctor.ts","../src/utils/routeParserUtils.ts","../src/utils/angularRouterParser.ts","../src/utils/navigationAnalyzer.ts","../src/utils/angularTemplateAnalyzer.ts","../src/utils/apiImportAnalyzer.ts","../src/utils/solidRouterParser.ts","../src/utils/tanstackRouterParser.ts","../src/utils/reactRouterParser.ts","../src/utils/vueRouterParser.ts","../src/utils/vueSFCTemplateAnalyzer.ts","../src/commands/generate.ts","../src/utils/impactAnalysis.ts","../src/commands/impact.ts","../src/utils/detectFramework.ts","../src/utils/isInteractive.ts","../src/commands/init.ts","../src/utils/openApiParser.ts","../src/utils/dependsOnValidation.ts","../src/commands/lint.ts","../src/utils/prImpact.ts","../src/commands/pr-impact.ts","../src/index.ts"],"sourcesContent":["import { existsSync } from \"node:fs\"\nimport { resolve } from \"node:path\"\nimport { type Config, defineConfig } from \"@screenbook/core\"\nimport { createJiti } from \"jiti\"\n\nconst CONFIG_FILES = [\n\t\"screenbook.config.ts\",\n\t\"screenbook.config.js\",\n\t\"screenbook.config.mjs\",\n]\n\nexport async function loadConfig(configPath?: string): Promise<Config> {\n\tconst cwd = process.cwd()\n\n\t// If config path is provided, use it\n\tif (configPath) {\n\t\tconst absolutePath = resolve(cwd, configPath)\n\t\tif (!existsSync(absolutePath)) {\n\t\t\tthrow new Error(`Config file not found: ${configPath}`)\n\t\t}\n\t\treturn await importConfig(absolutePath, cwd)\n\t}\n\n\t// Search for config file in cwd\n\tfor (const configFile of CONFIG_FILES) {\n\t\tconst absolutePath = resolve(cwd, configFile)\n\t\tif (existsSync(absolutePath)) {\n\t\t\treturn await importConfig(absolutePath, cwd)\n\t\t}\n\t}\n\n\t// Return default config if no config file found\n\treturn defineConfig()\n}\n\nasync function importConfig(\n\tabsolutePath: string,\n\tcwd: string,\n): Promise<Config> {\n\tconst jiti = createJiti(cwd)\n\tconst module = (await jiti.import(absolutePath)) as { default?: Config }\n\n\tif (module.default) {\n\t\treturn module.default\n\t}\n\n\tthrow new Error(`Config file must have a default export: ${absolutePath}`)\n}\n","import type { ErrorOptions } from \"./logger.js\"\n\n/**\n * Centralized error definitions for consistent error messages across CLI commands\n */\nexport const ERRORS = {\n\t// ============================================\n\t// Configuration Errors\n\t// ============================================\n\n\tROUTES_PATTERN_MISSING: {\n\t\ttitle: \"Routes configuration not found\",\n\t\tsuggestion:\n\t\t\t\"Add routesPattern (for file-based routing) or routesFile (for config-based routing) to your screenbook.config.ts.\",\n\t\texample: `import { defineConfig } from \"@screenbook/core\"\n\n// Option 1: File-based routing (Next.js, Nuxt, etc.)\nexport default defineConfig({\n routesPattern: \"src/pages/**/page.tsx\",\n})\n\n// Option 2: Config-based routing (Vue Router, React Router, etc.)\nexport default defineConfig({\n routesFile: \"src/router/routes.ts\",\n})`,\n\t} satisfies ErrorOptions,\n\n\tROUTES_FILE_NOT_FOUND: (filePath: string): ErrorOptions => ({\n\t\ttitle: `Routes file not found: ${filePath}`,\n\t\tsuggestion:\n\t\t\t\"Check the routesFile path in your screenbook.config.ts. The file should export a routes array.\",\n\t\texample: `import { defineConfig } from \"@screenbook/core\"\n\nexport default defineConfig({\n routesFile: \"src/router/routes.ts\", // Make sure this file exists\n})`,\n\t}),\n\n\tROUTES_FILE_PARSE_ERROR: (filePath: string, error: string): ErrorOptions => ({\n\t\ttitle: `Failed to parse routes file: ${filePath}`,\n\t\tmessage: error,\n\t\tsuggestion:\n\t\t\t\"Ensure the file exports a valid routes array. Check for syntax errors or unsupported patterns.\",\n\t}),\n\n\tCONFIG_NOT_FOUND: {\n\t\ttitle: \"Configuration file not found\",\n\t\tsuggestion:\n\t\t\t\"Run 'screenbook init' to create a screenbook.config.ts file, or create one manually.\",\n\t\texample: `import { defineConfig } from \"@screenbook/core\"\n\nexport default defineConfig({\n metaPattern: \"src/**/screen.meta.ts\",\n})`,\n\t} satisfies ErrorOptions,\n\n\tNO_ROUTES_FOUND: (pattern: string): ErrorOptions => ({\n\t\ttitle: `No routes found matching pattern: ${pattern}`,\n\t\tsuggestion: \"Check your routesPattern in screenbook.config.ts.\",\n\t\texample: `// Common patterns:\n\"src/app/**/page.tsx\" // Next.js App Router\n\"src/pages/**/*.tsx\" // Pages Router\n\"src/pages/**/*.vue\" // Vue/Nuxt\n\"src/routes/**/*.tsx\" // SolidStart/QwikCity`,\n\t}),\n\n\t// ============================================\n\t// Build/File Errors\n\t// ============================================\n\n\tSCREENS_NOT_FOUND: {\n\t\ttitle: \"screens.json not found\",\n\t\tsuggestion: \"Run 'screenbook build' first to generate the screen catalog.\",\n\t\tmessage:\n\t\t\t\"If you haven't set up Screenbook yet, run 'screenbook init' to get started.\",\n\t} satisfies ErrorOptions,\n\n\tSCREENS_PARSE_ERROR: {\n\t\ttitle: \"Failed to parse screens.json\",\n\t\tsuggestion:\n\t\t\t\"The screens.json file may be corrupted. Try running 'screenbook build' to regenerate it.\",\n\t} satisfies ErrorOptions,\n\n\tMETA_FILE_LOAD_ERROR: (filePath: string): ErrorOptions => ({\n\t\ttitle: `Failed to load ${filePath}`,\n\t\tsuggestion:\n\t\t\t\"Check the file for syntax errors or missing exports. The file should export a 'screen' object.\",\n\t\texample: `import { defineScreen } from \"@screenbook/core\"\n\nexport const screen = defineScreen({\n id: \"example.screen\",\n title: \"Example Screen\",\n route: \"/example\",\n})`,\n\t}),\n\n\tFILE_READ_ERROR: (filePath: string, error: string): ErrorOptions => ({\n\t\ttitle: `Failed to read file: ${filePath}`,\n\t\tmessage: error,\n\t\tsuggestion: \"Check if the file exists and you have read permissions.\",\n\t}),\n\n\tPARSE_ERROR: (filePath: string, error: string): ErrorOptions => ({\n\t\ttitle: `Failed to parse: ${filePath}`,\n\t\tmessage: error,\n\t\tsuggestion: \"Check for syntax errors in the file.\",\n\t}),\n\n\tSCREEN_NOT_FOUND: (\n\t\tscreenId: string,\n\t\tsuggestions?: string[],\n\t): ErrorOptions => ({\n\t\ttitle: `Screen \"${screenId}\" not found`,\n\t\tmessage:\n\t\t\tsuggestions && suggestions.length > 0\n\t\t\t\t? `Did you mean one of these?\\n${suggestions.map((s) => ` - ${s}`).join(\"\\n\")}`\n\t\t\t\t: undefined,\n\t\tsuggestion: \"Check the screen ID in your screen.meta.ts file.\",\n\t}),\n\n\t// ============================================\n\t// Command Argument Errors\n\t// ============================================\n\n\tAPI_NAME_REQUIRED: {\n\t\ttitle: \"API name is required\",\n\t\tsuggestion: \"Provide the API name as an argument.\",\n\t\texample: `screenbook impact UserAPI.getProfile\nscreenbook impact \"PaymentAPI.*\" # Use quotes for patterns`,\n\t} satisfies ErrorOptions,\n\n\t// ============================================\n\t// Git Errors\n\t// ============================================\n\n\tGIT_CHANGED_FILES_ERROR: (baseBranch: string): ErrorOptions => ({\n\t\ttitle: \"Failed to get changed files from git\",\n\t\tmessage: `Make sure you are in a git repository and the base branch '${baseBranch}' exists.`,\n\t\tsuggestion: `Verify the base branch exists with: git branch -a | grep ${baseBranch}`,\n\t}),\n\n\tGIT_NOT_REPOSITORY: {\n\t\ttitle: \"Not a git repository\",\n\t\tsuggestion:\n\t\t\t\"This command requires a git repository. Initialize one with 'git init' or navigate to an existing repository.\",\n\t} satisfies ErrorOptions,\n\n\t// ============================================\n\t// Server Errors\n\t// ============================================\n\n\tSERVER_START_FAILED: (error: string): ErrorOptions => ({\n\t\ttitle: \"Failed to start development server\",\n\t\tmessage: error,\n\t\tsuggestion:\n\t\t\t\"Check if the port is already in use or if there are any dependency issues.\",\n\t}),\n\n\t// ============================================\n\t// Validation Errors\n\t// ============================================\n\n\tVALIDATION_FAILED: (errorCount: number): ErrorOptions => ({\n\t\ttitle: `Validation failed with ${errorCount} error${errorCount === 1 ? \"\" : \"s\"}`,\n\t\tsuggestion:\n\t\t\t\"Fix the validation errors above. Screen references must point to existing screens.\",\n\t}),\n\n\tINVALID_API_DEPENDENCIES: (count: number): ErrorOptions => ({\n\t\ttitle: `${count} invalid API ${count === 1 ? \"dependency\" : \"dependencies\"} detected`,\n\t\tsuggestion:\n\t\t\t\"Fix the dependsOn values to match operationIds or HTTP endpoints from your OpenAPI spec.\",\n\t\texample: `// Using operationId\ndependsOn: [\"getInvoiceById\", \"updatePaymentStatus\"]\n\n// Using HTTP method + path\ndependsOn: [\"GET /api/invoices/{id}\", \"POST /api/payments\"]`,\n\t}),\n\n\t// ============================================\n\t// OpenAPI Errors\n\t// ============================================\n\n\tOPENAPI_PARSE_ERROR: (source: string): ErrorOptions => ({\n\t\ttitle: `Failed to parse OpenAPI spec: ${source}`,\n\t\tsuggestion:\n\t\t\t\"Check that the file path is correct and the OpenAPI specification is valid YAML or JSON.\",\n\t}),\n\n\t// ============================================\n\t// Lint Errors\n\t// ============================================\n\n\tLINT_MISSING_META: (\n\t\tmissingCount: number,\n\t\ttotalRoutes: number,\n\t): ErrorOptions => ({\n\t\ttitle: `${missingCount} route${missingCount === 1 ? \"\" : \"s\"} missing screen.meta.ts`,\n\t\tmessage: `Found ${totalRoutes} route file${totalRoutes === 1 ? \"\" : \"s\"}, but ${missingCount} ${missingCount === 1 ? \"is\" : \"are\"} missing colocated screen.meta.ts.`,\n\t\tsuggestion:\n\t\t\t\"Add screen.meta.ts files next to your route files, or run 'screenbook generate' to create them.\",\n\t}),\n\n\t// ============================================\n\t// Cycle Detection Errors\n\t// ============================================\n\n\tCYCLES_DETECTED: (cycleCount: number): ErrorOptions => ({\n\t\ttitle: `${cycleCount} circular navigation${cycleCount === 1 ? \"\" : \"s\"} detected`,\n\t\tsuggestion:\n\t\t\t\"Review the navigation flow. Use 'allowCycles: true' in screen.meta.ts to allow intentional cycles, or use --allow-cycles to suppress all warnings.\",\n\t\texample: `// Allow a specific screen to be part of cycles\nexport const screen = defineScreen({\n id: \"billing.invoice.detail\",\n next: [\"billing.invoices\"],\n allowCycles: true, // This cycle is intentional\n})`,\n\t}),\n}\n","import pc from \"picocolors\"\n\n// ============================================\n// Verbose Mode State\n// ============================================\n\nlet verboseMode = false\n\n/**\n * Enable or disable verbose mode for detailed output\n */\nexport function setVerbose(verbose: boolean): void {\n\tverboseMode = verbose\n}\n\n/**\n * Check if verbose mode is enabled\n */\nexport function isVerbose(): boolean {\n\treturn verboseMode\n}\n\n/**\n * Structured error options for detailed error messages\n */\nexport interface ErrorOptions {\n\t/** Error title (shown after \"Error:\") */\n\ttitle: string\n\t/** Additional error message/details */\n\tmessage?: string\n\t/** Actionable suggestion for the user */\n\tsuggestion?: string\n\t/** Code example to help the user */\n\texample?: string\n}\n\n/**\n * Logger utility for consistent, color-coded CLI output\n */\nexport const logger = {\n\t// ============================================\n\t// Status Messages\n\t// ============================================\n\n\t/**\n\t * Success message (green checkmark)\n\t */\n\tsuccess: (msg: string): void => {\n\t\tconsole.log(`${pc.green(\"✓\")} ${msg}`)\n\t},\n\n\t/**\n\t * Error message (red X)\n\t */\n\terror: (msg: string): void => {\n\t\tconsole.error(`${pc.red(\"✗\")} ${pc.red(`Error: ${msg}`)}`)\n\t},\n\n\t/**\n\t * Warning message (yellow warning sign)\n\t */\n\twarn: (msg: string): void => {\n\t\tconsole.log(`${pc.yellow(\"⚠\")} ${pc.yellow(`Warning: ${msg}`)}`)\n\t},\n\n\t/**\n\t * Info message (cyan info icon)\n\t */\n\tinfo: (msg: string): void => {\n\t\tconsole.log(`${pc.cyan(\"ℹ\")} ${msg}`)\n\t},\n\n\t// ============================================\n\t// Detailed Error with Guidance\n\t// ============================================\n\n\t/**\n\t * Display a detailed error with actionable suggestions\n\t */\n\terrorWithHelp: (options: ErrorOptions): void => {\n\t\tconst { title, message, suggestion, example } = options\n\n\t\tconsole.error()\n\t\tconsole.error(`${pc.red(\"✗\")} ${pc.red(`Error: ${title}`)}`)\n\n\t\tif (message) {\n\t\t\tconsole.error()\n\t\t\tconsole.error(` ${message}`)\n\t\t}\n\n\t\tif (suggestion) {\n\t\t\tconsole.error()\n\t\t\tconsole.error(` ${pc.cyan(\"Suggestion:\")} ${suggestion}`)\n\t\t}\n\n\t\tif (example) {\n\t\t\tconsole.error()\n\t\t\tconsole.error(` ${pc.dim(\"Example:\")}`)\n\t\t\tfor (const line of example.split(\"\\n\")) {\n\t\t\t\tconsole.error(` ${pc.dim(line)}`)\n\t\t\t}\n\t\t}\n\n\t\tconsole.error()\n\t},\n\n\t/**\n\t * Display an error with optional stack trace (shown only in verbose mode)\n\t */\n\terrorWithStack: (error: unknown, context?: string): void => {\n\t\tconst err = error instanceof Error ? error : new Error(String(error))\n\t\tconst message = context ? `${context}: ${err.message}` : err.message\n\t\tconsole.error(`${pc.red(\"✗\")} ${pc.red(`Error: ${message}`)}`)\n\t\tif (verboseMode && err.stack) {\n\t\t\tconsole.error()\n\t\t\tconsole.error(` ${pc.dim(\"Stack trace:\")}`)\n\t\t\tfor (const line of err.stack.split(\"\\n\").slice(1)) {\n\t\t\t\tconsole.error(` ${pc.dim(line)}`)\n\t\t\t}\n\t\t}\n\t\tconsole.error()\n\t},\n\n\t// ============================================\n\t// Progress Indicators\n\t// ============================================\n\n\t/**\n\t * Step indicator (dimmed arrow)\n\t */\n\tstep: (msg: string): void => {\n\t\tconsole.log(`${pc.dim(\"→\")} ${msg}`)\n\t},\n\n\t/**\n\t * Done/completed indicator (green checkmark with green text)\n\t */\n\tdone: (msg: string): void => {\n\t\tconsole.log(`${pc.green(\"✓\")} ${pc.green(msg)}`)\n\t},\n\n\t/**\n\t * Item success (green checkmark, indented)\n\t */\n\titemSuccess: (msg: string): void => {\n\t\tconsole.log(` ${pc.green(\"✓\")} ${msg}`)\n\t},\n\n\t/**\n\t * Item failure (red X, indented)\n\t */\n\titemError: (msg: string): void => {\n\t\tconsole.log(` ${pc.red(\"✗\")} ${msg}`)\n\t},\n\n\t/**\n\t * Item warning (yellow warning, indented)\n\t */\n\titemWarn: (msg: string): void => {\n\t\tconsole.log(` ${pc.yellow(\"⚠\")} ${msg}`)\n\t},\n\n\t// ============================================\n\t// Plain Output\n\t// ============================================\n\n\t/**\n\t * Plain log (no prefix)\n\t */\n\tlog: (msg: string): void => {\n\t\tconsole.log(msg)\n\t},\n\n\t/**\n\t * Blank line\n\t */\n\tblank: (): void => {\n\t\tconsole.log()\n\t},\n\n\t// ============================================\n\t// Formatting Helpers\n\t// ============================================\n\n\t/**\n\t * Bold text\n\t */\n\tbold: (msg: string): string => pc.bold(msg),\n\n\t/**\n\t * Dimmed text\n\t */\n\tdim: (msg: string): string => pc.dim(msg),\n\n\t/**\n\t * Code/command text (cyan)\n\t */\n\tcode: (msg: string): string => pc.cyan(msg),\n\n\t/**\n\t * File path text (underlined)\n\t */\n\tpath: (msg: string): string => pc.underline(msg),\n\n\t/**\n\t * Highlight text (cyan bold)\n\t */\n\thighlight: (msg: string): string => pc.cyan(pc.bold(msg)),\n\n\t/**\n\t * Green text\n\t */\n\tgreen: (msg: string): string => pc.green(msg),\n\n\t/**\n\t * Red text\n\t */\n\tred: (msg: string): string => pc.red(msg),\n\n\t/**\n\t * Yellow text\n\t */\n\tyellow: (msg: string): string => pc.yellow(msg),\n}\n","import { existsSync, mkdirSync, writeFileSync } from \"node:fs\"\nimport { dirname, join } from \"node:path\"\nimport { define } from \"gunshi\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger, setVerbose } from \"../utils/logger.js\"\n\nconst VALID_FORMATS = [\"svg\", \"json\", \"shields-json\"] as const\nconst VALID_STYLES = [\"flat\", \"flat-square\"] as const\n\ntype BadgeFormat = (typeof VALID_FORMATS)[number]\ntype BadgeStyle = (typeof VALID_STYLES)[number]\n\ninterface CoverageResult {\n\ttotal: number\n\tcovered: number\n\tpercentage: number\n}\n\n/**\n * Calculate coverage based on route files and meta files\n */\nasync function calculateCoverage(\n\troutesPattern: string,\n\tmetaPattern: string,\n\tignore: string[],\n\tcwd: string,\n): Promise<CoverageResult> {\n\t// Find all route files\n\tconst routeFiles = await glob(routesPattern, { cwd, ignore })\n\n\tif (routeFiles.length === 0) {\n\t\treturn { total: 0, covered: 0, percentage: 0 }\n\t}\n\n\t// Find all screen.meta.ts files\n\tconst metaFiles = await glob(metaPattern, { cwd, ignore })\n\n\t// Build a set of directories that have screen.meta.ts\n\tconst metaDirs = new Set<string>()\n\tfor (const metaFile of metaFiles) {\n\t\tmetaDirs.add(dirname(metaFile))\n\t}\n\n\t// Count covered routes\n\tlet covered = 0\n\tfor (const routeFile of routeFiles) {\n\t\tif (metaDirs.has(dirname(routeFile))) {\n\t\t\tcovered++\n\t\t}\n\t}\n\n\tconst total = routeFiles.length\n\tconst percentage = Math.round((covered / total) * 100)\n\n\treturn { total, covered, percentage }\n}\n\n/**\n * Get badge color based on coverage percentage\n */\nfunction getBadgeColor(percentage: number): string {\n\tif (percentage >= 80) return \"#4c1\" // Green\n\tif (percentage >= 50) return \"#dfb317\" // Yellow\n\treturn \"#e05d44\" // Red\n}\n\n/**\n * Get shields.io color name based on coverage percentage\n */\nfunction getShieldsColorName(percentage: number): string {\n\tif (percentage >= 80) return \"brightgreen\"\n\tif (percentage >= 50) return \"yellow\"\n\treturn \"red\"\n}\n\n/**\n * Generate SVG badge\n */\nexport function generateSvgBadge(\n\tpercentage: number,\n\tstyle: BadgeStyle = \"flat\",\n): string {\n\tconst color = getBadgeColor(percentage)\n\tconst label = \"screenbook\"\n\tconst value = `${percentage}%`\n\n\tconst labelWidth = 70\n\tconst valueWidth = 45\n\tconst totalWidth = labelWidth + valueWidth\n\tconst radius = style === \"flat\" ? 3 : 0\n\n\treturn `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${totalWidth}\" height=\"20\">\n <linearGradient id=\"b\" x2=\"0\" y2=\"100%\">\n <stop offset=\"0\" stop-color=\"#bbb\" stop-opacity=\".1\"/>\n <stop offset=\"1\" stop-opacity=\".1\"/>\n </linearGradient>\n <clipPath id=\"a\">\n <rect width=\"${totalWidth}\" height=\"20\" rx=\"${radius}\" fill=\"#fff\"/>\n </clipPath>\n <g clip-path=\"url(#a)\">\n <rect width=\"${labelWidth}\" height=\"20\" fill=\"#555\"/>\n <rect x=\"${labelWidth}\" width=\"${valueWidth}\" height=\"20\" fill=\"${color}\"/>\n <rect width=\"${totalWidth}\" height=\"20\" fill=\"url(#b)\"/>\n </g>\n <g fill=\"#fff\" text-anchor=\"middle\" font-family=\"Verdana,Geneva,sans-serif\" font-size=\"11\">\n <text x=\"${labelWidth / 2}\" y=\"15\" fill=\"#010101\" fill-opacity=\".3\">${label}</text>\n <text x=\"${labelWidth / 2}\" y=\"14\">${label}</text>\n <text x=\"${labelWidth + valueWidth / 2}\" y=\"15\" fill=\"#010101\" fill-opacity=\".3\">${value}</text>\n <text x=\"${labelWidth + valueWidth / 2}\" y=\"14\">${value}</text>\n </g>\n</svg>`\n}\n\n/**\n * Generate shields.io endpoint JSON\n */\nexport function generateShieldsJson(percentage: number): object {\n\treturn {\n\t\tschemaVersion: 1,\n\t\tlabel: \"screenbook\",\n\t\tmessage: `${percentage}%`,\n\t\tcolor: getShieldsColorName(percentage),\n\t}\n}\n\n/**\n * Generate simple JSON with coverage data\n */\nexport function generateSimpleJson(coverage: CoverageResult): object {\n\treturn {\n\t\tpercentage: coverage.percentage,\n\t\tcovered: coverage.covered,\n\t\ttotal: coverage.total,\n\t}\n}\n\nexport const badgeCommand = define({\n\tname: \"badge\",\n\tdescription: \"Generate coverage badge for README\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\toutput: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"o\",\n\t\t\tdescription: \"Output file path\",\n\t\t},\n\t\tformat: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Output format: svg (default), json, shields-json\",\n\t\t\tdefault: \"svg\",\n\t\t},\n\t\tstyle: {\n\t\t\ttype: \"string\",\n\t\t\tdescription: \"Badge style: flat (default), flat-square\",\n\t\t\tdefault: \"flat\",\n\t\t},\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output including stack traces\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tsetVerbose(ctx.values.verbose)\n\t\tconst cwd = process.cwd()\n\n\t\t// Validate format and style arguments\n\t\tconst formatInput = ctx.values.format ?? \"svg\"\n\t\tconst styleInput = ctx.values.style ?? \"flat\"\n\n\t\tif (!VALID_FORMATS.includes(formatInput as BadgeFormat)) {\n\t\t\tlogger.error(`Invalid format: \"${formatInput}\"`)\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(`Valid formats: ${VALID_FORMATS.join(\", \")}`)}`,\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tif (!VALID_STYLES.includes(styleInput as BadgeStyle)) {\n\t\t\tlogger.error(`Invalid style: \"${styleInput}\"`)\n\t\t\tlogger.log(` ${logger.dim(`Valid styles: ${VALID_STYLES.join(\", \")}`)}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst format = formatInput as BadgeFormat\n\t\tconst style = styleInput as BadgeStyle\n\n\t\t// Load configuration\n\t\tlet config: Awaited<ReturnType<typeof loadConfig>>\n\t\ttry {\n\t\t\tconfig = await loadConfig(ctx.values.config)\n\t\t} catch (error) {\n\t\t\tif (ctx.values.config) {\n\t\t\t\tlogger.errorWithStack(\n\t\t\t\t\terror,\n\t\t\t\t\t`Failed to load config from ${ctx.values.config}`,\n\t\t\t\t)\n\t\t\t} else {\n\t\t\t\tlogger.errorWithHelp(ERRORS.CONFIG_NOT_FOUND)\n\t\t\t}\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Check for routes configuration\n\t\tif (!config.routesPattern) {\n\t\t\tlogger.errorWithHelp(ERRORS.ROUTES_PATTERN_MISSING)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlogger.info(\"Calculating coverage...\")\n\n\t\t// Calculate coverage\n\t\tlet coverage: CoverageResult\n\t\ttry {\n\t\t\tcoverage = await calculateCoverage(\n\t\t\t\tconfig.routesPattern,\n\t\t\t\tconfig.metaPattern,\n\t\t\t\tconfig.ignore,\n\t\t\t\tcwd,\n\t\t\t)\n\t\t} catch (error) {\n\t\t\tlogger.errorWithStack(error, \"Failed to calculate coverage\")\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"Check that your routesPattern and metaPattern are valid glob patterns.\")}`,\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tif (coverage.total === 0) {\n\t\t\tlogger.warn(`No route files found matching: ${config.routesPattern}`)\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"Badge will show 0% coverage for empty projects.\")}`,\n\t\t\t)\n\t\t}\n\n\t\tlogger.log(\n\t\t\t`Coverage: ${coverage.covered}/${coverage.total} (${coverage.percentage}%)`,\n\t\t)\n\n\t\t// Generate badge content\n\t\tlet content: string\n\t\tlet extension: string\n\n\t\tswitch (format) {\n\t\t\tcase \"shields-json\":\n\t\t\t\tcontent = JSON.stringify(\n\t\t\t\t\tgenerateShieldsJson(coverage.percentage),\n\t\t\t\t\tnull,\n\t\t\t\t\t2,\n\t\t\t\t)\n\t\t\t\textension = \"json\"\n\t\t\t\tbreak\n\t\t\tcase \"json\":\n\t\t\t\tcontent = JSON.stringify(generateSimpleJson(coverage), null, 2)\n\t\t\t\textension = \"json\"\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tcontent = generateSvgBadge(coverage.percentage, style)\n\t\t\t\textension = \"svg\"\n\t\t\t\tbreak\n\t\t}\n\n\t\t// Determine output path\n\t\tconst defaultFileName =\n\t\t\tformat === \"shields-json\"\n\t\t\t\t? \"coverage.json\"\n\t\t\t\t: `coverage-badge.${extension}`\n\t\tconst outputPath =\n\t\t\tctx.values.output ?? join(cwd, config.outDir, defaultFileName)\n\n\t\t// Ensure output directory exists\n\t\tconst outputDir = dirname(outputPath)\n\t\ttry {\n\t\t\tif (!existsSync(outputDir)) {\n\t\t\t\tmkdirSync(outputDir, { recursive: true })\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogger.errorWithStack(\n\t\t\t\terror,\n\t\t\t\t`Failed to create output directory: ${outputDir}`,\n\t\t\t)\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"Check that you have write permissions to this location.\")}`,\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Write the badge\n\t\ttry {\n\t\t\twriteFileSync(outputPath, content)\n\t\t} catch (error) {\n\t\t\tlogger.errorWithStack(error, `Failed to write badge file: ${outputPath}`)\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"Check that you have write permissions and sufficient disk space.\")}`,\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlogger.blank()\n\t\tlogger.success(`Generated ${logger.path(outputPath)}`)\n\n\t\t// Show usage hint\n\t\tif (format === \"svg\") {\n\t\t\tlogger.blank()\n\t\t\tlogger.log(logger.dim(\"Usage in README.md:\"))\n\t\t\tlogger.log(\n\t\t\t\tlogger.dim(` ![Screenbook Coverage](${outputPath.replace(cwd, \".\")})`),\n\t\t\t)\n\t\t} else if (format === \"shields-json\") {\n\t\t\tlogger.blank()\n\t\t\tlogger.log(logger.dim(\"Usage with shields.io:\"))\n\t\t\tlogger.log(\n\t\t\t\tlogger.dim(\n\t\t\t\t\t\" ![Coverage](https://img.shields.io/endpoint?url=YOUR_URL/coverage.json)\",\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\t},\n})\n","import type { Screen } from \"@screenbook/core\"\n\n/**\n * Information about a detected cycle\n */\nexport interface CycleInfo {\n\t/**\n\t * Array of screen IDs forming the cycle, including the repeated start node\n\t * @example [\"A\", \"B\", \"C\", \"A\"]\n\t */\n\tcycle: string[]\n\t/**\n\t * True if any screen in the cycle has allowCycles: true\n\t */\n\tallowed: boolean\n}\n\n/**\n * Result of cycle detection analysis\n */\nexport interface CycleDetectionResult {\n\t/**\n\t * True if any cycles were detected\n\t */\n\thasCycles: boolean\n\t/**\n\t * All detected cycles\n\t */\n\tcycles: CycleInfo[]\n\t/**\n\t * Cycles that are not allowed (allowed: false)\n\t */\n\tdisallowedCycles: CycleInfo[]\n\t/**\n\t * Screen IDs that appear more than once (indicates data issue)\n\t */\n\tduplicateIds: string[]\n}\n\n// Node colors for DFS\nenum Color {\n\tWhite = 0, // Unvisited\n\tGray = 1, // In progress (on current path)\n\tBlack = 2, // Completed\n}\n\n/**\n * Detect circular navigation dependencies in screen definitions.\n * Uses DFS with coloring algorithm: O(V + E) complexity.\n *\n * @example\n * ```ts\n * const screens = [\n * { id: \"A\", next: [\"B\"] },\n * { id: \"B\", next: [\"C\"] },\n * { id: \"C\", next: [\"A\"] }, // Creates cycle A → B → C → A\n * ]\n * const result = detectCycles(screens)\n * // result.hasCycles === true\n * // result.cycles[0].cycle === [\"A\", \"B\", \"C\", \"A\"]\n * ```\n */\nexport function detectCycles(screens: Screen[]): CycleDetectionResult {\n\tconst screenMap = new Map<string, Screen>()\n\tconst duplicateIds: string[] = []\n\n\t// Build screen map and detect duplicates\n\tfor (const screen of screens) {\n\t\tif (!screen.id || typeof screen.id !== \"string\") {\n\t\t\t// Skip screens with invalid IDs\n\t\t\tcontinue\n\t\t}\n\t\tif (screenMap.has(screen.id)) {\n\t\t\tduplicateIds.push(screen.id)\n\t\t}\n\t\tscreenMap.set(screen.id, screen)\n\t}\n\n\tconst color = new Map<string, Color>()\n\tconst parent = new Map<string, string | null>()\n\tconst cycles: CycleInfo[] = []\n\n\t// Initialize all nodes as white\n\tfor (const id of screenMap.keys()) {\n\t\tcolor.set(id, Color.White)\n\t}\n\n\t// DFS from each unvisited node\n\tfor (const id of screenMap.keys()) {\n\t\tif (color.get(id) === Color.White) {\n\t\t\tdfs(id, null)\n\t\t}\n\t}\n\n\tfunction dfs(nodeId: string, parentId: string | null): void {\n\t\tcolor.set(nodeId, Color.Gray)\n\t\tparent.set(nodeId, parentId)\n\n\t\tconst node = screenMap.get(nodeId)\n\t\tconst neighbors = node?.next ?? []\n\n\t\tfor (const neighborId of neighbors) {\n\t\t\tconst neighborColor = color.get(neighborId)\n\n\t\t\tif (neighborColor === Color.Gray) {\n\t\t\t\t// Back edge found - cycle detected\n\t\t\t\tconst cyclePath = reconstructCycle(nodeId, neighborId)\n\t\t\t\tconst allowed = isCycleAllowed(cyclePath, screenMap)\n\t\t\t\tcycles.push({ cycle: cyclePath, allowed })\n\t\t\t} else if (neighborColor === Color.White) {\n\t\t\t\t// Only visit if the neighbor exists in our screen map\n\t\t\t\tif (screenMap.has(neighborId)) {\n\t\t\t\t\tdfs(neighborId, nodeId)\n\t\t\t\t}\n\t\t\t}\n\t\t\t// If Black, already fully processed - skip\n\t\t}\n\n\t\tcolor.set(nodeId, Color.Black)\n\t}\n\n\t/**\n\t * Reconstruct cycle path from back edge\n\t */\n\tfunction reconstructCycle(from: string, to: string): string[] {\n\t\tconst path: string[] = []\n\t\tlet current: string | null | undefined = from\n\t\tconst visited = new Set<string>()\n\t\tconst maxIterations = screenMap.size + 1\n\n\t\t// Trace back from 'from' to 'to' with loop guard\n\t\twhile (\n\t\t\tcurrent &&\n\t\t\tcurrent !== to &&\n\t\t\t!visited.has(current) &&\n\t\t\tpath.length < maxIterations\n\t\t) {\n\t\t\tvisited.add(current)\n\t\t\tpath.unshift(current)\n\t\t\tcurrent = parent.get(current)\n\t\t}\n\n\t\t// Add 'to' at the beginning (the cycle start)\n\t\tpath.unshift(to)\n\n\t\t// Add 'to' at the end to close the cycle\n\t\tpath.push(to)\n\n\t\treturn path\n\t}\n\n\tconst disallowedCycles = cycles.filter((c) => !c.allowed)\n\n\treturn {\n\t\thasCycles: cycles.length > 0,\n\t\tcycles,\n\t\tdisallowedCycles,\n\t\tduplicateIds,\n\t}\n}\n\n/**\n * Check if any screen in the cycle has allowCycles: true\n */\nfunction isCycleAllowed(\n\tcyclePath: string[],\n\tscreenMap: Map<string, Screen>,\n): boolean {\n\t// Exclude the last element as it's a duplicate of the first\n\tconst uniqueNodes = cyclePath.slice(0, -1)\n\n\tfor (const nodeId of uniqueNodes) {\n\t\tconst screen = screenMap.get(nodeId)\n\t\tif (screen?.allowCycles === true) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n/**\n * Format cycle information for console output\n *\n * @example\n * ```\n * Cycle 1: A → B → C → A\n * Cycle 2 (allowed): D → E → D\n * ```\n */\nexport function formatCycleWarnings(cycles: CycleInfo[]): string {\n\tif (cycles.length === 0) {\n\t\treturn \"\"\n\t}\n\n\tconst lines: string[] = []\n\n\tfor (let i = 0; i < cycles.length; i++) {\n\t\tconst cycle = cycles[i]\n\t\tif (!cycle) continue\n\n\t\tconst cycleStr = cycle.cycle.join(\" → \")\n\t\tconst allowedSuffix = cycle.allowed ? \" (allowed)\" : \"\"\n\t\tlines.push(` Cycle ${i + 1}${allowedSuffix}: ${cycleStr}`)\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n\n/**\n * Get a summary of cycle detection results\n */\nexport function getCycleSummary(result: CycleDetectionResult): string {\n\tif (!result.hasCycles) {\n\t\treturn \"No circular navigation detected\"\n\t}\n\n\tconst total = result.cycles.length\n\tconst disallowed = result.disallowedCycles.length\n\tconst allowed = total - disallowed\n\n\tif (disallowed === 0) {\n\t\treturn `${total} circular navigation${total > 1 ? \"s\" : \"\"} detected (all allowed)`\n\t}\n\n\tif (allowed === 0) {\n\t\treturn `${total} circular navigation${total > 1 ? \"s\" : \"\"} detected`\n\t}\n\n\treturn `${total} circular navigation${total > 1 ? \"s\" : \"\"} detected (${disallowed} not allowed, ${allowed} allowed)`\n}\n","/**\n * Fuzzy matching utilities for suggesting similar values\n */\n\nexport interface FindSimilarOptions {\n\t/** Maximum Levenshtein distance ratio (0-1). Default: 0.4 (40% of target length) */\n\tmaxDistanceRatio?: number\n\t/** Maximum number of suggestions to return. Default: 3 */\n\tmaxSuggestions?: number\n}\n\n/**\n * Find similar strings using Levenshtein distance\n */\nexport function findSimilar(\n\ttarget: string,\n\tcandidates: string[] | Set<string>,\n\toptions: FindSimilarOptions = {},\n): string[] {\n\tconst { maxDistanceRatio = 0.4, maxSuggestions = 3 } = options\n\n\t// Runtime validation for options\n\tconst effectiveRatio = Math.max(0, Math.min(1, maxDistanceRatio))\n\tconst effectiveSuggestions = Math.max(1, Math.floor(maxSuggestions))\n\n\tconst candidateArray = Array.isArray(candidates)\n\t\t? candidates\n\t\t: Array.from(candidates)\n\n\t// Only suggest if distance is reasonable\n\tconst maxDistance = Math.ceil(target.length * effectiveRatio)\n\n\tconst matches: Array<{ candidate: string; distance: number }> = []\n\n\tfor (const candidate of candidateArray) {\n\t\tconst distance = levenshteinDistance(target, candidate)\n\t\tif (distance <= maxDistance) {\n\t\t\tmatches.push({ candidate, distance })\n\t\t}\n\t}\n\n\t// Sort by distance (closest first) and return top suggestions\n\treturn matches\n\t\t.sort((a, b) => a.distance - b.distance)\n\t\t.slice(0, effectiveSuggestions)\n\t\t.map((m) => m.candidate)\n}\n\n/**\n * Find the single best match (convenience wrapper for findSimilar)\n */\nexport function findBestMatch(\n\ttarget: string,\n\tcandidates: string[] | Set<string>,\n\tmaxDistanceRatio = 0.4,\n): string | undefined {\n\tconst matches = findSimilar(target, candidates, {\n\t\tmaxDistanceRatio,\n\t\tmaxSuggestions: 1,\n\t})\n\treturn matches[0]\n}\n\n/**\n * Format suggestions for display in error messages\n */\nexport function formatSuggestions(suggestions: string[]): string {\n\tif (suggestions.length === 0) {\n\t\treturn \"\"\n\t}\n\n\tconst lines = [\"Did you mean one of these?\"]\n\tfor (const suggestion of suggestions) {\n\t\tlines.push(` - ${suggestion}`)\n\t}\n\treturn lines.join(\"\\n\")\n}\n\n/**\n * Calculate Levenshtein distance between two strings\n */\nexport function levenshteinDistance(a: string, b: string): number {\n\t// Pre-initialize matrix with proper dimensions\n\tconst matrix: number[][] = Array.from({ length: a.length + 1 }, () =>\n\t\tArray.from({ length: b.length + 1 }, () => 0),\n\t)\n\n\t// Helper to safely get/set matrix values (matrix is pre-initialized, so these are always valid)\n\tconst get = (i: number, j: number): number => matrix[i]?.[j] ?? 0\n\tconst set = (i: number, j: number, value: number): void => {\n\t\tconst row = matrix[i]\n\t\tif (row) row[j] = value\n\t}\n\n\t// Initialize first column\n\tfor (let i = 0; i <= a.length; i++) {\n\t\tset(i, 0, i)\n\t}\n\n\t// Initialize first row\n\tfor (let j = 0; j <= b.length; j++) {\n\t\tset(0, j, j)\n\t}\n\n\t// Fill the matrix\n\tfor (let i = 1; i <= a.length; i++) {\n\t\tfor (let j = 1; j <= b.length; j++) {\n\t\t\tconst cost = a[i - 1] === b[j - 1] ? 0 : 1\n\t\t\tset(\n\t\t\t\ti,\n\t\t\t\tj,\n\t\t\t\tMath.min(\n\t\t\t\t\tget(i - 1, j) + 1, // deletion\n\t\t\t\t\tget(i, j - 1) + 1, // insertion\n\t\t\t\t\tget(i - 1, j - 1) + cost, // substitution\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\t}\n\n\treturn get(a.length, b.length)\n}\n","import type { Screen } from \"@screenbook/core\"\nimport { findBestMatch } from \"./suggestions.js\"\n\nexport interface ValidationError {\n\tscreenId: string\n\tfield: \"next\" | \"entryPoints\"\n\tinvalidRef: string\n\tsuggestion?: string\n}\n\nexport interface ValidationResult {\n\tvalid: boolean\n\terrors: ValidationError[]\n}\n\n/**\n * Validate screen references (next and entryPoints)\n */\nexport function validateScreenReferences(screens: Screen[]): ValidationResult {\n\tconst screenIds = new Set(screens.map((s) => s.id))\n\tconst errors: ValidationError[] = []\n\n\tfor (const screen of screens) {\n\t\t// Validate next references\n\t\tif (screen.next) {\n\t\t\tfor (const nextId of screen.next) {\n\t\t\t\tif (!screenIds.has(nextId)) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\tscreenId: screen.id,\n\t\t\t\t\t\tfield: \"next\",\n\t\t\t\t\t\tinvalidRef: nextId,\n\t\t\t\t\t\tsuggestion: findBestMatch(nextId, screenIds),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Validate entryPoints references\n\t\tif (screen.entryPoints) {\n\t\t\tfor (const entryId of screen.entryPoints) {\n\t\t\t\tif (!screenIds.has(entryId)) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\tscreenId: screen.id,\n\t\t\t\t\t\tfield: \"entryPoints\",\n\t\t\t\t\t\tinvalidRef: entryId,\n\t\t\t\t\t\tsuggestion: findBestMatch(entryId, screenIds),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tvalid: errors.length === 0,\n\t\terrors,\n\t}\n}\n\n/**\n * Format validation errors for console output\n */\nexport function formatValidationErrors(errors: ValidationError[]): string {\n\tconst lines: string[] = []\n\n\tfor (const error of errors) {\n\t\tlines.push(` Screen \"${error.screenId}\"`)\n\t\tlines.push(\n\t\t\t` → ${error.field} references non-existent screen \"${error.invalidRef}\"`,\n\t\t)\n\t\tif (error.suggestion) {\n\t\t\tlines.push(` Did you mean \"${error.suggestion}\"?`)\n\t\t}\n\t\tlines.push(\"\")\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n","import { existsSync, mkdirSync, writeFileSync } from \"node:fs\"\nimport { dirname, join, resolve } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { createJiti } from \"jiti\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.js\"\nimport {\n\tdetectCycles,\n\tformatCycleWarnings,\n\tgetCycleSummary,\n} from \"../utils/cycleDetection.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger, setVerbose } from \"../utils/logger.js\"\nimport {\n\tformatValidationErrors,\n\tvalidateScreenReferences,\n} from \"../utils/validation.js\"\n\nexport interface CoverageData {\n\ttotal: number\n\tcovered: number\n\tpercentage: number\n\tmissing: Array<{\n\t\troute: string\n\t\tsuggestedPath: string\n\t}>\n\tbyOwner: Record<string, { count: number; screens: string[] }>\n\tbyTag: Record<string, number>\n\ttimestamp: string\n}\n\nexport const buildCommand = define({\n\tname: \"build\",\n\tdescription: \"Build screen metadata JSON from screen.meta.ts files\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\toutDir: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"o\",\n\t\t\tdescription: \"Output directory\",\n\t\t},\n\t\tstrict: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"s\",\n\t\t\tdescription: \"Fail on validation errors and disallowed cycles\",\n\t\t\tdefault: false,\n\t\t},\n\t\tallowCycles: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Suppress all circular navigation warnings\",\n\t\t\tdefault: false,\n\t\t},\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output including stack traces\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tsetVerbose(ctx.values.verbose)\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst outDir = ctx.values.outDir ?? config.outDir\n\t\tconst cwd = process.cwd()\n\n\t\tlogger.info(\"Building screen metadata...\")\n\n\t\t// Find all screen.meta.ts files\n\t\tconst files = await glob(config.metaPattern, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\n\t\tif (files.length === 0) {\n\t\t\tlogger.warn(\n\t\t\t\t`No screen.meta.ts files found matching: ${config.metaPattern}`,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\tlogger.info(`Found ${files.length} screen files`)\n\n\t\t// Create jiti instance for loading TypeScript files\n\t\tconst jiti = createJiti(cwd)\n\n\t\t// Extended screen type with file path (for internal use)\n\t\ttype ScreenWithFilePath = Screen & { filePath: string }\n\n\t\t// Load and collect screen metadata\n\t\tconst screens: ScreenWithFilePath[] = []\n\n\t\tfor (const file of files) {\n\t\t\tconst absolutePath = resolve(cwd, file)\n\n\t\t\ttry {\n\t\t\t\tconst module = (await jiti.import(absolutePath)) as { screen?: Screen }\n\t\t\t\tif (module.screen) {\n\t\t\t\t\tscreens.push({ ...module.screen, filePath: absolutePath })\n\t\t\t\t\tlogger.itemSuccess(module.screen.id)\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlogger.itemError(`Failed to load ${file}`)\n\t\t\t\tif (error instanceof Error) {\n\t\t\t\t\tlogger.log(` ${logger.dim(error.message)}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Validate screen references\n\t\tconst validation = validateScreenReferences(screens)\n\t\tif (!validation.valid) {\n\t\t\tlogger.blank()\n\t\t\tlogger.warn(\"Invalid screen references found:\")\n\t\t\tlogger.log(formatValidationErrors(validation.errors))\n\n\t\t\tif (ctx.values.strict) {\n\t\t\t\tlogger.errorWithHelp(ERRORS.VALIDATION_FAILED(validation.errors.length))\n\t\t\t\tprocess.exit(1)\n\t\t\t}\n\t\t}\n\n\t\t// Detect circular navigation\n\t\tif (!ctx.values.allowCycles) {\n\t\t\tconst cycleResult = detectCycles(screens)\n\t\t\tif (cycleResult.hasCycles) {\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.warn(getCycleSummary(cycleResult))\n\t\t\t\tlogger.log(formatCycleWarnings(cycleResult.cycles))\n\n\t\t\t\tif (ctx.values.strict && cycleResult.disallowedCycles.length > 0) {\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.errorWithHelp(\n\t\t\t\t\t\tERRORS.CYCLES_DETECTED(cycleResult.disallowedCycles.length),\n\t\t\t\t\t)\n\t\t\t\t\tprocess.exit(1)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Generate screens.json\n\t\tconst outputPath = join(cwd, outDir, \"screens.json\")\n\t\tconst outputDir = dirname(outputPath)\n\n\t\tif (!existsSync(outputDir)) {\n\t\t\tmkdirSync(outputDir, { recursive: true })\n\t\t}\n\n\t\twriteFileSync(outputPath, JSON.stringify(screens, null, 2))\n\t\tlogger.blank()\n\t\tlogger.success(`Generated ${logger.path(outputPath)}`)\n\n\t\t// Generate Mermaid graph\n\t\tconst mermaidPath = join(cwd, outDir, \"graph.mmd\")\n\t\tconst mermaidContent = generateMermaidGraph(screens)\n\t\twriteFileSync(mermaidPath, mermaidContent)\n\t\tlogger.success(`Generated ${logger.path(mermaidPath)}`)\n\n\t\t// Generate coverage.json\n\t\tconst coverage = await generateCoverageData(config, cwd, screens)\n\t\tconst coveragePath = join(cwd, outDir, \"coverage.json\")\n\t\twriteFileSync(coveragePath, JSON.stringify(coverage, null, 2))\n\t\tlogger.success(`Generated ${logger.path(coveragePath)}`)\n\t\tlogger.blank()\n\t\tlogger.done(\n\t\t\t`Coverage: ${coverage.covered}/${coverage.total} (${coverage.percentage}%)`,\n\t\t)\n\t},\n})\n\nasync function generateCoverageData(\n\tconfig: { routesPattern?: string; metaPattern: string; ignore: string[] },\n\tcwd: string,\n\tscreens: Screen[],\n): Promise<CoverageData> {\n\t// Get all route files if routesPattern is configured\n\tlet routeFiles: string[] = []\n\tif (config.routesPattern) {\n\t\trouteFiles = await glob(config.routesPattern, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\t}\n\n\t// Get directories that have screen.meta.ts\n\tconst _metaDirs = new Set(\n\t\tscreens.map((s) => {\n\t\t\t// Extract directory from route or id\n\t\t\tconst parts = s.id.split(\".\")\n\t\t\treturn parts.slice(0, -1).join(\"/\") || parts[0]\n\t\t}),\n\t)\n\n\t// Find missing routes (routes without screen.meta.ts)\n\tconst missing: CoverageData[\"missing\"] = []\n\tfor (const routeFile of routeFiles) {\n\t\tconst routeDir = dirname(routeFile)\n\t\tconst hasMetaFile = screens.some((s) => {\n\t\t\t// Check if any screen's route matches this route file's directory\n\t\t\tconst screenDir = s.id.replace(/\\./g, \"/\")\n\t\t\treturn (\n\t\t\t\trouteDir.includes(screenDir) ||\n\t\t\t\tscreenDir.includes(\n\t\t\t\t\trouteDir.replace(/^src\\/pages\\//, \"\").replace(/^app\\//, \"\"),\n\t\t\t\t)\n\t\t\t)\n\t\t})\n\n\t\tif (!hasMetaFile) {\n\t\t\tmissing.push({\n\t\t\t\troute: routeFile,\n\t\t\t\tsuggestedPath: join(dirname(routeFile), \"screen.meta.ts\"),\n\t\t\t})\n\t\t}\n\t}\n\n\t// Calculate coverage\n\tconst total = routeFiles.length > 0 ? routeFiles.length : screens.length\n\tconst covered = screens.length\n\tconst percentage = total > 0 ? Math.round((covered / total) * 100) : 100\n\n\t// Group by owner\n\tconst byOwner: CoverageData[\"byOwner\"] = {}\n\tfor (const screen of screens) {\n\t\tconst owners = screen.owner || [\"unassigned\"]\n\t\tfor (const owner of owners) {\n\t\t\tif (!byOwner[owner]) {\n\t\t\t\tbyOwner[owner] = { count: 0, screens: [] }\n\t\t\t}\n\t\t\tbyOwner[owner].count++\n\t\t\tbyOwner[owner].screens.push(screen.id)\n\t\t}\n\t}\n\n\t// Group by tag\n\tconst byTag: CoverageData[\"byTag\"] = {}\n\tfor (const screen of screens) {\n\t\tconst tags = screen.tags || []\n\t\tfor (const tag of tags) {\n\t\t\tbyTag[tag] = (byTag[tag] || 0) + 1\n\t\t}\n\t}\n\n\treturn {\n\t\ttotal,\n\t\tcovered,\n\t\tpercentage,\n\t\tmissing,\n\t\tbyOwner,\n\t\tbyTag,\n\t\ttimestamp: new Date().toISOString(),\n\t}\n}\n\nfunction generateMermaidGraph(screens: Screen[]): string {\n\tconst lines: string[] = [\"flowchart TD\"]\n\n\t// Create nodes\n\tfor (const screen of screens) {\n\t\tconst label = screen.title.replace(/\"/g, \"'\")\n\t\tlines.push(` ${sanitizeId(screen.id)}[\"${label}\"]`)\n\t}\n\n\tlines.push(\"\")\n\n\t// Create edges from next relationships\n\tfor (const screen of screens) {\n\t\tif (screen.next) {\n\t\t\tfor (const nextId of screen.next) {\n\t\t\t\tlines.push(` ${sanitizeId(screen.id)} --> ${sanitizeId(nextId)}`)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n\nfunction sanitizeId(id: string): string {\n\treturn id.replace(/\\./g, \"_\")\n}\n","import { spawn } from \"node:child_process\"\nimport { copyFileSync, existsSync, mkdirSync, writeFileSync } from \"node:fs\"\nimport { createRequire } from \"node:module\"\nimport { dirname, join, resolve } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { createJiti } from \"jiti\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger, setVerbose } from \"../utils/logger.js\"\n\nexport const devCommand = define({\n\tname: \"dev\",\n\tdescription: \"Start the Screenbook development server\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tport: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"p\",\n\t\t\tdescription: \"Port to run the server on\",\n\t\t\tdefault: \"4321\",\n\t\t},\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output including stack traces\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tsetVerbose(ctx.values.verbose)\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst port = ctx.values.port ?? \"4321\"\n\t\tconst cwd = process.cwd()\n\n\t\tlogger.info(\"Starting Screenbook development server...\")\n\n\t\t// First, build the screen metadata\n\t\tawait buildScreens(config, cwd)\n\n\t\t// Find the UI package location\n\t\tconst uiPackagePath = resolveUiPackage()\n\n\t\tif (!uiPackagePath) {\n\t\t\tlogger.errorWithHelp({\n\t\t\t\ttitle: \"Could not find @screenbook/ui package\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Make sure @screenbook/ui is installed. Run 'npm install @screenbook/ui' or 'pnpm add @screenbook/ui'.\",\n\t\t\t})\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Copy screens.json and coverage.json to UI package\n\t\tconst screensJsonPath = join(cwd, config.outDir, \"screens.json\")\n\t\tconst coverageJsonPath = join(cwd, config.outDir, \"coverage.json\")\n\t\tconst uiScreensDir = join(uiPackagePath, \".screenbook\")\n\n\t\tif (!existsSync(uiScreensDir)) {\n\t\t\tmkdirSync(uiScreensDir, { recursive: true })\n\t\t}\n\n\t\tif (existsSync(screensJsonPath)) {\n\t\t\tcopyFileSync(screensJsonPath, join(uiScreensDir, \"screens.json\"))\n\t\t}\n\n\t\tif (existsSync(coverageJsonPath)) {\n\t\t\tcopyFileSync(coverageJsonPath, join(uiScreensDir, \"coverage.json\"))\n\t\t}\n\n\t\t// Start Astro dev server\n\t\tlogger.blank()\n\t\tlogger.info(\n\t\t\t`Starting UI server on ${logger.highlight(`http://localhost:${port}`)}`,\n\t\t)\n\n\t\tconst astroProcess = spawn(\"npx\", [\"astro\", \"dev\", \"--port\", port], {\n\t\t\tcwd: uiPackagePath,\n\t\t\tstdio: \"inherit\",\n\t\t\tshell: true,\n\t\t})\n\n\t\tastroProcess.on(\"error\", (error) => {\n\t\t\tlogger.errorWithHelp(ERRORS.SERVER_START_FAILED(error.message))\n\t\t\tprocess.exit(1)\n\t\t})\n\n\t\tastroProcess.on(\"close\", (code) => {\n\t\t\tprocess.exit(code ?? 0)\n\t\t})\n\n\t\t// Handle graceful shutdown\n\t\tprocess.on(\"SIGINT\", () => {\n\t\t\tastroProcess.kill(\"SIGINT\")\n\t\t})\n\n\t\tprocess.on(\"SIGTERM\", () => {\n\t\t\tastroProcess.kill(\"SIGTERM\")\n\t\t})\n\t},\n})\n\nexport async function buildScreens(\n\tconfig: { metaPattern: string; outDir: string; ignore: string[] },\n\tcwd: string,\n): Promise<void> {\n\tconst files = await glob(config.metaPattern, {\n\t\tcwd,\n\t\tignore: config.ignore,\n\t})\n\n\tif (files.length === 0) {\n\t\tlogger.warn(`No screen.meta.ts files found matching: ${config.metaPattern}`)\n\t\treturn\n\t}\n\n\tlogger.info(`Found ${files.length} screen files`)\n\n\t// Extended screen type with file path (for internal use)\n\ttype ScreenWithFilePath = Screen & { filePath: string }\n\n\tconst jiti = createJiti(cwd)\n\tconst screens: ScreenWithFilePath[] = []\n\n\tfor (const file of files) {\n\t\tconst absolutePath = resolve(cwd, file)\n\n\t\ttry {\n\t\t\tconst module = (await jiti.import(absolutePath)) as { screen?: Screen }\n\t\t\tif (module.screen) {\n\t\t\t\tscreens.push({ ...module.screen, filePath: absolutePath })\n\t\t\t\tlogger.itemSuccess(module.screen.id)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogger.itemError(`Failed to load ${file}`)\n\t\t\tif (error instanceof Error) {\n\t\t\t\tlogger.log(` ${logger.dim(error.message)}`)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst outputPath = join(cwd, config.outDir, \"screens.json\")\n\tconst outputDir = dirname(outputPath)\n\n\tif (!existsSync(outputDir)) {\n\t\tmkdirSync(outputDir, { recursive: true })\n\t}\n\n\twriteFileSync(outputPath, JSON.stringify(screens, null, 2))\n\tlogger.blank()\n\tlogger.success(`Generated ${logger.path(outputPath)}`)\n}\n\nexport function resolveUiPackage(): string | null {\n\t// Try to resolve @screenbook/ui from node_modules\n\ttry {\n\t\tconst require = createRequire(import.meta.url)\n\t\tconst uiPackageJson = require.resolve(\"@screenbook/ui/package.json\")\n\t\treturn dirname(uiPackageJson)\n\t} catch {\n\t\t// Fallback: look in common locations\n\t\tconst possiblePaths = [\n\t\t\tjoin(process.cwd(), \"node_modules\", \"@screenbook\", \"ui\"),\n\t\t\tjoin(process.cwd(), \"..\", \"ui\"),\n\t\t\tjoin(process.cwd(), \"packages\", \"ui\"),\n\t\t]\n\n\t\tfor (const p of possiblePaths) {\n\t\t\tif (existsSync(join(p, \"package.json\"))) {\n\t\t\t\treturn p\n\t\t\t}\n\t\t}\n\n\t\treturn null\n\t}\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { join, resolve } from \"node:path\"\nimport { define } from \"gunshi\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { logger } from \"../utils/logger.js\"\n\nconst CONFIG_FILES = [\n\t\"screenbook.config.ts\",\n\t\"screenbook.config.js\",\n\t\"screenbook.config.mjs\",\n]\n\nexport interface CheckResult {\n\tname: string\n\tstatus: \"pass\" | \"fail\" | \"warn\"\n\tmessage: string\n\tsuggestion?: string\n}\n\ninterface PackageJson {\n\tdependencies?: Record<string, string>\n\tdevDependencies?: Record<string, string>\n}\n\nexport const doctorCommand = define({\n\tname: \"doctor\",\n\tdescription: \"Diagnose common issues with Screenbook setup\",\n\targs: {\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst cwd = process.cwd()\n\t\tconst verbose = ctx.values.verbose\n\n\t\tlogger.log(\"\")\n\t\tlogger.log(logger.bold(\"Screenbook Doctor\"))\n\t\tlogger.log(\"─────────────────\")\n\t\tlogger.log(\"\")\n\n\t\tconst results: CheckResult[] = []\n\n\t\t// Run all checks\n\t\tresults.push(await checkConfigFile(cwd))\n\t\tresults.push(await checkDependencies(cwd))\n\n\t\t// Load config for remaining checks\n\t\tconst config = await loadConfig()\n\n\t\tresults.push(await checkMetaPattern(cwd, config.metaPattern, config.ignore))\n\t\tresults.push(\n\t\t\tawait checkRoutesPattern(cwd, config.routesPattern, config.ignore),\n\t\t)\n\t\tresults.push(await checkBuildOutput(cwd, config.outDir))\n\t\tresults.push(await checkVersionCompatibility(cwd))\n\t\tresults.push(await checkGitRepository(cwd))\n\n\t\t// Display results\n\t\tdisplayResults(results, verbose)\n\t},\n})\n\n// Exported for testing\nexport async function checkConfigFile(cwd: string): Promise<CheckResult> {\n\tfor (const configFile of CONFIG_FILES) {\n\t\tconst absolutePath = resolve(cwd, configFile)\n\t\tif (existsSync(absolutePath)) {\n\t\t\treturn {\n\t\t\t\tname: \"Config file\",\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tmessage: `Found: ${configFile}`,\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tname: \"Config file\",\n\t\tstatus: \"warn\",\n\t\tmessage: \"No config file found (using defaults)\",\n\t\tsuggestion: \"Run 'screenbook init' to create a config file\",\n\t}\n}\n\nexport async function checkDependencies(cwd: string): Promise<CheckResult> {\n\tconst packageJsonPath = join(cwd, \"package.json\")\n\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn {\n\t\t\tname: \"Dependencies\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: \"package.json not found\",\n\t\t\tsuggestion: \"Run 'npm init' or 'pnpm init' to create package.json\",\n\t\t}\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(packageJsonPath, \"utf-8\")\n\t\tconst pkg = JSON.parse(content) as PackageJson\n\n\t\tconst allDeps = { ...pkg.dependencies, ...pkg.devDependencies }\n\t\tconst unifiedVersion = allDeps.screenbook\n\t\tconst coreVersion = allDeps[\"@screenbook/core\"]\n\t\tconst cliVersion = allDeps[\"@screenbook/cli\"]\n\n\t\t// Check for unified screenbook package first\n\t\tif (unifiedVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Dependencies\",\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tmessage: `screenbook@${unifiedVersion}`,\n\t\t\t}\n\t\t}\n\n\t\tif (!coreVersion && !cliVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Dependencies\",\n\t\t\t\tstatus: \"fail\",\n\t\t\t\tmessage: \"Screenbook packages not installed\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Run 'pnpm add -D screenbook' or 'pnpm add -D @screenbook/core @screenbook/cli' to install\",\n\t\t\t}\n\t\t}\n\n\t\tif (!coreVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Dependencies\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: \"@screenbook/core not found in dependencies\",\n\t\t\t\tsuggestion: \"Run 'pnpm add -D @screenbook/core' to install\",\n\t\t\t}\n\t\t}\n\n\t\tif (!cliVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Dependencies\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: \"@screenbook/cli not found in dependencies\",\n\t\t\t\tsuggestion: \"Run 'pnpm add -D @screenbook/cli' to install\",\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: \"Dependencies\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `@screenbook/core@${coreVersion}, @screenbook/cli@${cliVersion}`,\n\t\t}\n\t} catch (error) {\n\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\t\treturn {\n\t\t\tname: \"Dependencies\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `Failed to read package.json: ${errorMessage}`,\n\t\t\tsuggestion: \"Ensure package.json is valid JSON\",\n\t\t}\n\t}\n}\n\nexport async function checkMetaPattern(\n\tcwd: string,\n\tmetaPattern: string,\n\tignore: string[],\n): Promise<CheckResult> {\n\ttry {\n\t\tconst files = await glob(metaPattern, { cwd, ignore })\n\n\t\tif (files.length === 0) {\n\t\t\treturn {\n\t\t\t\tname: \"Screen meta files\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: `No files matching: ${metaPattern}`,\n\t\t\t\tsuggestion: \"Run 'screenbook generate' to create screen.meta.ts files\",\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: \"Screen meta files\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `Found ${files.length} screen.meta.ts file${files.length > 1 ? \"s\" : \"\"}`,\n\t\t}\n\t} catch (error) {\n\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\t\treturn {\n\t\t\tname: \"Screen meta files\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `Invalid pattern: ${metaPattern} (${errorMessage})`,\n\t\t\tsuggestion: \"Check metaPattern in your config file\",\n\t\t}\n\t}\n}\n\nexport async function checkRoutesPattern(\n\tcwd: string,\n\troutesPattern: string | undefined,\n\tignore: string[],\n): Promise<CheckResult> {\n\tif (!routesPattern) {\n\t\treturn {\n\t\t\tname: \"Routes pattern\",\n\t\t\tstatus: \"warn\",\n\t\t\tmessage: \"routesPattern not configured\",\n\t\t\tsuggestion:\n\t\t\t\t\"Set routesPattern in config to enable 'lint' and 'generate' commands\",\n\t\t}\n\t}\n\n\ttry {\n\t\tconst files = await glob(routesPattern, { cwd, ignore })\n\n\t\tif (files.length === 0) {\n\t\t\treturn {\n\t\t\t\tname: \"Routes pattern\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: `No files matching: ${routesPattern}`,\n\t\t\t\tsuggestion: \"Check routesPattern in your config file\",\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: \"Routes pattern\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `Found ${files.length} route file${files.length > 1 ? \"s\" : \"\"}`,\n\t\t}\n\t} catch (error) {\n\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\t\treturn {\n\t\t\tname: \"Routes pattern\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `Invalid pattern: ${routesPattern} (${errorMessage})`,\n\t\t\tsuggestion: \"Check routesPattern in your config file\",\n\t\t}\n\t}\n}\n\nexport async function checkBuildOutput(\n\tcwd: string,\n\toutDir: string,\n): Promise<CheckResult> {\n\tconst screensJsonPath = join(cwd, outDir, \"screens.json\")\n\n\tif (!existsSync(screensJsonPath)) {\n\t\treturn {\n\t\t\tname: \"Build output\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `screens.json not found in ${outDir}/`,\n\t\t\tsuggestion: \"Run 'screenbook build' to generate metadata\",\n\t\t}\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(screensJsonPath, \"utf-8\")\n\t\tconst screens = JSON.parse(content) as unknown[]\n\n\t\treturn {\n\t\t\tname: \"Build output\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `screens.json contains ${screens.length} screen${screens.length > 1 ? \"s\" : \"\"}`,\n\t\t}\n\t} catch (error) {\n\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\t\treturn {\n\t\t\tname: \"Build output\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `screens.json is corrupted: ${errorMessage}`,\n\t\t\tsuggestion: \"Run 'screenbook build' to regenerate\",\n\t\t}\n\t}\n}\n\nexport async function checkVersionCompatibility(\n\tcwd: string,\n): Promise<CheckResult> {\n\tconst packageJsonPath = join(cwd, \"package.json\")\n\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn {\n\t\t\tname: \"Version compatibility\",\n\t\t\tstatus: \"warn\",\n\t\t\tmessage: \"Cannot check - package.json not found\",\n\t\t}\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(packageJsonPath, \"utf-8\")\n\t\tconst pkg = JSON.parse(content) as PackageJson\n\n\t\tconst allDeps = { ...pkg.dependencies, ...pkg.devDependencies }\n\t\tconst unifiedVersion = allDeps.screenbook\n\t\tconst coreVersion = allDeps[\"@screenbook/core\"]\n\t\tconst cliVersion = allDeps[\"@screenbook/cli\"]\n\n\t\t// Unified package - no compatibility check needed\n\t\tif (unifiedVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Version compatibility\",\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tmessage: \"Using unified screenbook package\",\n\t\t\t}\n\t\t}\n\n\t\tif (!coreVersion || !cliVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Version compatibility\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: \"Cannot check - packages not installed\",\n\t\t\t}\n\t\t}\n\n\t\t// Extract major version (handle ^, ~, etc.)\n\t\tconst extractMajor = (version: string): string => {\n\t\t\tconst cleaned = version.replace(/^[\\^~>=<]+/, \"\")\n\t\t\treturn cleaned.split(\".\")[0] ?? \"0\"\n\t\t}\n\n\t\tconst coreMajor = extractMajor(coreVersion)\n\t\tconst cliMajor = extractMajor(cliVersion)\n\n\t\tif (coreMajor !== cliMajor) {\n\t\t\treturn {\n\t\t\t\tname: \"Version compatibility\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: `Major version mismatch: core@${coreVersion} vs cli@${cliVersion}`,\n\t\t\t\tsuggestion: \"Update packages to matching major versions\",\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: \"Version compatibility\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: \"Package versions are compatible\",\n\t\t}\n\t} catch (error) {\n\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\t\treturn {\n\t\t\tname: \"Version compatibility\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `Failed to read package.json: ${errorMessage}`,\n\t\t\tsuggestion: \"Ensure package.json is valid JSON\",\n\t\t}\n\t}\n}\n\nexport async function checkGitRepository(cwd: string): Promise<CheckResult> {\n\tconst gitDir = join(cwd, \".git\")\n\n\tif (!existsSync(gitDir)) {\n\t\treturn {\n\t\t\tname: \"Git repository\",\n\t\t\tstatus: \"warn\",\n\t\t\tmessage: \"Not a git repository\",\n\t\t\tsuggestion:\n\t\t\t\t\"Run 'git init' to enable 'pr-impact' command for PR analysis\",\n\t\t}\n\t}\n\n\treturn {\n\t\tname: \"Git repository\",\n\t\tstatus: \"pass\",\n\t\tmessage: \"Git repository detected\",\n\t}\n}\n\nfunction displayResults(results: CheckResult[], verbose: boolean): void {\n\tlet passCount = 0\n\tlet failCount = 0\n\tlet warnCount = 0\n\n\tfor (const result of results) {\n\t\tconst icon =\n\t\t\tresult.status === \"pass\"\n\t\t\t\t? logger.green(\"✓\")\n\t\t\t\t: result.status === \"fail\"\n\t\t\t\t\t? logger.red(\"✗\")\n\t\t\t\t\t: logger.yellow(\"⚠\")\n\n\t\tconst statusColor =\n\t\t\tresult.status === \"pass\"\n\t\t\t\t? logger.green\n\t\t\t\t: result.status === \"fail\"\n\t\t\t\t\t? logger.red\n\t\t\t\t\t: logger.yellow\n\n\t\tlogger.log(`${icon} ${statusColor(result.name)}: ${result.message}`)\n\n\t\tif (result.suggestion && (result.status !== \"pass\" || verbose)) {\n\t\t\tlogger.log(` ${logger.dim(\"→\")} ${result.suggestion}`)\n\t\t}\n\n\t\tif (result.status === \"pass\") passCount++\n\t\telse if (result.status === \"fail\") failCount++\n\t\telse warnCount++\n\t}\n\n\tlogger.log(\"\")\n\n\tconst summary: string[] = []\n\tif (passCount > 0) summary.push(logger.green(`${passCount} passed`))\n\tif (failCount > 0) summary.push(logger.red(`${failCount} failed`))\n\tif (warnCount > 0) summary.push(logger.yellow(`${warnCount} warnings`))\n\n\tlogger.log(`Summary: ${summary.join(\", \")}`)\n\n\tif (failCount > 0) {\n\t\tlogger.log(\"\")\n\t\tlogger.log(\n\t\t\tlogger.dim(\"Run the suggested commands above to fix the issues.\"),\n\t\t)\n\t}\n}\n","import { resolve } from \"node:path\"\n\n/**\n * Supported router types for auto-detection.\n * Detection order: TanStack Router -> Solid Router -> Angular Router -> React Router -> Vue Router.\n */\nexport type RouterType =\n\t| \"react-router\"\n\t| \"vue-router\"\n\t| \"tanstack-router\"\n\t| \"solid-router\"\n\t| \"angular-router\"\n\t| \"unknown\"\n\n/**\n * Parsed route from router config (Vue Router, React Router, etc.)\n */\nexport interface ParsedRoute {\n\tpath: string\n\tname?: string\n\tcomponent?: string\n\tchildren?: ParsedRoute[]\n\tredirect?: string\n}\n\n/**\n * Flattened route with computed properties\n */\nexport interface FlatRoute {\n\t/** Full path including parent paths */\n\tfullPath: string\n\t/** Route name if defined */\n\tname?: string\n\t/** Component file path if available */\n\tcomponentPath?: string\n\t/** Computed screen ID from path */\n\tscreenId: string\n\t/** Computed screen title from path */\n\tscreenTitle: string\n\t/** Nesting depth */\n\tdepth: number\n}\n\n/**\n * Result of parsing a router config file\n */\nexport interface ParseResult {\n\troutes: ParsedRoute[]\n\twarnings: string[]\n}\n\n/**\n * Resolve relative import path to absolute path\n */\nexport function resolveImportPath(importPath: string, baseDir: string): string {\n\tif (importPath.startsWith(\".\")) {\n\t\treturn resolve(baseDir, importPath)\n\t}\n\treturn importPath\n}\n\n/**\n * Flatten nested routes into a flat list with computed properties\n */\nexport function flattenRoutes(\n\troutes: ParsedRoute[],\n\tparentPath = \"\",\n\tdepth = 0,\n): FlatRoute[] {\n\tconst result: FlatRoute[] = []\n\n\tfor (const route of routes) {\n\t\t// Skip redirect-only routes\n\t\tif (route.redirect && !route.component) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Compute full path\n\t\tlet fullPath: string\n\t\tif (route.path.startsWith(\"/\")) {\n\t\t\tfullPath = route.path\n\t\t} else if (parentPath === \"/\") {\n\t\t\tfullPath = `/${route.path}`\n\t\t} else {\n\t\t\tfullPath = parentPath ? `${parentPath}/${route.path}` : `/${route.path}`\n\t\t}\n\n\t\t// Normalize path\n\t\tfullPath = fullPath.replace(/\\/+/g, \"/\")\n\t\tif (fullPath !== \"/\" && fullPath.endsWith(\"/\")) {\n\t\t\tfullPath = fullPath.slice(0, -1)\n\t\t}\n\n\t\t// Handle empty path (index routes)\n\t\tif (fullPath === \"\") {\n\t\t\tfullPath = parentPath || \"/\"\n\t\t}\n\n\t\t// Only add routes with components (skip abstract parent routes)\n\t\tif (route.component || !route.children) {\n\t\t\tresult.push({\n\t\t\t\tfullPath,\n\t\t\t\tname: route.name,\n\t\t\t\tcomponentPath: route.component,\n\t\t\t\tscreenId: pathToScreenId(fullPath),\n\t\t\t\tscreenTitle: pathToScreenTitle(fullPath),\n\t\t\t\tdepth,\n\t\t\t})\n\t\t}\n\n\t\t// Process children\n\t\tif (route.children) {\n\t\t\tresult.push(...flattenRoutes(route.children, fullPath, depth + 1))\n\t\t}\n\t}\n\n\treturn result\n}\n\n/**\n * Convert route path to screen ID\n * /user/:id/profile -> user.id.profile\n */\nexport function pathToScreenId(path: string): string {\n\tif (path === \"/\" || path === \"\") {\n\t\treturn \"home\"\n\t}\n\n\treturn path\n\t\t.replace(/^\\//, \"\") // Remove leading slash\n\t\t.replace(/\\/$/, \"\") // Remove trailing slash\n\t\t.split(\"/\")\n\t\t.map((segment) => {\n\t\t\t// Convert :param to param\n\t\t\tif (segment.startsWith(\":\")) {\n\t\t\t\treturn segment.slice(1)\n\t\t\t}\n\t\t\t// Convert *catchall or ** to catchall\n\t\t\tif (segment.startsWith(\"*\")) {\n\t\t\t\t// Handle ** (Angular) as catchall\n\t\t\t\tif (segment === \"**\") {\n\t\t\t\t\treturn \"catchall\"\n\t\t\t\t}\n\t\t\t\treturn segment.slice(1) || \"catchall\"\n\t\t\t}\n\t\t\treturn segment\n\t\t})\n\t\t.join(\".\")\n}\n\n/**\n * Convert route path to screen title\n * /user/:id/profile -> Profile\n */\nexport function pathToScreenTitle(path: string): string {\n\tif (path === \"/\" || path === \"\") {\n\t\treturn \"Home\"\n\t}\n\n\tconst segments = path\n\t\t.replace(/^\\//, \"\")\n\t\t.replace(/\\/$/, \"\")\n\t\t.split(\"/\")\n\t\t.filter((s) => !s.startsWith(\":\") && !s.startsWith(\"*\"))\n\n\tconst lastSegment = segments[segments.length - 1] || \"Home\"\n\n\t// Convert kebab-case or snake_case to Title Case\n\treturn lastSegment\n\t\t.split(/[-_]/)\n\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n\t\t.join(\" \")\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { parse } from \"@babel/parser\"\nimport {\n\ttype ParsedRoute,\n\ttype ParseResult,\n\tresolveImportPath,\n} from \"./routeParserUtils.js\"\n\n// Re-export shared types\nexport type { ParsedRoute, ParseResult }\n\n/**\n * Parse Angular Router configuration file and extract routes.\n * Supports both Standalone (Angular 14+) and NgModule patterns.\n *\n * Supported patterns:\n * - `export const routes: Routes = [...]`\n * - `const routes: Routes = [...]`\n * - `RouterModule.forRoot([...])`\n * - `RouterModule.forChild([...])`\n * - `export default [...]`\n * - `export default [...] satisfies Routes`\n *\n * @param filePath - Path to the router configuration file\n * @param preloadedContent - Optional pre-read file content. When provided, the file is not read from disk,\n * enabling testing with virtual content or avoiding duplicate file reads.\n * @returns ParseResult containing extracted routes and any warnings\n * @throws Error if the file cannot be read or contains syntax errors\n */\nexport function parseAngularRouterConfig(\n\tfilePath: string,\n\tpreloadedContent?: string,\n): ParseResult {\n\tconst absolutePath = resolve(filePath)\n\tconst routesFileDir = dirname(absolutePath)\n\tconst warnings: string[] = []\n\n\t// Read file with proper error handling (skip if content is preloaded)\n\tlet content: string\n\tif (preloadedContent !== undefined) {\n\t\tcontent = preloadedContent\n\t} else {\n\t\ttry {\n\t\t\tcontent = readFileSync(absolutePath, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to read routes file \"${absolutePath}\": ${message}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t// Parse with Babel - wrap for better error messages\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", [\"decorators\", { decoratorsBeforeExport: true }]],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Syntax error in routes file \"${absolutePath}\": ${error.message}`,\n\t\t\t)\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tthrow new Error(`Failed to parse routes file \"${absolutePath}\": ${message}`)\n\t}\n\n\tconst routes: ParsedRoute[] = []\n\n\t// Find routes array in the AST\n\tfor (const node of ast.program.body) {\n\t\t// Handle: const routes: Routes = [...] or export const routes: Routes = [...]\n\t\tif (node.type === \"VariableDeclaration\") {\n\t\t\tfor (const decl of node.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\t// Check if it looks like a routes array (name contains \"route\" or has Routes type)\n\t\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\t\t\t\t\tconst typeAnnotation = (decl.id as any).typeAnnotation?.typeAnnotation\n\t\t\t\t\tconst isRoutesVariable =\n\t\t\t\t\t\tdecl.id.name.toLowerCase().includes(\"route\") ||\n\t\t\t\t\t\t(typeAnnotation?.type === \"TSTypeReference\" &&\n\t\t\t\t\t\t\ttypeAnnotation.typeName?.type === \"Identifier\" &&\n\t\t\t\t\t\t\ttypeAnnotation.typeName.name === \"Routes\")\n\t\t\t\t\tif (isRoutesVariable) {\n\t\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export const routes: Routes = [...]\n\t\tif (\n\t\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t\t) {\n\t\t\tfor (const decl of node.declaration.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\t\t\t\t\tconst typeAnnotation = (decl.id as any).typeAnnotation?.typeAnnotation\n\t\t\t\t\tconst isRoutesVariable =\n\t\t\t\t\t\tdecl.id.name.toLowerCase().includes(\"route\") ||\n\t\t\t\t\t\t(typeAnnotation?.type === \"TSTypeReference\" &&\n\t\t\t\t\t\t\ttypeAnnotation.typeName?.type === \"Identifier\" &&\n\t\t\t\t\t\t\ttypeAnnotation.typeName.name === \"Routes\")\n\t\t\t\t\tif (isRoutesVariable) {\n\t\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export default [...] (less common but possible)\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(node.declaration, routesFileDir, warnings)\n\t\t\troutes.push(...parsed)\n\t\t}\n\n\t\t// Handle: export default [...] satisfies Routes\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"TSSatisfiesExpression\" &&\n\t\t\tnode.declaration.expression.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(\n\t\t\t\tnode.declaration.expression,\n\t\t\t\troutesFileDir,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t\troutes.push(...parsed)\n\t\t}\n\n\t\t// Handle NgModule pattern: RouterModule.forRoot([...]) or RouterModule.forChild([...])\n\t\t// This can appear in @NgModule decorator or as a call expression\n\t\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\t\tlet classNode: any = null\n\t\tif (node.type === \"ClassDeclaration\") {\n\t\t\tclassNode = node\n\t\t} else if (\n\t\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: AST node types require type assertions\n\t\t\t(node as any).declaration?.type === \"ClassDeclaration\"\n\t\t) {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: AST node types require type assertions\n\t\t\tclassNode = (node as any).declaration\n\t\t}\n\n\t\tif (classNode) {\n\t\t\tconst decorators = classNode.decorators || []\n\t\t\tfor (const decorator of decorators) {\n\t\t\t\tif (decorator.expression?.type === \"CallExpression\") {\n\t\t\t\t\tconst routesFromDecorator = extractRoutesFromNgModule(\n\t\t\t\t\t\tdecorator.expression,\n\t\t\t\t\t\troutesFileDir,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t)\n\t\t\t\t\troutes.push(...routesFromDecorator)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle standalone RouterModule.forRoot/forChild calls in exports\n\t\tif (node.type === \"ExpressionStatement\") {\n\t\t\tconst routesFromExpr = extractRoutesFromExpression(\n\t\t\t\tnode.expression,\n\t\t\t\troutesFileDir,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t\troutes.push(...routesFromExpr)\n\t\t}\n\t}\n\n\t// Warn if no routes were found\n\tif (routes.length === 0) {\n\t\twarnings.push(\n\t\t\t\"No routes found. Supported patterns: 'export const routes: Routes = [...]', 'RouterModule.forRoot([...])', or 'RouterModule.forChild([...])'\",\n\t\t)\n\t}\n\n\treturn { routes, warnings }\n}\n\n/**\n * Extract routes from @NgModule decorator\n */\nfunction extractRoutesFromNgModule(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tcallExpr: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\t// Check if it's @NgModule({...})\n\tif (\n\t\tcallExpr.callee?.type === \"Identifier\" &&\n\t\tcallExpr.callee.name === \"NgModule\"\n\t) {\n\t\tconst arg = callExpr.arguments[0]\n\t\tif (arg?.type === \"ObjectExpression\") {\n\t\t\tfor (const prop of arg.properties) {\n\t\t\t\tif (\n\t\t\t\t\tprop.type === \"ObjectProperty\" &&\n\t\t\t\t\tprop.key?.type === \"Identifier\" &&\n\t\t\t\t\tprop.key.name === \"imports\"\n\t\t\t\t) {\n\t\t\t\t\tif (prop.value?.type === \"ArrayExpression\") {\n\t\t\t\t\t\tfor (const element of prop.value.elements) {\n\t\t\t\t\t\t\tif (!element) continue\n\t\t\t\t\t\t\tconst extracted = extractRoutesFromExpression(\n\t\t\t\t\t\t\t\telement,\n\t\t\t\t\t\t\t\tbaseDir,\n\t\t\t\t\t\t\t\twarnings,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\troutes.push(...extracted)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Extract routes from RouterModule.forRoot/forChild call expressions\n */\nfunction extractRoutesFromExpression(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\tif (node?.type !== \"CallExpression\") return routes\n\n\tconst callee = node.callee\n\tif (\n\t\tcallee?.type === \"MemberExpression\" &&\n\t\tcallee.object?.type === \"Identifier\" &&\n\t\tcallee.object.name === \"RouterModule\" &&\n\t\tcallee.property?.type === \"Identifier\" &&\n\t\t(callee.property.name === \"forRoot\" || callee.property.name === \"forChild\")\n\t) {\n\t\tconst routesArg = node.arguments[0]\n\t\tif (routesArg?.type === \"ArrayExpression\") {\n\t\t\tconst parsed = parseRoutesArray(routesArg, baseDir, warnings)\n\t\t\troutes.push(...parsed)\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Parse an array expression containing route objects\n */\nfunction parseRoutesArray(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tarrayNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const element of arrayNode.elements) {\n\t\tif (!element) continue\n\n\t\t// Handle spread elements\n\t\tif (element.type === \"SpreadElement\") {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Spread operator detected${loc}. Routes from spread cannot be statically analyzed.`,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (element.type === \"ObjectExpression\") {\n\t\t\tconst parsedRoute = parseRouteObject(element, baseDir, warnings)\n\t\t\tif (parsedRoute) {\n\t\t\t\troutes.push(parsedRoute)\n\t\t\t}\n\t\t} else {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Non-object route element (${element.type})${loc}. Only object literals can be statically analyzed.`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Parse a single route object expression\n */\nfunction parseRouteObject(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tobjectNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute | null {\n\tlet path: string | undefined\n\tlet component: string | undefined\n\tlet children: ParsedRoute[] | undefined\n\tlet redirectTo: string | undefined\n\tlet hasPath = false\n\n\tfor (const prop of objectNode.properties) {\n\t\tif (prop.type !== \"ObjectProperty\") continue\n\t\tif (prop.key.type !== \"Identifier\") continue\n\n\t\tconst key = prop.key.name\n\n\t\tswitch (key) {\n\t\t\tcase \"path\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\tpath = prop.value.value\n\t\t\t\t\thasPath = true\n\t\t\t\t} else {\n\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t`Dynamic path value (${prop.value.type})${loc}. Only string literal paths can be statically analyzed.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"component\":\n\t\t\t\t// Direct component reference: component: HomeComponent\n\t\t\t\tif (prop.value.type === \"Identifier\") {\n\t\t\t\t\tcomponent = prop.value.name\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"loadComponent\":\n\t\t\t\t// Lazy component: loadComponent: () => import('./path').then(m => m.Component)\n\t\t\t\tcomponent = extractLazyComponent(prop.value, baseDir, warnings)\n\t\t\t\tbreak\n\n\t\t\tcase \"loadChildren\": {\n\t\t\t\t// Lazy children: loadChildren: () => import('./path').then(m => m.routes)\n\t\t\t\t// We just note this for now - the actual routes are in another file\n\t\t\t\tconst lazyPath = extractLazyPath(prop.value, baseDir, warnings)\n\t\t\t\tif (lazyPath) {\n\t\t\t\t\tcomponent = `[lazy: ${lazyPath}]`\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase \"children\":\n\t\t\t\tif (prop.value.type === \"ArrayExpression\") {\n\t\t\t\t\tchildren = parseRoutesArray(prop.value, baseDir, warnings)\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"redirectTo\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\tredirectTo = prop.value.value\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\t// Skip these properties (not relevant for screen detection)\n\t\t\tcase \"pathMatch\":\n\t\t\tcase \"canActivate\":\n\t\t\tcase \"canDeactivate\":\n\t\t\tcase \"canMatch\":\n\t\t\tcase \"resolve\":\n\t\t\tcase \"data\":\n\t\t\tcase \"title\":\n\t\t\tcase \"providers\":\n\t\t\tcase \"runGuardsAndResolvers\":\n\t\t\tcase \"outlet\":\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t// Skip redirect-only routes (no component)\n\tif (redirectTo && !component && !children) {\n\t\treturn null\n\t}\n\n\t// Skip routes without path (unless they have children for layout purposes)\n\tif (!hasPath) {\n\t\tif (children && children.length > 0) {\n\t\t\treturn { path: \"\", component, children }\n\t\t}\n\t\treturn null\n\t}\n\n\treturn {\n\t\tpath: path || \"\",\n\t\tcomponent,\n\t\tchildren,\n\t}\n}\n\n/**\n * Extract component from lazy loadComponent pattern\n * loadComponent: () => import('./path').then(m => m.Component)\n */\nfunction extractLazyComponent(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Arrow function: () => import('./path').then(m => m.Component)\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\t// Handle .then(m => m.Component) chain\n\t\tif (\n\t\t\tbody.type === \"CallExpression\" &&\n\t\t\tbody.callee?.type === \"MemberExpression\" &&\n\t\t\tbody.callee.property?.type === \"Identifier\" &&\n\t\t\tbody.callee.property.name === \"then\"\n\t\t) {\n\t\t\tconst importCall = body.callee.object\n\t\t\tconst thenArg = body.arguments[0]\n\n\t\t\t// Check if it's an import() call\n\t\t\tif (\n\t\t\t\timportCall?.type === \"CallExpression\" &&\n\t\t\t\timportCall.callee?.type === \"Import\"\n\t\t\t) {\n\t\t\t\t// Check if the argument is a string literal\n\t\t\t\tif (importCall.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\t\tconst importPath = resolveImportPath(\n\t\t\t\t\t\timportCall.arguments[0].value,\n\t\t\t\t\t\tbaseDir,\n\t\t\t\t\t)\n\n\t\t\t\t\t// Extract component name from .then(m => m.Component)\n\t\t\t\t\tif (\n\t\t\t\t\t\tthenArg?.type === \"ArrowFunctionExpression\" &&\n\t\t\t\t\t\tthenArg.body?.type === \"MemberExpression\" &&\n\t\t\t\t\t\tthenArg.body.property?.type === \"Identifier\"\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn `${importPath}#${thenArg.body.property.name}`\n\t\t\t\t\t}\n\n\t\t\t\t\treturn importPath\n\t\t\t\t}\n\t\t\t\t// Dynamic import path - warn the user\n\t\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Lazy loadComponent with dynamic path${loc}. Only string literal imports can be analyzed.`,\n\t\t\t\t)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t}\n\n\t\t// Direct import without .then(): () => import('./path')\n\t\tif (body.type === \"CallExpression\" && body.callee?.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Lazy loadComponent with dynamic path${loc}. Only string literal imports can be analyzed.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\twarnings.push(\n\t\t`Unrecognized loadComponent pattern (${node.type})${loc}. Expected arrow function with import().then().`,\n\t)\n\treturn undefined\n}\n\n/**\n * Extract path from lazy loadChildren pattern\n * loadChildren: () => import('./path').then(m => m.routes)\n */\nfunction extractLazyPath(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\t// Handle .then() chain\n\t\tif (\n\t\t\tbody.type === \"CallExpression\" &&\n\t\t\tbody.callee?.type === \"MemberExpression\" &&\n\t\t\tbody.callee.property?.type === \"Identifier\" &&\n\t\t\tbody.callee.property.name === \"then\"\n\t\t) {\n\t\t\tconst importCall = body.callee.object\n\t\t\tif (\n\t\t\t\timportCall?.type === \"CallExpression\" &&\n\t\t\t\timportCall.callee?.type === \"Import\" &&\n\t\t\t\timportCall.arguments[0]?.type === \"StringLiteral\"\n\t\t\t) {\n\t\t\t\treturn resolveImportPath(importCall.arguments[0].value, baseDir)\n\t\t\t}\n\t\t}\n\n\t\t// Direct import without .then()\n\t\tif (body.type === \"CallExpression\" && body.callee?.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\twarnings.push(\n\t\t`Unrecognized loadChildren pattern (${node.type})${loc}. Expected arrow function with import().`,\n\t)\n\treturn undefined\n}\n\n/**\n * Detect if content is Angular Router based on patterns.\n * Checks for @angular/router import, RouterModule patterns, or Routes type annotation.\n */\nexport function isAngularRouterContent(content: string): boolean {\n\t// Check for Angular Router import\n\tif (content.includes(\"@angular/router\")) {\n\t\treturn true\n\t}\n\n\t// Check for RouterModule patterns\n\tif (\n\t\tcontent.includes(\"RouterModule.forRoot\") ||\n\t\tcontent.includes(\"RouterModule.forChild\")\n\t) {\n\t\treturn true\n\t}\n\n\t// Check for Routes type annotation: : Routes = or : Routes[\n\tif (/:\\s*Routes\\s*[=[]/.test(content)) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n","import { parse } from \"@babel/parser\"\nimport { pathToScreenId } from \"./routeParserUtils.js\"\n\n/**\n * Detected navigation target from a source file\n */\nexport interface DetectedNavigation {\n\t/** The target path (e.g., \"/users\", \"/billing/invoices/:id\") */\n\treadonly path: string\n\t/** Converted screen ID using pathToScreenId */\n\treadonly screenId: string\n\t/** Navigation method type */\n\treadonly type:\n\t\t| \"link\"\n\t\t| \"router-push\"\n\t\t| \"navigate\"\n\t\t| \"redirect\"\n\t\t| \"navigate-by-url\"\n\t/** Line number where detected */\n\treadonly line: number\n}\n\n/**\n * Factory function to create DetectedNavigation with proper screenId derivation.\n * Ensures the screenId is always correctly derived from the path.\n */\nexport function createDetectedNavigation(\n\tpath: string,\n\ttype: DetectedNavigation[\"type\"],\n\tline: number,\n): DetectedNavigation {\n\t// Strip query params and hash from path before converting to screenId\n\tconst pathWithoutQuery = path.split(\"?\")[0] ?? path\n\tconst cleanPath = pathWithoutQuery.split(\"#\")[0] ?? pathWithoutQuery\n\treturn {\n\t\tpath,\n\t\tscreenId: pathToScreenId(cleanPath),\n\t\ttype,\n\t\tline,\n\t}\n}\n\n/**\n * Result of analyzing a file for navigation\n */\nexport interface NavigationAnalysisResult {\n\t/** Detected navigation targets */\n\treadonly navigations: readonly DetectedNavigation[]\n\t/** Any warnings during analysis */\n\treadonly warnings: readonly string[]\n}\n\n/**\n * Result of analyzing a component for navigation.\n * Shared interface for framework-specific analyzers (Angular, Vue SFC, etc.)\n */\nexport interface ComponentAnalysisResult {\n\t/** Navigation targets detected in template */\n\treadonly templateNavigations: readonly DetectedNavigation[]\n\t/** Navigation targets detected in script */\n\treadonly scriptNavigations: readonly DetectedNavigation[]\n\t/** Any warnings during analysis */\n\treadonly warnings: readonly string[]\n}\n\n/**\n * Deduplicate navigations by screenId.\n * Exported for use by framework-specific analyzers.\n *\n * @param navigations - Array of detected navigations\n * @returns Array with duplicate screenIds removed (keeps first occurrence)\n */\nexport function deduplicateByScreenId(\n\tnavigations: DetectedNavigation[],\n): DetectedNavigation[] {\n\tconst seen = new Set<string>()\n\treturn navigations.filter((nav) => {\n\t\tif (seen.has(nav.screenId)) {\n\t\t\treturn false\n\t\t}\n\t\tseen.add(nav.screenId)\n\t\treturn true\n\t})\n}\n\n/**\n * Result of framework detection\n */\nexport interface FrameworkDetectionResult {\n\t/** Detected or default framework */\n\treadonly framework: NavigationFramework\n\t/** Whether the framework was explicitly detected (false means defaulted to Next.js) */\n\treadonly detected: boolean\n}\n\n/**\n * Navigation framework to detect\n */\nexport type NavigationFramework =\n\t| \"nextjs\"\n\t| \"react-router\"\n\t| \"vue-router\"\n\t| \"angular\"\n\t| \"solid-router\"\n\t| \"tanstack-router\"\n\n/**\n * Analyze a file's content for navigation patterns.\n *\n * Supports:\n * - Next.js: `<Link href=\"/path\">`, `router.push(\"/path\")`, `router.replace(\"/path\")`, `redirect(\"/path\")`\n * - React Router: `<Link to=\"/path\">`, `navigate(\"/path\")`\n * - Vue Router: `router.push(\"/path\")`, `router.replace(\"/path\")`, `router.push({ path: \"/path\" })`\n * - Angular: `router.navigate(['/path'])`, `router.navigateByUrl('/path')`\n * - Solid Router: `<A href=\"/path\">`, `navigate(\"/path\")`\n * - TanStack Router: `<Link to=\"/path\">`, `navigate({ to: \"/path\" })`\n *\n * @param content - The file content to analyze\n * @param framework - The navigation framework to detect\n * @returns Analysis result with detected navigations and warnings\n */\nexport function analyzeNavigation(\n\tcontent: string,\n\tframework: NavigationFramework,\n): NavigationAnalysisResult {\n\tconst navigations: DetectedNavigation[] = []\n\tconst warnings: string[] = []\n\n\t// Parse with Babel\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", \"jsx\", \"decorators-legacy\"],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\twarnings.push(`Syntax error during navigation analysis: ${error.message}`)\n\t\t\treturn { navigations, warnings }\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\twarnings.push(`Failed to parse file for navigation analysis: ${message}`)\n\t\treturn { navigations, warnings }\n\t}\n\n\t// Walk the AST with error handling\n\ttry {\n\t\twalkNode(ast.program, (node) => {\n\t\t\t// Detect JSX Link components\n\t\t\tif (node.type === \"JSXOpeningElement\") {\n\t\t\t\tconst linkNav = extractLinkNavigation(node, framework, warnings)\n\t\t\t\tif (linkNav) {\n\t\t\t\t\tnavigations.push(linkNav)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Detect call expressions (router.push, router.replace, navigate, redirect)\n\t\t\tif (node.type === \"CallExpression\") {\n\t\t\t\tconst callNav = extractCallNavigation(node, framework, warnings)\n\t\t\t\tif (callNav) {\n\t\t\t\t\tnavigations.push(callNav)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof RangeError) {\n\t\t\twarnings.push(\n\t\t\t\t`File too complex for navigation analysis (${error.message}). Consider simplifying the file structure.`,\n\t\t\t)\n\t\t\treturn { navigations, warnings }\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tconst errorType = error?.constructor?.name ?? \"Unknown\"\n\t\twarnings.push(`Unexpected ${errorType} during AST traversal: ${message}`)\n\t\treturn { navigations, warnings }\n\t}\n\n\t// Remove duplicates (same screenId)\n\tconst seen = new Set<string>()\n\tconst uniqueNavigations = navigations.filter((nav) => {\n\t\tif (seen.has(nav.screenId)) {\n\t\t\treturn false\n\t\t}\n\t\tseen.add(nav.screenId)\n\t\treturn true\n\t})\n\n\treturn { navigations: uniqueNavigations, warnings }\n}\n\n/**\n * Extract navigation from JSX Link component\n */\nfunction extractLinkNavigation(\n\t// biome-ignore lint/suspicious/noExplicitAny: Babel AST nodes have complex union types that are impractical to fully type\n\tnode: any,\n\tframework: NavigationFramework,\n\twarnings: string[],\n): DetectedNavigation | null {\n\t// Check if it's a Link component\n\tif (node.name?.type !== \"JSXIdentifier\") {\n\t\treturn null\n\t}\n\n\tconst componentName = node.name.name\n\t// Solid Router uses <A> component, others use <Link>\n\tif (componentName !== \"Link\" && componentName !== \"A\") {\n\t\treturn null\n\t}\n\n\t// Determine which attribute to look for based on framework\n\t// Next.js and Solid Router use href, React Router uses to\n\tconst attrName =\n\t\tframework === \"nextjs\" || framework === \"solid-router\" ? \"href\" : \"to\"\n\n\tfor (const attr of node.attributes || []) {\n\t\tif (\n\t\t\tattr.type === \"JSXAttribute\" &&\n\t\t\tattr.name?.type === \"JSXIdentifier\" &&\n\t\t\tattr.name.name === attrName\n\t\t) {\n\t\t\t// String literal: href=\"/path\"\n\t\t\tif (attr.value?.type === \"StringLiteral\") {\n\t\t\t\tconst path = attr.value.value\n\t\t\t\tif (isValidInternalPath(path)) {\n\t\t\t\t\treturn createDetectedNavigation(\n\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\"link\",\n\t\t\t\t\t\tnode.loc?.start.line ?? 0,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// JSX expression: href={\"/path\"} or href={`/path`}\n\t\t\tif (attr.value?.type === \"JSXExpressionContainer\") {\n\t\t\t\tconst expr = attr.value.expression\n\n\t\t\t\t// Simple string literal in expression\n\t\t\t\tif (expr.type === \"StringLiteral\") {\n\t\t\t\t\tconst path = expr.value\n\t\t\t\t\tif (isValidInternalPath(path)) {\n\t\t\t\t\t\treturn createDetectedNavigation(\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\"link\",\n\t\t\t\t\t\t\tnode.loc?.start.line ?? 0,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Template literal with no expressions (static template)\n\t\t\t\tif (\n\t\t\t\t\texpr.type === \"TemplateLiteral\" &&\n\t\t\t\t\texpr.expressions.length === 0 &&\n\t\t\t\t\texpr.quasis.length === 1\n\t\t\t\t) {\n\t\t\t\t\tconst path = expr.quasis[0].value.cooked\n\t\t\t\t\tif (path && isValidInternalPath(path)) {\n\t\t\t\t\t\treturn createDetectedNavigation(\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\"link\",\n\t\t\t\t\t\t\tnode.loc?.start.line ?? 0,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Dynamic expression - add warning with actionable guidance\n\t\t\t\tconst line = node.loc?.start.line ?? 0\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Dynamic ${componentName} ${attrName} at line ${line} cannot be statically analyzed. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null\n}\n\n/**\n * Extract navigation from function calls (router.push, router.replace, navigate, redirect)\n */\nfunction extractCallNavigation(\n\t// biome-ignore lint/suspicious/noExplicitAny: Babel AST nodes have complex union types that are impractical to fully type\n\tnode: any,\n\tframework: NavigationFramework,\n\twarnings: string[],\n): DetectedNavigation | null {\n\tconst callee = node.callee\n\n\t// router.push() or router.replace() - Next.js\n\tif (\n\t\tframework === \"nextjs\" &&\n\t\tcallee?.type === \"MemberExpression\" &&\n\t\tcallee.object?.type === \"Identifier\" &&\n\t\tcallee.object.name === \"router\" &&\n\t\tcallee.property?.type === \"Identifier\" &&\n\t\t(callee.property.name === \"push\" || callee.property.name === \"replace\")\n\t) {\n\t\treturn extractPathFromCallArgs(node, \"router-push\", warnings)\n\t}\n\n\t// router.push() or router.replace() - Vue Router\n\tif (\n\t\tframework === \"vue-router\" &&\n\t\tcallee?.type === \"MemberExpression\" &&\n\t\tcallee.object?.type === \"Identifier\" &&\n\t\tcallee.object.name === \"router\" &&\n\t\tcallee.property?.type === \"Identifier\" &&\n\t\t(callee.property.name === \"push\" || callee.property.name === \"replace\")\n\t) {\n\t\treturn extractPathFromCallArgs(node, \"router-push\", warnings)\n\t}\n\n\t// navigate() - React Router or Solid Router\n\tif (\n\t\t(framework === \"react-router\" || framework === \"solid-router\") &&\n\t\tcallee?.type === \"Identifier\" &&\n\t\tcallee.name === \"navigate\"\n\t) {\n\t\treturn extractPathFromCallArgs(node, \"navigate\", warnings)\n\t}\n\n\t// navigate({ to: '/path' }) - TanStack Router\n\tif (\n\t\tframework === \"tanstack-router\" &&\n\t\tcallee?.type === \"Identifier\" &&\n\t\tcallee.name === \"navigate\"\n\t) {\n\t\treturn extractPathFromObjectArg(node, \"navigate\", warnings, \"to\")\n\t}\n\n\t// redirect() - Next.js\n\tif (\n\t\tframework === \"nextjs\" &&\n\t\tcallee?.type === \"Identifier\" &&\n\t\tcallee.name === \"redirect\"\n\t) {\n\t\treturn extractPathFromCallArgs(node, \"redirect\", warnings)\n\t}\n\n\t// router.navigate() - Angular Router\n\t// Matches: this.router.navigate(['/path']) or router.navigate(['/path'])\n\tif (\n\t\tframework === \"angular\" &&\n\t\tcallee?.type === \"MemberExpression\" &&\n\t\tcallee.property?.type === \"Identifier\" &&\n\t\tcallee.property.name === \"navigate\"\n\t) {\n\t\tconst obj = callee.object\n\t\t// Match: this.router.navigate() or router.navigate()\n\t\tif (\n\t\t\t(obj?.type === \"MemberExpression\" &&\n\t\t\t\tobj.property?.type === \"Identifier\" &&\n\t\t\t\tobj.property.name === \"router\") ||\n\t\t\t(obj?.type === \"Identifier\" && obj.name === \"router\")\n\t\t) {\n\t\t\treturn extractPathFromArrayArg(node, \"navigate\", warnings)\n\t\t}\n\t}\n\n\t// router.navigateByUrl() - Angular Router\n\tif (\n\t\tframework === \"angular\" &&\n\t\tcallee?.type === \"MemberExpression\" &&\n\t\tcallee.property?.type === \"Identifier\" &&\n\t\tcallee.property.name === \"navigateByUrl\"\n\t) {\n\t\tconst obj = callee.object\n\t\tif (\n\t\t\t(obj?.type === \"MemberExpression\" &&\n\t\t\t\tobj.property?.type === \"Identifier\" &&\n\t\t\t\tobj.property.name === \"router\") ||\n\t\t\t(obj?.type === \"Identifier\" && obj.name === \"router\")\n\t\t) {\n\t\t\treturn extractPathFromCallArgs(node, \"navigate-by-url\", warnings)\n\t\t}\n\t}\n\n\treturn null\n}\n\n/**\n * Extract path from function call arguments\n */\nfunction extractPathFromCallArgs(\n\t// biome-ignore lint/suspicious/noExplicitAny: Babel AST nodes have complex union types that are impractical to fully type\n\tnode: any,\n\ttype: DetectedNavigation[\"type\"],\n\twarnings: string[],\n): DetectedNavigation | null {\n\tconst firstArg = node.arguments?.[0]\n\n\tif (!firstArg) {\n\t\treturn null\n\t}\n\n\t// String literal\n\tif (firstArg.type === \"StringLiteral\") {\n\t\tconst path = firstArg.value\n\t\tif (isValidInternalPath(path)) {\n\t\t\treturn createDetectedNavigation(path, type, node.loc?.start.line ?? 0)\n\t\t}\n\t}\n\n\t// Template literal with no expressions\n\tif (\n\t\tfirstArg.type === \"TemplateLiteral\" &&\n\t\tfirstArg.expressions.length === 0 &&\n\t\tfirstArg.quasis.length === 1\n\t) {\n\t\tconst path = firstArg.quasis[0].value.cooked\n\t\tif (path && isValidInternalPath(path)) {\n\t\t\treturn createDetectedNavigation(path, type, node.loc?.start.line ?? 0)\n\t\t}\n\t}\n\n\t// Object argument with path property (Vue Router style): { path: \"/users\" }\n\tif (firstArg.type === \"ObjectExpression\") {\n\t\tfor (const prop of firstArg.properties || []) {\n\t\t\tif (\n\t\t\t\tprop.type === \"ObjectProperty\" &&\n\t\t\t\tprop.key?.type === \"Identifier\" &&\n\t\t\t\tprop.key.name === \"path\"\n\t\t\t) {\n\t\t\t\t// String literal value\n\t\t\t\tif (prop.value?.type === \"StringLiteral\") {\n\t\t\t\t\tconst path = prop.value.value\n\t\t\t\t\tif (isValidInternalPath(path)) {\n\t\t\t\t\t\treturn createDetectedNavigation(\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\ttype,\n\t\t\t\t\t\t\tnode.loc?.start.line ?? 0,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Static template literal value\n\t\t\t\tif (\n\t\t\t\t\tprop.value?.type === \"TemplateLiteral\" &&\n\t\t\t\t\tprop.value.expressions.length === 0 &&\n\t\t\t\t\tprop.value.quasis.length === 1\n\t\t\t\t) {\n\t\t\t\t\tconst path = prop.value.quasis[0].value.cooked\n\t\t\t\t\tif (path && isValidInternalPath(path)) {\n\t\t\t\t\t\treturn createDetectedNavigation(\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\ttype,\n\t\t\t\t\t\t\tnode.loc?.start.line ?? 0,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Object without static path (named route or dynamic path)\n\t\tconst line = node.loc?.start.line ?? 0\n\t\twarnings.push(\n\t\t\t`Object-based navigation at line ${line} cannot be statically analyzed. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t\treturn null\n\t}\n\n\t// Dynamic argument - add warning with actionable guidance\n\tconst line = node.loc?.start.line ?? 0\n\twarnings.push(\n\t\t`Dynamic navigation path at line ${line} cannot be statically analyzed. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t)\n\n\treturn null\n}\n\n/**\n * Extract path from object argument with a specific property.\n *\n * @param node - The AST CallExpression node\n * @param type - The navigation type to assign\n * @param warnings - Array to collect warnings\n * @param propertyName - The property name to look for (e.g., \"to\" for TanStack Router)\n * @returns DetectedNavigation if a valid path is found, null otherwise\n *\n * @example\n * // TanStack Router: navigate({ to: '/users' }) -> extracts '/users'\n */\nfunction extractPathFromObjectArg(\n\t// biome-ignore lint/suspicious/noExplicitAny: Babel AST nodes have complex union types that are impractical to fully type\n\tnode: any,\n\ttype: DetectedNavigation[\"type\"],\n\twarnings: string[],\n\tpropertyName: string,\n): DetectedNavigation | null {\n\tconst firstArg = node.arguments?.[0]\n\n\tif (!firstArg) {\n\t\tconst line = node.loc?.start.line ?? 0\n\t\twarnings.push(\n\t\t\t`Navigation call at line ${line} has no arguments. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t\treturn null\n\t}\n\n\t// Object argument with specified property: { to: \"/users\" } or { path: \"/users\" }\n\tif (firstArg.type === \"ObjectExpression\") {\n\t\tfor (const prop of firstArg.properties || []) {\n\t\t\tif (\n\t\t\t\tprop.type === \"ObjectProperty\" &&\n\t\t\t\tprop.key?.type === \"Identifier\" &&\n\t\t\t\tprop.key.name === propertyName\n\t\t\t) {\n\t\t\t\t// String literal value\n\t\t\t\tif (prop.value?.type === \"StringLiteral\") {\n\t\t\t\t\tconst path = prop.value.value\n\t\t\t\t\tif (isValidInternalPath(path)) {\n\t\t\t\t\t\treturn createDetectedNavigation(\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\ttype,\n\t\t\t\t\t\t\tnode.loc?.start.line ?? 0,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Static template literal value\n\t\t\t\tif (\n\t\t\t\t\tprop.value?.type === \"TemplateLiteral\" &&\n\t\t\t\t\tprop.value.expressions.length === 0 &&\n\t\t\t\t\tprop.value.quasis.length === 1\n\t\t\t\t) {\n\t\t\t\t\tconst path = prop.value.quasis[0].value.cooked\n\t\t\t\t\tif (path && isValidInternalPath(path)) {\n\t\t\t\t\t\treturn createDetectedNavigation(\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\ttype,\n\t\t\t\t\t\t\tnode.loc?.start.line ?? 0,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Object without static property (named route or dynamic path)\n\t\tconst line = node.loc?.start.line ?? 0\n\t\twarnings.push(\n\t\t\t`Object-based navigation at line ${line} cannot be statically analyzed. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t\treturn null\n\t}\n\n\t// Non-object argument - add warning with actionable guidance\n\tconst line = node.loc?.start.line ?? 0\n\twarnings.push(\n\t\t`navigate() at line ${line} expects an object argument with a '${propertyName}' property (e.g., navigate({ ${propertyName}: '/path' })). Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t)\n\n\treturn null\n}\n\n/**\n * Extract path from array argument (Angular Router style)\n * e.g., router.navigate(['/users', userId]) -> extracts '/users'\n */\nfunction extractPathFromArrayArg(\n\t// biome-ignore lint/suspicious/noExplicitAny: Babel AST nodes have complex union types that are impractical to fully type\n\tnode: any,\n\ttype: DetectedNavigation[\"type\"],\n\twarnings: string[],\n): DetectedNavigation | null {\n\tconst firstArg = node.arguments?.[0]\n\n\tif (!firstArg) {\n\t\t// No argument provided\n\t\tconst line = node.loc?.start.line ?? 0\n\t\twarnings.push(\n\t\t\t`Navigation call at line ${line} has no arguments. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t\treturn null\n\t}\n\n\t// Array expression: ['/path', ...]\n\tif (firstArg.type === \"ArrayExpression\") {\n\t\tif (firstArg.elements.length === 0) {\n\t\t\t// Empty array\n\t\t\tconst line = node.loc?.start.line ?? 0\n\t\t\twarnings.push(\n\t\t\t\t`Navigation call at line ${line} has an empty array. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t\t)\n\t\t\treturn null\n\t\t}\n\t\tconst firstElement = firstArg.elements[0]\n\n\t\t// String literal as first element\n\t\tif (firstElement?.type === \"StringLiteral\") {\n\t\t\tconst path = firstElement.value\n\t\t\tif (isValidInternalPath(path)) {\n\t\t\t\treturn createDetectedNavigation(path, type, node.loc?.start.line ?? 0)\n\t\t\t}\n\t\t}\n\n\t\t// Static template literal as first element\n\t\tif (\n\t\t\tfirstElement?.type === \"TemplateLiteral\" &&\n\t\t\tfirstElement.expressions.length === 0 &&\n\t\t\tfirstElement.quasis.length === 1\n\t\t) {\n\t\t\tconst path = firstElement.quasis[0].value.cooked\n\t\t\tif (path && isValidInternalPath(path)) {\n\t\t\t\treturn createDetectedNavigation(path, type, node.loc?.start.line ?? 0)\n\t\t\t}\n\t\t}\n\n\t\t// Dynamic first element\n\t\tconst line = node.loc?.start.line ?? 0\n\t\twarnings.push(\n\t\t\t`Dynamic navigation path at line ${line} cannot be statically analyzed. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t\treturn null\n\t}\n\n\t// Non-array argument (dynamic)\n\tconst line = node.loc?.start.line ?? 0\n\twarnings.push(\n\t\t`Dynamic navigation path at line ${line} cannot be statically analyzed. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t)\n\treturn null\n}\n\n/**\n * Check if a path is a valid internal path (not external URL or hash link)\n */\nexport function isValidInternalPath(path: string): boolean {\n\t// Skip external URLs\n\tif (\n\t\tpath.startsWith(\"http://\") ||\n\t\tpath.startsWith(\"https://\") ||\n\t\tpath.startsWith(\"//\")\n\t) {\n\t\treturn false\n\t}\n\t// Skip hash-only links\n\tif (path.startsWith(\"#\")) {\n\t\treturn false\n\t}\n\t// Skip mailto and tel links\n\tif (path.startsWith(\"mailto:\") || path.startsWith(\"tel:\")) {\n\t\treturn false\n\t}\n\t// Must start with /\n\treturn path.startsWith(\"/\")\n}\n\n/**\n * Walk AST node recursively\n */\nfunction walkNode(\n\t// biome-ignore lint/suspicious/noExplicitAny: Babel AST nodes have complex union types that are impractical to fully type\n\tnode: any,\n\t// biome-ignore lint/suspicious/noExplicitAny: Babel AST nodes have complex union types that are impractical to fully type\n\tcallback: (node: any) => void,\n): void {\n\tif (!node || typeof node !== \"object\") return\n\n\tcallback(node)\n\n\tfor (const key of Object.keys(node)) {\n\t\tconst child = node[key]\n\t\tif (Array.isArray(child)) {\n\t\t\tfor (const item of child) {\n\t\t\t\twalkNode(item, callback)\n\t\t\t}\n\t\t} else if (child && typeof child === \"object\" && child.type) {\n\t\t\twalkNode(child, callback)\n\t\t}\n\t}\n}\n\n/**\n * Merge detected navigation targets with existing `next` array.\n * Removes duplicates and preserves manual entries.\n *\n * @param existing - Existing next array (may include manual entries)\n * @param detected - Newly detected navigation targets\n * @returns Merged array without duplicates\n */\nexport function mergeNext(\n\texisting: string[],\n\tdetected: DetectedNavigation[],\n): string[] {\n\tconst merged = new Set(existing)\n\n\tfor (const nav of detected) {\n\t\tmerged.add(nav.screenId)\n\t}\n\n\treturn Array.from(merged).sort()\n}\n\n/**\n * Detect navigation framework from file content.\n * Returns both the framework and whether it was explicitly detected.\n */\nexport function detectNavigationFramework(\n\tcontent: string,\n): FrameworkDetectionResult {\n\t// Check for Next.js patterns\n\tif (\n\t\tcontent.includes(\"next/link\") ||\n\t\tcontent.includes(\"next/navigation\") ||\n\t\tcontent.includes(\"next/router\")\n\t) {\n\t\treturn { framework: \"nextjs\", detected: true }\n\t}\n\n\t// Check for Vue Router patterns\n\tif (content.includes(\"vue-router\")) {\n\t\treturn { framework: \"vue-router\", detected: true }\n\t}\n\n\t// Check for Angular Router patterns\n\tif (content.includes(\"@angular/router\")) {\n\t\treturn { framework: \"angular\", detected: true }\n\t}\n\n\t// Check for Solid Router patterns\n\tif (content.includes(\"@solidjs/router\")) {\n\t\treturn { framework: \"solid-router\", detected: true }\n\t}\n\n\t// Check for TanStack Router patterns\n\tif (content.includes(\"@tanstack/react-router\")) {\n\t\treturn { framework: \"tanstack-router\", detected: true }\n\t}\n\n\t// Check for React Router patterns\n\tif (\n\t\tcontent.includes(\"react-router\") ||\n\t\tcontent.includes(\"@remix-run/react\")\n\t) {\n\t\treturn { framework: \"react-router\", detected: true }\n\t}\n\n\t// Check for useNavigate (React Router)\n\tif (content.includes(\"useNavigate\")) {\n\t\treturn { framework: \"react-router\", detected: true }\n\t}\n\n\t// Default to Next.js (more common in file-based routing projects)\n\treturn { framework: \"nextjs\", detected: false }\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { Parser } from \"htmlparser2\"\nimport {\n\tanalyzeNavigation,\n\ttype ComponentAnalysisResult,\n\tcreateDetectedNavigation,\n\ttype DetectedNavigation,\n\tdeduplicateByScreenId,\n\tisValidInternalPath,\n} from \"./navigationAnalyzer.js\"\n\n/**\n * Result of analyzing an Angular component for navigation.\n * Type alias for the shared ComponentAnalysisResult.\n */\nexport type AngularComponentAnalysisResult = ComponentAnalysisResult\n\n/**\n * Analyze an Angular component for navigation patterns.\n *\n * Detects:\n * - Template: `<a routerLink=\"/path\">`, `<a [routerLink]=\"'/path'\">`, `<a [routerLink]=\"['/path']\">`\n * - Script: `router.navigate(['/path'])`, `router.navigateByUrl('/path')`\n *\n * Results are deduplicated by screenId, keeping the first occurrence.\n *\n * @param content - The Angular component content to analyze\n * @param filePath - The file path (used for error messages and resolving templateUrl)\n * @param _cwd - The current working directory (unused, kept for API consistency with other analyzers)\n * @returns Analysis result with detected navigations and warnings\n *\n * @throws Never - All errors are caught and converted to warnings\n */\nexport function analyzeAngularComponent(\n\tcontent: string,\n\tfilePath: string,\n\t_cwd: string,\n): AngularComponentAnalysisResult {\n\tconst templateNavigations: DetectedNavigation[] = []\n\tconst scriptNavigations: DetectedNavigation[] = []\n\tconst warnings: string[] = []\n\n\ttry {\n\t\t// Extract template content from @Component decorator\n\t\tconst templateResult = extractTemplateContent(content, filePath)\n\n\t\tif (templateResult.warning) {\n\t\t\twarnings.push(templateResult.warning)\n\t\t}\n\n\t\tif (templateResult.content) {\n\t\t\tconst templateNavs = analyzeTemplateHTML(templateResult.content, warnings)\n\t\t\ttemplateNavigations.push(...templateNavs)\n\t\t}\n\n\t\t// Analyze script with existing navigation analyzer\n\t\tconst scriptResult = analyzeNavigation(content, \"angular\")\n\t\tscriptNavigations.push(...scriptResult.navigations)\n\t\twarnings.push(...scriptResult.warnings)\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\twarnings.push(`Syntax error in ${filePath}: ${error.message}`)\n\t\t} else if (error instanceof RangeError) {\n\t\t\twarnings.push(\n\t\t\t\t`${filePath}: File too complex for navigation analysis. Consider simplifying the component structure.`,\n\t\t\t)\n\t\t} else {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\twarnings.push(\n\t\t\t\t`${filePath}: Unexpected error during analysis: ${message}. Please report this as a bug.`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn {\n\t\ttemplateNavigations: deduplicateByScreenId(templateNavigations),\n\t\tscriptNavigations: deduplicateByScreenId(scriptNavigations),\n\t\twarnings,\n\t}\n}\n\n/**\n * Result of template extraction.\n */\ninterface TemplateExtractionResult {\n\t/** The extracted template content, or null if not found */\n\tcontent: string | null\n\t/** Warning message if extraction failed */\n\twarning: string | null\n}\n\n/**\n * Extract template content from @Component decorator.\n *\n * Handles both inline `template` and external `templateUrl` properties.\n * Uses regex-based extraction for reliability with Angular's decorator syntax.\n *\n * Limitations:\n * - Does not handle templates containing unescaped backticks\n * - Does not handle escaped quotes within template strings\n * - Returns warning if templateUrl file cannot be read\n *\n * @param content - The component TypeScript content\n * @param filePath - The file path for resolving relative templateUrl\n * @returns Extraction result with content and optional warning\n */\nfunction extractTemplateContent(\n\tcontent: string,\n\tfilePath: string,\n): TemplateExtractionResult {\n\t// Try to extract inline template using regex\n\t// Pattern 1: template: `...` (template literal)\n\t// Note: This simple pattern does not handle templates containing backticks\n\tconst backtickPattern = /template\\s*:\\s*`([^`]*)`/\n\tconst templateLiteralMatch = content.match(backtickPattern)\n\tif (templateLiteralMatch?.[1] !== undefined) {\n\t\treturn { content: templateLiteralMatch[1], warning: null }\n\t}\n\n\t// Pattern 2: template: '...' (single-quoted string)\n\tconst singleQuoteMatch = content.match(/template\\s*:\\s*'([^']*)'/)\n\tif (singleQuoteMatch?.[1] !== undefined) {\n\t\treturn { content: singleQuoteMatch[1], warning: null }\n\t}\n\n\t// Pattern 3: template: \"...\" (double-quoted string)\n\tconst doubleQuoteMatch = content.match(/template\\s*:\\s*\"([^\"]*)\"/)\n\tif (doubleQuoteMatch?.[1] !== undefined) {\n\t\treturn { content: doubleQuoteMatch[1], warning: null }\n\t}\n\n\t// Pattern 4: templateUrl: '...' or templateUrl: \"...\" (external file)\n\tconst templateUrlMatch = content.match(/templateUrl\\s*:\\s*['\"]([^'\"]+)['\"]/)\n\tif (templateUrlMatch?.[1]) {\n\t\tconst templatePath = resolve(dirname(filePath), templateUrlMatch[1])\n\t\ttry {\n\t\t\treturn { content: readFileSync(templatePath, \"utf-8\"), warning: null }\n\t\t} catch (error) {\n\t\t\tconst errorCode = (error as NodeJS.ErrnoException).code\n\t\t\tlet warning: string\n\n\t\t\tif (errorCode === \"ENOENT\") {\n\t\t\t\twarning =\n\t\t\t\t\t`Template file not found: ${templatePath}. ` +\n\t\t\t\t\t\"Navigation in this template will not be detected. \" +\n\t\t\t\t\t\"Add navigation targets manually to the 'next' field in screen.meta.ts.\"\n\t\t\t} else if (errorCode === \"EACCES\") {\n\t\t\t\twarning =\n\t\t\t\t\t`Permission denied reading template: ${templatePath}. ` +\n\t\t\t\t\t\"Check file permissions to enable template analysis.\"\n\t\t\t} else {\n\t\t\t\tconst msg = error instanceof Error ? error.message : String(error)\n\t\t\t\twarning = `Failed to read template file ${templatePath}: ${msg}`\n\t\t\t}\n\n\t\t\treturn { content: null, warning }\n\t\t}\n\t}\n\n\treturn { content: null, warning: null }\n}\n\n/**\n * Analyze HTML template for routerLink directives.\n *\n * Note: Line numbers are approximate as htmlparser2 only tracks newlines in text nodes,\n * not in tags or attributes. Line numbers may drift from actual positions.\n *\n * @param html - The HTML template content\n * @param warnings - Array to collect warnings (mutated)\n * @returns Array of detected navigation targets\n */\nfunction analyzeTemplateHTML(\n\thtml: string,\n\twarnings: string[],\n): DetectedNavigation[] {\n\tconst navigations: DetectedNavigation[] = []\n\tlet currentLine = 1\n\tconst parserErrors: string[] = []\n\n\tconst parser = new Parser({\n\t\tonopentag(_name, attribs) {\n\t\t\t// Note: htmlparser2 lowercases attribute names\n\t\t\t// Check for static routerLink (becomes routerlink)\n\t\t\tif (\"routerlink\" in attribs) {\n\t\t\t\tconst value = attribs.routerlink\n\t\t\t\tif (value) {\n\t\t\t\t\tif (isValidInternalPath(value)) {\n\t\t\t\t\t\tnavigations.push(\n\t\t\t\t\t\t\tcreateDetectedNavigation(value, \"link\", currentLine),\n\t\t\t\t\t\t)\n\t\t\t\t\t} else if (!value.startsWith(\"/\") && !value.includes(\"://\")) {\n\t\t\t\t\t\t// Looks like a relative path - warn user\n\t\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t\t`routerLink at line ~${currentLine} has relative path \"${value}\". ` +\n\t\t\t\t\t\t\t\t\"Angular routerLink paths should start with '/' for absolute routing. \" +\n\t\t\t\t\t\t\t\t\"Navigation will not be detected for this link.\",\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\t// External URLs are intentionally skipped without warning\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check for property binding [routerLink] (becomes [routerlink])\n\t\t\tif (\"[routerlink]\" in attribs) {\n\t\t\t\tconst expression = attribs[\"[routerlink]\"]\n\t\t\t\tconst nav = extractRouterLinkPath(expression, currentLine, warnings)\n\t\t\t\tif (nav) {\n\t\t\t\t\tnavigations.push(nav)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tontext(text) {\n\t\t\t// Track approximate line numbers by counting newlines in text nodes.\n\t\t\t// Note: Line numbers may be approximate as newlines in tags/attributes are not counted.\n\t\t\tcurrentLine += (text.match(/\\n/g) || []).length\n\t\t},\n\t\tonerror(error) {\n\t\t\tparserErrors.push(\n\t\t\t\t`HTML parsing error at line ~${currentLine}: ${error.message}. ` +\n\t\t\t\t\t\"Some navigation targets may not be detected.\",\n\t\t\t)\n\t\t},\n\t})\n\n\tparser.write(html)\n\tparser.end()\n\n\t// Add any parser errors to warnings\n\twarnings.push(...parserErrors)\n\n\treturn navigations\n}\n\n/**\n * Extract path from [routerLink] binding expression.\n *\n * Handles:\n * - String literals: `[routerLink]=\"'/path'\"` or `[routerLink]='\"/path\"'`\n * - Array literals: `[routerLink]=\"['/path']\"` or `[routerLink]=\"['/path', param]\"`\n * - Warns on dynamic expressions: `[routerLink]=\"dynamicPath\"`\n *\n * Note: Does not handle escaped quotes (e.g., \\' or \\\"). This is acceptable\n * because Angular routerLink paths should not contain quotes.\n *\n * @param expression - The binding expression value\n * @param line - Line number for warning messages (approximate)\n * @param warnings - Array to collect warnings (mutated)\n * @returns Detected navigation or null if path cannot be extracted\n */\nfunction extractRouterLinkPath(\n\texpression: string,\n\tline: number,\n\twarnings: string[],\n): DetectedNavigation | null {\n\tif (!expression) {\n\t\twarnings.push(\n\t\t\t`Empty [routerLink] binding at line ~${line}. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t\treturn null\n\t}\n\n\tconst trimmed = expression.trim()\n\n\t// Check for string literal: '/path' or \"/path\"\n\tif (\n\t\t(trimmed.startsWith(\"'\") && trimmed.endsWith(\"'\")) ||\n\t\t(trimmed.startsWith('\"') && trimmed.endsWith('\"'))\n\t) {\n\t\tconst path = trimmed.slice(1, -1)\n\t\tif (isValidInternalPath(path)) {\n\t\t\treturn createDetectedNavigation(path, \"link\", line)\n\t\t}\n\t\tif (!path.startsWith(\"/\") && !path.includes(\"://\")) {\n\t\t\twarnings.push(\n\t\t\t\t`[routerLink] at line ~${line} has relative path \"${path}\". ` +\n\t\t\t\t\t\"Angular routerLink paths should start with '/' for absolute routing. \" +\n\t\t\t\t\t\"Navigation will not be detected for this link.\",\n\t\t\t)\n\t\t}\n\t\treturn null\n\t}\n\n\t// Check for array literal: ['/path'] or ['/path', param]\n\tif (trimmed.startsWith(\"[\") && trimmed.endsWith(\"]\")) {\n\t\tconst arrayContent = trimmed.slice(1, -1).trim()\n\n\t\t// Find the first element (path) - handle comma separation\n\t\tconst firstComma = findFirstCommaOutsideQuotes(arrayContent)\n\t\tconst firstElement =\n\t\t\tfirstComma >= 0\n\t\t\t\t? arrayContent.slice(0, firstComma).trim()\n\t\t\t\t: arrayContent.trim()\n\n\t\t// Check if first element is a string literal\n\t\tif (\n\t\t\t(firstElement.startsWith(\"'\") && firstElement.endsWith(\"'\")) ||\n\t\t\t(firstElement.startsWith('\"') && firstElement.endsWith('\"'))\n\t\t) {\n\t\t\tconst path = firstElement.slice(1, -1)\n\t\t\tif (isValidInternalPath(path)) {\n\t\t\t\treturn createDetectedNavigation(path, \"link\", line)\n\t\t\t}\n\t\t\tif (!path.startsWith(\"/\") && !path.includes(\"://\")) {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`[routerLink] at line ~${line} has relative path \"${path}\". ` +\n\t\t\t\t\t\t\"Angular routerLink paths should start with '/' for absolute routing. \" +\n\t\t\t\t\t\t\"Navigation will not be detected for this link.\",\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\treturn null\n\t}\n\n\t// Dynamic expression - cannot be statically analyzed\n\twarnings.push(\n\t\t`Dynamic [routerLink] binding at line ~${line} cannot be statically analyzed. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t)\n\treturn null\n}\n\n/**\n * Find the index of the first comma outside of quotes.\n *\n * Note: Does not handle escaped quotes (e.g., \\' or \\\"). This is acceptable\n * because Angular routerLink paths should not contain quotes.\n *\n * @param str - The string to search\n * @returns Index of first comma, or -1 if not found\n */\nfunction findFirstCommaOutsideQuotes(str: string): number {\n\tlet inSingleQuote = false\n\tlet inDoubleQuote = false\n\n\tfor (let i = 0; i < str.length; i++) {\n\t\tconst char = str[i]\n\n\t\tif (char === \"'\" && !inDoubleQuote) {\n\t\t\tinSingleQuote = !inSingleQuote\n\t\t} else if (char === '\"' && !inSingleQuote) {\n\t\t\tinDoubleQuote = !inDoubleQuote\n\t\t} else if (char === \",\" && !inSingleQuote && !inDoubleQuote) {\n\t\t\treturn i\n\t\t}\n\t}\n\n\treturn -1\n}\n","import { parse } from \"@babel/parser\"\nimport type { ApiIntegrationConfig } from \"@screenbook/core\"\n\n/**\n * Detected API import from a source file\n */\nexport interface DetectedApiImport {\n\t/** The imported name (function, hook, or type) */\n\timportName: string\n\t/** The source package */\n\tpackageName: string\n\t/** Transformed name for dependsOn field (packageName/importName or custom) */\n\tdependsOnName: string\n\t/** Line number of the import */\n\tline: number\n}\n\n/**\n * Result of analyzing a file for API imports\n */\nexport interface ApiAnalysisResult {\n\t/** Detected API imports */\n\timports: DetectedApiImport[]\n\t/** Any warnings during analysis */\n\twarnings: string[]\n}\n\n/**\n * Analyze a file's content for API client imports.\n *\n * Supports:\n * - Named imports: `import { getUsers, createUser } from \"@api/client\"`\n * - Default imports: `import api from \"@api/client\"` (with warning)\n * - Namespace imports: `import * as api from \"@api/client\"` (with warning)\n *\n * @param content - The file content to analyze\n * @param config - API integration configuration\n * @returns Analysis result with detected imports and warnings\n */\nexport function analyzeApiImports(\n\tcontent: string,\n\tconfig: ApiIntegrationConfig,\n): ApiAnalysisResult {\n\tconst imports: DetectedApiImport[] = []\n\tconst warnings: string[] = []\n\n\t// Parse with Babel\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", \"jsx\"],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\twarnings.push(`Syntax error during import analysis: ${error.message}`)\n\t\t\treturn { imports, warnings }\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\twarnings.push(`Failed to parse file for import analysis: ${message}`)\n\t\treturn { imports, warnings }\n\t}\n\n\t// Normalize client packages for matching\n\tconst clientPackages = new Set(config.clientPackages)\n\n\t// Find import declarations\n\tfor (const node of ast.program.body) {\n\t\tif (node.type !== \"ImportDeclaration\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tconst source = node.source.value\n\t\tconst line = node.loc?.start.line ?? 0\n\n\t\t// Check if this import is from a configured client package\n\t\tif (!isMatchingPackage(source, clientPackages)) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Skip type-only imports\n\t\tif (node.importKind === \"type\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor (const specifier of node.specifiers) {\n\t\t\t// Skip type-only specifiers within a regular import\n\t\t\tif (\n\t\t\t\tspecifier.type === \"ImportSpecifier\" &&\n\t\t\t\tspecifier.importKind === \"type\"\n\t\t\t) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (specifier.type === \"ImportSpecifier\") {\n\t\t\t\t// Named import: import { getUsers } from \"@api/client\"\n\t\t\t\tconst importedName =\n\t\t\t\t\tspecifier.imported.type === \"Identifier\"\n\t\t\t\t\t\t? specifier.imported.name\n\t\t\t\t\t\t: specifier.imported.value\n\n\t\t\t\tconst transformedName = config.extractApiName\n\t\t\t\t\t? config.extractApiName(importedName)\n\t\t\t\t\t: importedName\n\n\t\t\t\timports.push({\n\t\t\t\t\timportName: importedName,\n\t\t\t\t\tpackageName: source,\n\t\t\t\t\tdependsOnName: `${source}/${transformedName}`,\n\t\t\t\t\tline,\n\t\t\t\t})\n\t\t\t} else if (specifier.type === \"ImportDefaultSpecifier\") {\n\t\t\t\t// Default import: import api from \"@api/client\"\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Default import from \"${source}\" at line ${line} cannot be statically analyzed. Consider using named imports.`,\n\t\t\t\t)\n\t\t\t} else if (specifier.type === \"ImportNamespaceSpecifier\") {\n\t\t\t\t// Namespace import: import * as api from \"@api/client\"\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Namespace import from \"${source}\" at line ${line} cannot be statically analyzed. Consider using named imports.`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { imports, warnings }\n}\n\n/**\n * Check if a source path matches any of the configured client packages.\n * Supports exact match and path prefix matching.\n */\nfunction isMatchingPackage(\n\tsource: string,\n\tclientPackages: Set<string>,\n): boolean {\n\t// Exact match\n\tif (clientPackages.has(source)) {\n\t\treturn true\n\t}\n\n\t// Check if source starts with any client package (for subpath imports)\n\t// e.g., \"@api/client\" should match \"@api/client/users\"\n\tfor (const pkg of clientPackages) {\n\t\tif (source.startsWith(`${pkg}/`)) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n/**\n * Merge detected API dependencies with existing ones.\n * Removes duplicates and preserves manual entries.\n *\n * @param existing - Existing dependsOn array (may include manual entries)\n * @param detected - Newly detected API dependencies\n * @returns Merged array without duplicates\n */\nexport function mergeDependsOn(\n\texisting: string[],\n\tdetected: DetectedApiImport[],\n): string[] {\n\tconst merged = new Set(existing)\n\n\tfor (const dep of detected) {\n\t\tmerged.add(dep.dependsOnName)\n\t}\n\n\treturn Array.from(merged).sort()\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { parse } from \"@babel/parser\"\nimport {\n\ttype ParsedRoute,\n\ttype ParseResult,\n\tresolveImportPath,\n} from \"./routeParserUtils.js\"\n\n// Re-export shared types\nexport type { ParsedRoute, ParseResult }\n\n/**\n * Parse Solid Router configuration file and extract routes.\n * Supports various export patterns including `export const routes`, `export default`,\n * and TypeScript's `satisfies` operator.\n *\n * @param filePath - Path to the router configuration file\n * @param preloadedContent - Optional pre-read file content to avoid duplicate file reads\n * @returns ParseResult containing extracted routes and any warnings\n * @throws Error if the file cannot be read or contains syntax errors\n */\nexport function parseSolidRouterConfig(\n\tfilePath: string,\n\tpreloadedContent?: string,\n): ParseResult {\n\tconst absolutePath = resolve(filePath)\n\tconst routesFileDir = dirname(absolutePath)\n\tconst warnings: string[] = []\n\n\t// Read file with proper error handling (skip if content is preloaded)\n\tlet content: string\n\tif (preloadedContent !== undefined) {\n\t\tcontent = preloadedContent\n\t} else {\n\t\ttry {\n\t\t\tcontent = readFileSync(absolutePath, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to read routes file \"${absolutePath}\": ${message}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t// Parse with Babel - wrap for better error messages\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", \"jsx\"],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Syntax error in routes file \"${absolutePath}\": ${error.message}`,\n\t\t\t)\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tthrow new Error(`Failed to parse routes file \"${absolutePath}\": ${message}`)\n\t}\n\n\tconst routes: ParsedRoute[] = []\n\n\t// Find routes array in the AST\n\tfor (const node of ast.program.body) {\n\t\t// Handle: const routes = [...]\n\t\tif (node.type === \"VariableDeclaration\") {\n\t\t\tfor (const decl of node.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export const routes = [...]\n\t\tif (\n\t\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t\t) {\n\t\t\tfor (const decl of node.declaration.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export default [...]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(node.declaration, routesFileDir, warnings)\n\t\t\troutes.push(...parsed)\n\t\t}\n\n\t\t// Handle: export default [...] satisfies RouteDefinition[]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"TSSatisfiesExpression\" &&\n\t\t\tnode.declaration.expression.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(\n\t\t\t\tnode.declaration.expression,\n\t\t\t\troutesFileDir,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t\troutes.push(...parsed)\n\t\t}\n\t}\n\n\t// Warn if no routes were found\n\tif (routes.length === 0) {\n\t\twarnings.push(\n\t\t\t\"No routes found. Supported patterns: 'export const routes = [...]' or 'export default [...]'\",\n\t\t)\n\t}\n\n\treturn { routes, warnings }\n}\n\n/**\n * Parse an array expression containing route objects\n */\nfunction parseRoutesArray(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tarrayNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const element of arrayNode.elements) {\n\t\tif (!element) continue\n\n\t\t// Handle spread elements\n\t\tif (element.type === \"SpreadElement\") {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Spread operator detected${loc}. Routes from spread cannot be statically analyzed.`,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (element.type === \"ObjectExpression\") {\n\t\t\tconst parsedRoutes = parseRouteObject(element, baseDir, warnings)\n\t\t\troutes.push(...parsedRoutes)\n\t\t} else {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Non-object route element (${element.type})${loc}. Only object literals can be statically analyzed.`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Parse a single route object expression\n * Returns array to handle multiple paths case: path: [\"/a\", \"/b\"]\n */\nfunction parseRouteObject(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tobjectNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tlet paths: string[] = []\n\tlet component: string | undefined\n\tlet children: ParsedRoute[] | undefined\n\tlet hasPath = false\n\n\tfor (const prop of objectNode.properties) {\n\t\tif (prop.type !== \"ObjectProperty\") continue\n\t\tif (prop.key.type !== \"Identifier\") continue\n\n\t\tconst key = prop.key.name\n\n\t\tswitch (key) {\n\t\t\tcase \"path\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\tpaths = [prop.value.value]\n\t\t\t\t\thasPath = true\n\t\t\t\t} else if (prop.value.type === \"ArrayExpression\") {\n\t\t\t\t\t// Solid Router supports path arrays: path: [\"/login\", \"/register\"]\n\t\t\t\t\tconst arrayElementCount = prop.value.elements.filter(Boolean).length\n\t\t\t\t\tpaths = extractPathArray(prop.value, warnings)\n\t\t\t\t\thasPath = paths.length > 0\n\t\t\t\t\t// Warn if path array had elements but none were extractable\n\t\t\t\t\tif (arrayElementCount > 0 && paths.length === 0) {\n\t\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t\t`Path array contains only dynamic values${loc}. No static paths could be extracted.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t`Dynamic path value (${prop.value.type})${loc}. Only string literal paths can be statically analyzed.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"component\":\n\t\t\t\tcomponent = extractComponent(prop.value, baseDir, warnings)\n\t\t\t\tbreak\n\n\t\t\tcase \"children\":\n\t\t\t\tif (prop.value.type === \"ArrayExpression\") {\n\t\t\t\t\tchildren = parseRoutesArray(prop.value, baseDir, warnings)\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\t// preload and matchFilters are ignored (not relevant for screen detection)\n\t\t}\n\t}\n\n\t// Skip routes without path (abstract layout wrappers)\n\tif (!hasPath) {\n\t\t// If it has children, process them with empty parent path contribution\n\t\tif (children && children.length > 0) {\n\t\t\treturn [{ path: \"\", component, children }]\n\t\t}\n\t\treturn []\n\t}\n\n\t// Create routes for each path (handles path arrays)\n\treturn paths.map((path) => ({\n\t\tpath,\n\t\tcomponent,\n\t\tchildren,\n\t}))\n}\n\n/**\n * Extract paths from array expression\n * path: [\"/login\", \"/register\"] -> [\"/login\", \"/register\"]\n */\nfunction extractPathArray(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tarrayNode: any,\n\twarnings: string[],\n): string[] {\n\tconst paths: string[] = []\n\n\tfor (const element of arrayNode.elements) {\n\t\tif (!element) continue\n\n\t\tif (element.type === \"StringLiteral\") {\n\t\t\tpaths.push(element.value)\n\t\t} else {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Non-string path in array (${element.type})${loc}. Only string literal paths can be analyzed.`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn paths\n}\n\n/**\n * Extract component from various patterns\n * - Direct identifier: component: Home\n * - Lazy component: component: lazy(() => import(\"./Home\"))\n */\nfunction extractComponent(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Direct component reference: component: Home\n\tif (node.type === \"Identifier\") {\n\t\treturn node.name\n\t}\n\n\t// Lazy component: component: lazy(() => import(\"./path\"))\n\tif (node.type === \"CallExpression\") {\n\t\tconst callee = node.callee\n\t\tif (callee.type === \"Identifier\" && callee.name === \"lazy\") {\n\t\t\tconst lazyArg = node.arguments[0]\n\t\t\tif (lazyArg) {\n\t\t\t\treturn extractLazyImportPath(lazyArg, baseDir, warnings)\n\t\t\t}\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`lazy() called without arguments${loc}. Expected arrow function with import().`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// Other call expressions not supported - add warning\n\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\tconst calleeName = callee.type === \"Identifier\" ? callee.name : \"unknown\"\n\t\twarnings.push(\n\t\t\t`Unrecognized component pattern: ${calleeName}(...)${loc}. Only 'lazy(() => import(...))' is supported.`,\n\t\t)\n\t\treturn undefined\n\t}\n\n\t// Arrow function component: component: () => <Home />\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tif (node.body.type === \"JSXElement\") {\n\t\t\tconst openingElement = node.body.openingElement\n\t\t\tif (openingElement?.name?.type === \"JSXIdentifier\") {\n\t\t\t\treturn openingElement.name.name\n\t\t\t}\n\t\t\t// JSXMemberExpression (namespaced components like <UI.Button />)\n\t\t\tif (openingElement?.name?.type === \"JSXMemberExpression\") {\n\t\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Namespaced JSX component (e.g., <UI.Button />)${loc}. Component extraction not supported for member expressions. Consider using a direct component reference or create a wrapper component.`,\n\t\t\t\t)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t}\n\t\t// Block body arrow functions\n\t\tif (node.body.type === \"BlockStatement\") {\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Arrow function with block body${loc}. Only concise arrow functions returning JSX directly can be analyzed.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// JSX Fragments\n\t\tif (node.body.type === \"JSXFragment\") {\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`JSX Fragment detected${loc}. Cannot extract component name from fragments.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// Conditional expressions\n\t\tif (node.body.type === \"ConditionalExpression\") {\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\t// Try to extract component names from both branches for context\n\t\t\tlet componentInfo = \"\"\n\t\t\tconst consequent = node.body.consequent\n\t\t\tconst alternate = node.body.alternate\n\t\t\tif (\n\t\t\t\tconsequent?.type === \"JSXElement\" &&\n\t\t\t\talternate?.type === \"JSXElement\"\n\t\t\t) {\n\t\t\t\tconst consName = consequent.openingElement?.name?.name || \"unknown\"\n\t\t\t\tconst altName = alternate.openingElement?.name?.name || \"unknown\"\n\t\t\t\tcomponentInfo = ` (${consName} or ${altName})`\n\t\t\t}\n\t\t\twarnings.push(\n\t\t\t\t`Conditional component${componentInfo}${loc}. Only static JSX elements can be analyzed. Consider extracting to a separate component.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// Unrecognized arrow function body\n\t\tconst arrowLoc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\twarnings.push(\n\t\t\t`Unrecognized arrow function body (${node.body.type})${arrowLoc}. Component will not be extracted.`,\n\t\t)\n\t\treturn undefined\n\t}\n\n\t// Catch-all for unrecognized component patterns\n\tif (node) {\n\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\twarnings.push(\n\t\t\t`Unrecognized component pattern (${node.type})${loc}. Component will not be extracted.`,\n\t\t)\n\t}\n\treturn undefined\n}\n\n/**\n * Extract import path from lazy function argument\n * () => import(\"./pages/Dashboard\") -> resolved path\n */\nfunction extractLazyImportPath(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Arrow function: () => import('./path')\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\tif (body.type === \"CallExpression\" && body.callee.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t\t// Dynamic import argument\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Lazy import with dynamic path${loc}. Only string literal imports can be analyzed.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\t// Unrecognized lazy pattern\n\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\twarnings.push(\n\t\t`Unrecognized lazy pattern (${node.type})${loc}. Expected arrow function with import().`,\n\t)\n\treturn undefined\n}\n\n/**\n * Detect if content is Solid Router based on patterns.\n * Note: Called by detectRouterType() in reactRouterParser.ts before React Router detection\n * because Solid Router and React Router share similar syntax patterns (both use `path` and `component`).\n * The detection order matters: TanStack Router -> Solid Router -> Angular Router -> React Router -> Vue Router.\n */\nexport function isSolidRouterContent(content: string): boolean {\n\t// Check for Solid Router specific import\n\tif (content.includes(\"@solidjs/router\")) {\n\t\treturn true\n\t}\n\n\t// Check for old package name\n\tif (content.includes(\"solid-app-router\")) {\n\t\treturn true\n\t}\n\n\t// Check for Solid.js lazy with route pattern\n\t// This is a weaker check, so we need to be more specific\n\tif (\n\t\tcontent.includes(\"solid-js\") &&\n\t\t/\\blazy\\s*\\(/.test(content) &&\n\t\t/\\bcomponent\\s*:/.test(content) &&\n\t\t/\\bpath\\s*:/.test(content)\n\t) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { parse } from \"@babel/parser\"\nimport {\n\ttype ParsedRoute,\n\ttype ParseResult,\n\tresolveImportPath,\n} from \"./routeParserUtils.js\"\n\n// Re-export shared types\nexport type { ParsedRoute, ParseResult }\n\n/**\n * Internal route definition collected from createRoute/createRootRoute calls\n */\ninterface RouteDefinition {\n\tvariableName: string\n\tpath?: string\n\tcomponent?: string\n\tparentVariableName?: string\n\tisRoot: boolean\n\tchildren?: string[] // Variable names of child routes from addChildren\n}\n\n/**\n * Parse TanStack Router configuration file and extract routes\n */\nexport function parseTanStackRouterConfig(\n\tfilePath: string,\n\tpreloadedContent?: string,\n): ParseResult {\n\tconst absolutePath = resolve(filePath)\n\tconst routesFileDir = dirname(absolutePath)\n\tconst warnings: string[] = []\n\n\t// Read file with proper error handling (skip if content is preloaded)\n\tlet content: string\n\tif (preloadedContent !== undefined) {\n\t\tcontent = preloadedContent\n\t} else {\n\t\ttry {\n\t\t\tcontent = readFileSync(absolutePath, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to read routes file \"${absolutePath}\": ${message}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t// Parse with Babel - wrap for better error messages\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", \"jsx\"],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Syntax error in routes file \"${absolutePath}\": ${error.message}`,\n\t\t\t)\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tthrow new Error(`Failed to parse routes file \"${absolutePath}\": ${message}`)\n\t}\n\n\t// Collect all route definitions from the AST\n\tconst routeMap = new Map<string, RouteDefinition>()\n\n\t// Two-pass AST processing is required because TanStack Router uses a function-based API\n\t// where routes are defined as variables and then composed using .addChildren().\n\t// Pass 1 must collect all route definitions first, so Pass 2 can resolve variable references.\n\n\t// First pass: collect all createRoute/createRootRoute calls\n\tfor (const node of ast.program.body) {\n\t\tcollectRouteDefinitions(node, routeMap, routesFileDir, warnings)\n\t}\n\n\t// Second pass: process addChildren calls to build parent-child relationships\n\tfor (const node of ast.program.body) {\n\t\tprocessAddChildrenCalls(node, routeMap, warnings)\n\t}\n\n\t// Build the route tree from collected definitions\n\tconst routes = buildRouteTree(routeMap, warnings)\n\n\t// Warn if no routes were found\n\tif (routes.length === 0) {\n\t\twarnings.push(\n\t\t\t\"No routes found. Supported patterns: 'createRootRoute()', 'createRoute()', and '.addChildren([...])'\",\n\t\t)\n\t}\n\n\treturn { routes, warnings }\n}\n\n/**\n * Collect route definitions from AST nodes\n */\nfunction collectRouteDefinitions(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\trouteMap: Map<string, RouteDefinition>,\n\tbaseDir: string,\n\twarnings: string[],\n): void {\n\t// Handle: const xxxRoute = createRoute({ ... }) or createRootRoute({ ... })\n\tif (node.type === \"VariableDeclaration\") {\n\t\tfor (const decl of node.declarations) {\n\t\t\tif (decl.id.type !== \"Identifier\") continue\n\n\t\t\tconst variableName = decl.id.name\n\n\t\t\t// Handle direct createRoute/createRootRoute call\n\t\t\tif (decl.init?.type === \"CallExpression\") {\n\t\t\t\tconst routeDef = extractRouteFromCallExpression(\n\t\t\t\t\tdecl.init,\n\t\t\t\t\tvariableName,\n\t\t\t\t\tbaseDir,\n\t\t\t\t\twarnings,\n\t\t\t\t)\n\t\t\t\tif (routeDef) {\n\t\t\t\t\trouteMap.set(variableName, routeDef)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle createRoute(...).lazy(...) pattern\n\t\t\tif (\n\t\t\t\tdecl.init?.type === \"CallExpression\" &&\n\t\t\t\tdecl.init.callee?.type === \"MemberExpression\" &&\n\t\t\t\tdecl.init.callee.property?.type === \"Identifier\" &&\n\t\t\t\tdecl.init.callee.property.name === \"lazy\"\n\t\t\t) {\n\t\t\t\t// The createRoute call is in callee.object\n\t\t\t\tconst createRouteCall = decl.init.callee.object\n\t\t\t\tif (createRouteCall?.type === \"CallExpression\") {\n\t\t\t\t\tconst routeDef = extractRouteFromCallExpression(\n\t\t\t\t\t\tcreateRouteCall,\n\t\t\t\t\t\tvariableName,\n\t\t\t\t\t\tbaseDir,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t)\n\t\t\t\t\tif (routeDef) {\n\t\t\t\t\t\t// Extract lazy import path\n\t\t\t\t\t\tconst lazyArg = decl.init.arguments[0]\n\t\t\t\t\t\tif (lazyArg) {\n\t\t\t\t\t\t\tconst lazyPath = extractLazyImportPath(lazyArg, baseDir, warnings)\n\t\t\t\t\t\t\tif (lazyPath) {\n\t\t\t\t\t\t\t\trouteDef.component = lazyPath\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\trouteMap.set(variableName, routeDef)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Handle: export const xxxRoute = createRoute({ ... })\n\tif (\n\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t) {\n\t\tfor (const decl of node.declaration.declarations) {\n\t\t\tif (decl.id.type !== \"Identifier\") continue\n\n\t\t\tconst variableName = decl.id.name\n\n\t\t\tif (decl.init?.type === \"CallExpression\") {\n\t\t\t\tconst routeDef = extractRouteFromCallExpression(\n\t\t\t\t\tdecl.init,\n\t\t\t\t\tvariableName,\n\t\t\t\t\tbaseDir,\n\t\t\t\t\twarnings,\n\t\t\t\t)\n\t\t\t\tif (routeDef) {\n\t\t\t\t\trouteMap.set(variableName, routeDef)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Extract route definition from a CallExpression (createRoute or createRootRoute)\n */\nfunction extractRouteFromCallExpression(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tcallNode: any,\n\tvariableName: string,\n\tbaseDir: string,\n\twarnings: string[],\n): RouteDefinition | null {\n\tconst callee = callNode.callee\n\n\t// Check if it's createRoute or createRootRoute\n\tlet isRoot = false\n\tlet optionsArg = callNode.arguments[0]\n\n\tif (callee.type === \"Identifier\") {\n\t\tif (callee.name === \"createRootRoute\") {\n\t\t\tisRoot = true\n\t\t} else if (callee.name === \"createRootRouteWithContext\") {\n\t\t\tisRoot = true\n\t\t} else if (callee.name !== \"createRoute\") {\n\t\t\treturn null\n\t\t}\n\t} else if (callee.type === \"CallExpression\") {\n\t\t// Handle curried form: createRootRouteWithContext<T>()({...})\n\t\tconst innerCallee = callee.callee\n\t\tif (\n\t\t\tinnerCallee?.type === \"Identifier\" &&\n\t\t\tinnerCallee.name === \"createRootRouteWithContext\"\n\t\t) {\n\t\t\tisRoot = true\n\t\t\t// Options are in the outer call's arguments\n\t\t\toptionsArg = callNode.arguments[0]\n\t\t} else {\n\t\t\treturn null\n\t\t}\n\t} else {\n\t\treturn null\n\t}\n\n\tconst routeDef: RouteDefinition = {\n\t\tvariableName,\n\t\tisRoot,\n\t}\n\tif (optionsArg?.type === \"ObjectExpression\") {\n\t\tfor (const prop of optionsArg.properties) {\n\t\t\tif (prop.type !== \"ObjectProperty\") continue\n\t\t\tif (prop.key.type !== \"Identifier\") continue\n\n\t\t\tconst key = prop.key.name\n\n\t\t\tswitch (key) {\n\t\t\t\tcase \"path\":\n\t\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\t\t// Normalize TanStack Router path: $param -> :param\n\t\t\t\t\t\trouteDef.path = normalizeTanStackPath(prop.value.value)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t\t`Dynamic path value (${prop.value.type})${loc}. Only string literal paths can be statically analyzed.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\n\t\t\t\tcase \"component\":\n\t\t\t\t\trouteDef.component = extractComponentValue(\n\t\t\t\t\t\tprop.value,\n\t\t\t\t\t\tbaseDir,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\n\t\t\t\tcase \"getParentRoute\":\n\t\t\t\t\t// Extract parent route variable name from arrow function\n\t\t\t\t\tif (prop.value.type === \"ArrowFunctionExpression\") {\n\t\t\t\t\t\tconst body = prop.value.body\n\t\t\t\t\t\tif (body.type === \"Identifier\") {\n\t\t\t\t\t\t\trouteDef.parentVariableName = body.name\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t\t\t`Dynamic getParentRoute${loc}. Only static route references can be analyzed.`,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\treturn routeDef\n}\n\n/**\n * Extract component value from different patterns\n * Returns undefined with a warning for unrecognized patterns\n */\nfunction extractComponentValue(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Direct component reference: component: Home\n\tif (node.type === \"Identifier\") {\n\t\treturn node.name\n\t}\n\n\t// lazyRouteComponent(() => import('./path'))\n\tif (node.type === \"CallExpression\") {\n\t\tconst callee = node.callee\n\t\tif (callee.type === \"Identifier\" && callee.name === \"lazyRouteComponent\") {\n\t\t\tconst importArg = node.arguments[0]\n\t\t\tif (importArg) {\n\t\t\t\treturn extractLazyImportPath(importArg, baseDir, warnings)\n\t\t\t}\n\t\t\t// lazyRouteComponent called without arguments\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`lazyRouteComponent called without arguments${loc}. Expected arrow function with import().`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// Other call expressions are not supported\n\t\treturn undefined\n\t}\n\n\t// Arrow function component: component: () => <Home />\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\t// Check if body is JSX\n\t\tif (node.body.type === \"JSXElement\") {\n\t\t\tconst openingElement = node.body.openingElement\n\t\t\tif (openingElement?.name?.type === \"JSXIdentifier\") {\n\t\t\t\treturn openingElement.name.name\n\t\t\t}\n\t\t\t// Handle JSXMemberExpression like <Namespace.Component />\n\t\t\tif (openingElement?.name?.type === \"JSXMemberExpression\") {\n\t\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Namespaced JSX component${loc}. Component extraction not fully supported for member expressions.`,\n\t\t\t\t)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t}\n\t\t// Block body arrow functions: () => { return <Home /> }\n\t\tif (node.body.type === \"BlockStatement\") {\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Arrow function with block body${loc}. Only concise arrow functions returning JSX directly can be analyzed.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// JSX Fragment: () => <>...</>\n\t\tif (node.body.type === \"JSXFragment\") {\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`JSX Fragment detected${loc}. Cannot extract component name from fragments.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\treturn undefined\n}\n\n/**\n * Extract import path from lazy function patterns\n */\nfunction extractLazyImportPath(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Arrow function: () => import('./path')\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\t// Direct import: () => import('./path')\n\t\tif (body.type === \"CallExpression\" && body.callee.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t}\n\n\t\t// Chained: () => import('./path').then(d => d.Route)\n\t\tif (\n\t\t\tbody.type === \"CallExpression\" &&\n\t\t\tbody.callee.type === \"MemberExpression\" &&\n\t\t\tbody.callee.object?.type === \"CallExpression\" &&\n\t\t\tbody.callee.object.callee?.type === \"Import\"\n\t\t) {\n\t\t\tconst importCall = body.callee.object\n\t\t\tif (importCall.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(importCall.arguments[0].value, baseDir)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\twarnings.push(\n\t\t`Unrecognized lazy pattern (${node.type})${loc}. Expected arrow function with import().`,\n\t)\n\treturn undefined\n}\n\n/**\n * Process addChildren calls to establish parent-child relationships\n */\nfunction processAddChildrenCalls(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\trouteMap: Map<string, RouteDefinition>,\n\twarnings: string[],\n): void {\n\t// Handle: const routeTree = rootRoute.addChildren([...])\n\t// or: const routeTree = rootRoute.addChildren([indexRoute, aboutRoute.addChildren([...])])\n\tif (node.type === \"VariableDeclaration\") {\n\t\tfor (const decl of node.declarations) {\n\t\t\tprocessAddChildrenExpression(decl.init, routeMap, warnings)\n\t\t}\n\t}\n\n\t// Handle: export const routeTree = rootRoute.addChildren([...])\n\tif (\n\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t) {\n\t\tfor (const decl of node.declaration.declarations) {\n\t\t\tprocessAddChildrenExpression(decl.init, routeMap, warnings)\n\t\t}\n\t}\n}\n\n/**\n * Recursively process addChildren expressions\n */\nfunction processAddChildrenExpression(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\texpr: any,\n\trouteMap: Map<string, RouteDefinition>,\n\twarnings: string[],\n): string | undefined {\n\tif (!expr) return undefined\n\n\t// Handle: parentRoute.addChildren([...])\n\tif (\n\t\texpr.type === \"CallExpression\" &&\n\t\texpr.callee?.type === \"MemberExpression\" &&\n\t\texpr.callee.property?.type === \"Identifier\" &&\n\t\texpr.callee.property.name === \"addChildren\"\n\t) {\n\t\t// Get parent route variable name\n\t\tlet parentVarName: string | undefined\n\t\tif (expr.callee.object?.type === \"Identifier\") {\n\t\t\tparentVarName = expr.callee.object.name\n\t\t} else if (expr.callee.object?.type === \"CallExpression\") {\n\t\t\t// Nested addChildren: parentRoute.addChildren([...]).addChildren([...])\n\t\t\t// This is rare but handle recursively\n\t\t\tparentVarName = processAddChildrenExpression(\n\t\t\t\texpr.callee.object,\n\t\t\t\trouteMap,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t}\n\n\t\tif (!parentVarName) return undefined\n\n\t\tconst parentDef = routeMap.get(parentVarName)\n\t\tif (!parentDef) {\n\t\t\tconst loc = expr.loc ? ` at line ${expr.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Parent route \"${parentVarName}\" not found${loc}. Ensure it's defined with createRoute/createRootRoute.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\n\t\t// Process children array\n\t\tconst childrenArg = expr.arguments[0]\n\t\tif (childrenArg?.type === \"ArrayExpression\") {\n\t\t\tconst childNames: string[] = []\n\n\t\t\tfor (const element of childrenArg.elements) {\n\t\t\t\tif (!element) continue\n\n\t\t\t\t// Direct reference: indexRoute\n\t\t\t\tif (element.type === \"Identifier\") {\n\t\t\t\t\tchildNames.push(element.name)\n\t\t\t\t}\n\t\t\t\t// Nested addChildren: aboutRoute.addChildren([...])\n\t\t\t\telse if (element.type === \"CallExpression\") {\n\t\t\t\t\tconst nestedParent = processAddChildrenExpression(\n\t\t\t\t\t\telement,\n\t\t\t\t\t\trouteMap,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t)\n\t\t\t\t\tif (nestedParent) {\n\t\t\t\t\t\tchildNames.push(nestedParent)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Spread operator\n\t\t\t\telse if (element.type === \"SpreadElement\") {\n\t\t\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t`Spread operator detected${loc}. Routes from spread cannot be statically analyzed.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tparentDef.children = childNames\n\t\t}\n\n\t\treturn parentVarName\n\t}\n\n\treturn undefined\n}\n\n/**\n * Build the route tree from collected definitions\n */\nfunction buildRouteTree(\n\trouteMap: Map<string, RouteDefinition>,\n\twarnings: string[],\n): ParsedRoute[] {\n\t// Find root routes\n\tconst rootDefs = Array.from(routeMap.values()).filter((def) => def.isRoot)\n\n\tif (rootDefs.length === 0) {\n\t\t// No explicit root route, try to build from parent relationships\n\t\treturn buildTreeFromParentRelations(routeMap, warnings)\n\t}\n\n\t// Build tree starting from root routes\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const rootDef of rootDefs) {\n\t\t// Use a Set to track visited routes and detect circular references\n\t\tconst visited = new Set<string>()\n\t\tconst rootRoute = buildRouteFromDefinition(\n\t\t\trootDef,\n\t\t\trouteMap,\n\t\t\twarnings,\n\t\t\tvisited,\n\t\t)\n\t\tif (rootRoute) {\n\t\t\t// If root has children, return only the children (root is typically just a layout)\n\t\t\t// because the root route in TanStack Router serves as a layout wrapper\n\t\t\tif (rootRoute.children && rootRoute.children.length > 0) {\n\t\t\t\troutes.push(...rootRoute.children)\n\t\t\t} else if (rootRoute.path) {\n\t\t\t\troutes.push(rootRoute)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Build tree when there's no explicit root route\n * Falls back to finding routes that have no parent relationship defined\n */\nfunction buildTreeFromParentRelations(\n\trouteMap: Map<string, RouteDefinition>,\n\twarnings: string[],\n): ParsedRoute[] {\n\t// Find routes without parents (top-level routes)\n\tconst topLevelDefs = Array.from(routeMap.values()).filter(\n\t\t(def) => !def.parentVariableName && !def.isRoot,\n\t)\n\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const def of topLevelDefs) {\n\t\tconst visited = new Set<string>()\n\t\tconst route = buildRouteFromDefinition(def, routeMap, warnings, visited)\n\t\tif (route) {\n\t\t\troutes.push(route)\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Build a ParsedRoute from a RouteDefinition\n * @param visited - Set of visited variable names for circular reference detection\n */\nfunction buildRouteFromDefinition(\n\tdef: RouteDefinition,\n\trouteMap: Map<string, RouteDefinition>,\n\twarnings: string[],\n\tvisited: Set<string>,\n): ParsedRoute | null {\n\t// Circular reference detection\n\tif (visited.has(def.variableName)) {\n\t\twarnings.push(\n\t\t\t`Circular reference detected: route \"${def.variableName}\" references itself in the route tree.`,\n\t\t)\n\t\treturn null\n\t}\n\tvisited.add(def.variableName)\n\n\tconst route: ParsedRoute = {\n\t\tpath: def.path ?? \"\",\n\t\tcomponent: def.component,\n\t}\n\n\t// Process children\n\tif (def.children && def.children.length > 0) {\n\t\tconst children: ParsedRoute[] = []\n\t\tfor (const childName of def.children) {\n\t\t\tconst childDef = routeMap.get(childName)\n\t\t\tif (childDef) {\n\t\t\t\tconst childRoute = buildRouteFromDefinition(\n\t\t\t\t\tchildDef,\n\t\t\t\t\trouteMap,\n\t\t\t\t\twarnings,\n\t\t\t\t\tvisited,\n\t\t\t\t)\n\t\t\t\tif (childRoute) {\n\t\t\t\t\tchildren.push(childRoute)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Child route \"${childName}\" not found. Ensure it's defined with createRoute.`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tif (children.length > 0) {\n\t\t\troute.children = children\n\t\t}\n\t}\n\n\treturn route\n}\n\n/**\n * Normalize TanStack Router path syntax to standard format\n * /$ (trailing catch-all) -> /*\n * $ (standalone splat) -> *\n * $param (dynamic segment) -> :param\n */\nfunction normalizeTanStackPath(path: string): string {\n\treturn (\n\t\tpath\n\t\t\t// Convert catch-all: $ at end -> *\n\t\t\t.replace(/\\/\\$$/, \"/*\")\n\t\t\t// Convert single $ to * (splat route)\n\t\t\t.replace(/^\\$$/, \"*\")\n\t\t\t// Convert $param to :param\n\t\t\t.replace(/\\$([a-zA-Z_][a-zA-Z0-9_]*)/g, \":$1\")\n\t)\n}\n\n/**\n * Detect if content is TanStack Router based on patterns\n */\nexport function isTanStackRouterContent(content: string): boolean {\n\t// Check for TanStack Router specific imports\n\tif (content.includes(\"@tanstack/react-router\")) {\n\t\treturn true\n\t}\n\n\t// Check for createRootRoute pattern\n\tif (content.includes(\"createRootRoute\")) {\n\t\treturn true\n\t}\n\n\t// Check for createRoute with getParentRoute (TanStack Router specific)\n\tif (content.includes(\"createRoute\") && content.includes(\"getParentRoute\")) {\n\t\treturn true\n\t}\n\n\t// Check for lazyRouteComponent (TanStack Router specific)\n\tif (content.includes(\"lazyRouteComponent\")) {\n\t\treturn true\n\t}\n\n\t// Check for addChildren pattern (TanStack Router specific)\n\tif (/\\.addChildren\\s*\\(/.test(content)) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { parse } from \"@babel/parser\"\nimport { isAngularRouterContent } from \"./angularRouterParser.js\"\nimport {\n\ttype ParsedRoute,\n\ttype ParseResult,\n\ttype RouterType,\n\tresolveImportPath,\n} from \"./routeParserUtils.js\"\nimport { isSolidRouterContent } from \"./solidRouterParser.js\"\nimport { isTanStackRouterContent } from \"./tanstackRouterParser.js\"\n\n// Re-export shared types\nexport type { ParsedRoute, ParseResult, RouterType }\n// Re-export router detection for convenience\nexport { isAngularRouterContent, isSolidRouterContent, isTanStackRouterContent }\n\n/**\n * Router factory function names to detect\n */\nconst ROUTER_FACTORY_NAMES = [\n\t\"createBrowserRouter\",\n\t\"createHashRouter\",\n\t\"createMemoryRouter\",\n]\n\n/**\n * Parse React Router configuration file and extract routes\n */\nexport function parseReactRouterConfig(\n\tfilePath: string,\n\tpreloadedContent?: string,\n): ParseResult {\n\tconst absolutePath = resolve(filePath)\n\tconst routesFileDir = dirname(absolutePath)\n\tconst warnings: string[] = []\n\n\t// Read file with proper error handling (skip if content is preloaded)\n\tlet content: string\n\tif (preloadedContent !== undefined) {\n\t\tcontent = preloadedContent\n\t} else {\n\t\ttry {\n\t\t\tcontent = readFileSync(absolutePath, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to read routes file \"${absolutePath}\": ${message}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t// Parse with Babel - wrap for better error messages\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", \"jsx\"],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Syntax error in routes file \"${absolutePath}\": ${error.message}`,\n\t\t\t)\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tthrow new Error(`Failed to parse routes file \"${absolutePath}\": ${message}`)\n\t}\n\n\tconst routes: ParsedRoute[] = []\n\n\t// Find routes array in the AST\n\tfor (const node of ast.program.body) {\n\t\t// Handle: const router = createBrowserRouter([...])\n\t\tif (node.type === \"VariableDeclaration\") {\n\t\t\tfor (const decl of node.declarations) {\n\t\t\t\tif (decl.init?.type === \"CallExpression\") {\n\t\t\t\t\tconst callee = decl.init.callee\n\t\t\t\t\tif (\n\t\t\t\t\t\tcallee.type === \"Identifier\" &&\n\t\t\t\t\t\tROUTER_FACTORY_NAMES.includes(callee.name)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst firstArg = decl.init.arguments[0]\n\t\t\t\t\t\tif (firstArg?.type === \"ArrayExpression\") {\n\t\t\t\t\t\t\tconst parsed = parseRoutesArray(firstArg, routesFileDir, warnings)\n\t\t\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export const router = createBrowserRouter([...])\n\t\t// and: export const routes = [...]\n\t\tif (\n\t\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t\t) {\n\t\t\tfor (const decl of node.declaration.declarations) {\n\t\t\t\t// Handle: export const router = createBrowserRouter([...])\n\t\t\t\tif (decl.init?.type === \"CallExpression\") {\n\t\t\t\t\tconst callee = decl.init.callee\n\t\t\t\t\tif (\n\t\t\t\t\t\tcallee.type === \"Identifier\" &&\n\t\t\t\t\t\tROUTER_FACTORY_NAMES.includes(callee.name)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst firstArg = decl.init.arguments[0]\n\t\t\t\t\t\tif (firstArg?.type === \"ArrayExpression\") {\n\t\t\t\t\t\t\tconst parsed = parseRoutesArray(firstArg, routesFileDir, warnings)\n\t\t\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Handle: export const routes = [...]\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: const routes = [...]; (for later export)\n\t\tif (node.type === \"VariableDeclaration\") {\n\t\t\tfor (const decl of node.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export default [...]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(node.declaration, routesFileDir, warnings)\n\t\t\troutes.push(...parsed)\n\t\t}\n\n\t\t// Handle: export default [...] satisfies RouteObject[]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"TSSatisfiesExpression\" &&\n\t\t\tnode.declaration.expression.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(\n\t\t\t\tnode.declaration.expression,\n\t\t\t\troutesFileDir,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t\troutes.push(...parsed)\n\t\t}\n\t}\n\n\t// Warn if no routes were found\n\tif (routes.length === 0) {\n\t\twarnings.push(\n\t\t\t\"No routes found. Supported patterns: 'createBrowserRouter([...])', 'export const routes = [...]', or 'export default [...]'\",\n\t\t)\n\t}\n\n\treturn { routes, warnings }\n}\n\n/**\n * Parse an array expression containing route objects\n */\nfunction parseRoutesArray(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tarrayNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const element of arrayNode.elements) {\n\t\tif (!element) continue\n\n\t\t// Handle spread elements\n\t\tif (element.type === \"SpreadElement\") {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Spread operator detected${loc}. Routes from spread cannot be statically analyzed.`,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (element.type === \"ObjectExpression\") {\n\t\t\tconst route = parseRouteObject(element, baseDir, warnings)\n\t\t\tif (route) {\n\t\t\t\troutes.push(route)\n\t\t\t}\n\t\t} else {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Non-object route element (${element.type})${loc}. Only object literals can be statically analyzed.`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Parse a single route object expression\n */\nfunction parseRouteObject(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tobjectNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute | null {\n\tconst route: ParsedRoute = {\n\t\tpath: \"\",\n\t}\n\tlet isIndexRoute = false\n\tlet hasPath = false\n\n\tfor (const prop of objectNode.properties) {\n\t\tif (prop.type !== \"ObjectProperty\") continue\n\t\tif (prop.key.type !== \"Identifier\") continue\n\n\t\tconst key = prop.key.name\n\n\t\tswitch (key) {\n\t\t\tcase \"path\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\troute.path = prop.value.value\n\t\t\t\t\thasPath = true\n\t\t\t\t} else {\n\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t`Dynamic path value (${prop.value.type})${loc}. Only string literal paths can be statically analyzed.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"index\":\n\t\t\t\tif (prop.value.type === \"BooleanLiteral\" && prop.value.value === true) {\n\t\t\t\t\tisIndexRoute = true\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"element\":\n\t\t\t\troute.component = extractComponentFromJSX(prop.value, warnings)\n\t\t\t\tbreak\n\n\t\t\tcase \"Component\":\n\t\t\t\tif (prop.value.type === \"Identifier\") {\n\t\t\t\t\troute.component = prop.value.name\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"lazy\": {\n\t\t\t\tconst lazyPath = extractLazyImportPath(prop.value, baseDir, warnings)\n\t\t\t\tif (lazyPath) {\n\t\t\t\t\troute.component = lazyPath\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase \"children\":\n\t\t\t\tif (prop.value.type === \"ArrayExpression\") {\n\t\t\t\t\troute.children = parseRoutesArray(prop.value, baseDir, warnings)\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t// Handle index routes\n\tif (isIndexRoute) {\n\t\troute.path = \"\"\n\t\treturn route\n\t}\n\n\t// Skip routes without path (layout wrappers without path)\n\t// These are parent routes that only serve as layout containers\n\tif (!hasPath && !isIndexRoute) {\n\t\t// If it has children, process them with empty parent path contribution\n\t\tif (route.children && route.children.length > 0) {\n\t\t\t// Return a route with empty path to act as layout\n\t\t\troute.path = \"\"\n\t\t\treturn route\n\t\t}\n\t\treturn null\n\t}\n\n\treturn route\n}\n\n/**\n * Extract component name from JSX element\n * element: <Dashboard /> -> \"Dashboard\"\n */\nfunction extractComponentFromJSX(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\twarnings: string[],\n): string | undefined {\n\tif (node.type === \"JSXElement\") {\n\t\tconst openingElement = node.openingElement\n\t\tif (openingElement?.name?.type === \"JSXIdentifier\") {\n\t\t\treturn openingElement.name.name\n\t\t}\n\t\tif (openingElement?.name?.type === \"JSXMemberExpression\") {\n\t\t\t// Handle <Namespace.Component />\n\t\t\tconst parts: string[] = []\n\t\t\tlet current = openingElement.name\n\t\t\twhile (current) {\n\t\t\t\tif (current.type === \"JSXIdentifier\") {\n\t\t\t\t\tparts.unshift(current.name)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif (current.type === \"JSXMemberExpression\") {\n\t\t\t\t\tif (current.property?.type === \"JSXIdentifier\") {\n\t\t\t\t\t\tparts.unshift(current.property.name)\n\t\t\t\t\t}\n\t\t\t\t\tcurrent = current.object\n\t\t\t\t} else {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn parts.join(\".\")\n\t\t}\n\n\t\t// Check if it has children (wrapper component)\n\t\tif (\n\t\t\tnode.children &&\n\t\t\tnode.children.length > 0 &&\n\t\t\topeningElement?.name?.type === \"JSXIdentifier\"\n\t\t) {\n\t\t\tconst wrapperName = openingElement.name.name\n\t\t\t// Find the first JSX child\n\t\t\tfor (const child of node.children) {\n\t\t\t\tif (child.type === \"JSXElement\") {\n\t\t\t\t\tconst childComponent = extractComponentFromJSX(child, warnings)\n\t\t\t\t\tif (childComponent) {\n\t\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t\t`Wrapper component detected: <${wrapperName}>. Using wrapper name for screen.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn wrapperName\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Handle JSXFragment\n\tif (node.type === \"JSXFragment\") {\n\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\twarnings.push(\n\t\t\t`JSX Fragment detected${loc}. Cannot extract component name from fragments. Consider wrapping in a named component.`,\n\t\t)\n\t\treturn undefined\n\t}\n\n\t// Catch-all for unrecognized element patterns\n\tif (node) {\n\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\twarnings.push(\n\t\t\t`Unrecognized element pattern (${node.type})${loc}. Component will not be extracted.`,\n\t\t)\n\t}\n\treturn undefined\n}\n\n/**\n * Extract import path from lazy function\n * lazy: () => import(\"./pages/Dashboard\") -> resolved path\n */\nfunction extractLazyImportPath(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Arrow function: () => import('./path')\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\tif (body.type === \"CallExpression\" && body.callee.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t\t// Dynamic import argument\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Lazy import with dynamic path${loc}. Only string literal imports can be statically analyzed.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\t// Unrecognized lazy pattern\n\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\twarnings.push(\n\t\t`Unrecognized lazy pattern (${node.type})${loc}. Expected arrow function with import().`,\n\t)\n\treturn undefined\n}\n\n/**\n * Detect if content is React Router based on patterns\n */\nexport function isReactRouterContent(content: string): boolean {\n\t// Check for React Router specific patterns\n\tif (\n\t\tcontent.includes(\"createBrowserRouter\") ||\n\t\tcontent.includes(\"createHashRouter\") ||\n\t\tcontent.includes(\"createMemoryRouter\") ||\n\t\tcontent.includes(\"RouteObject\")\n\t) {\n\t\treturn true\n\t}\n\n\t// Check for JSX element pattern in routes\n\tif (/element:\\s*</.test(content)) {\n\t\treturn true\n\t}\n\n\t// Check for Component property pattern (uppercase)\n\tif (/Component:\\s*[A-Z]/.test(content)) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n/**\n * Detect if content is Vue Router based on patterns\n */\nexport function isVueRouterContent(content: string): boolean {\n\t// Check for Vue Router specific patterns\n\tif (\n\t\tcontent.includes(\"RouteRecordRaw\") ||\n\t\tcontent.includes(\"vue-router\") ||\n\t\tcontent.includes(\".vue\")\n\t) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n/**\n * Detect router type from file content.\n * Detection order: TanStack Router -> Solid Router -> Angular Router -> React Router -> Vue Router.\n * This order ensures more specific patterns are checked before more generic ones.\n */\nexport function detectRouterType(content: string): RouterType {\n\t// Check TanStack Router first (more specific patterns)\n\tif (isTanStackRouterContent(content)) {\n\t\treturn \"tanstack-router\"\n\t}\n\t// Check Solid Router before React Router (both use similar patterns)\n\tif (isSolidRouterContent(content)) {\n\t\treturn \"solid-router\"\n\t}\n\t// Check Angular Router before React Router (distinct patterns)\n\tif (isAngularRouterContent(content)) {\n\t\treturn \"angular-router\"\n\t}\n\tif (isReactRouterContent(content)) {\n\t\treturn \"react-router\"\n\t}\n\tif (isVueRouterContent(content)) {\n\t\treturn \"vue-router\"\n\t}\n\treturn \"unknown\"\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { parse } from \"@babel/parser\"\nimport {\n\ttype FlatRoute,\n\tflattenRoutes,\n\ttype ParsedRoute,\n\ttype ParseResult,\n\tpathToScreenId,\n\tpathToScreenTitle,\n\tresolveImportPath,\n} from \"./routeParserUtils.js\"\n\n// Re-export shared types and utilities\nexport type { ParsedRoute, FlatRoute, ParseResult }\nexport { flattenRoutes, pathToScreenId, pathToScreenTitle }\n\n/**\n * Parse Vue Router configuration file and extract routes\n */\nexport function parseVueRouterConfig(\n\tfilePath: string,\n\tpreloadedContent?: string,\n): ParseResult {\n\tconst absolutePath = resolve(filePath)\n\tconst routesFileDir = dirname(absolutePath)\n\tconst warnings: string[] = []\n\n\t// Read file with proper error handling (skip if content is preloaded)\n\tlet content: string\n\tif (preloadedContent !== undefined) {\n\t\tcontent = preloadedContent\n\t} else {\n\t\ttry {\n\t\t\tcontent = readFileSync(absolutePath, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to read routes file \"${absolutePath}\": ${message}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t// Parse with Babel - wrap for better error messages\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", \"jsx\"],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Syntax error in routes file \"${absolutePath}\": ${error.message}`,\n\t\t\t)\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tthrow new Error(`Failed to parse routes file \"${absolutePath}\": ${message}`)\n\t}\n\n\tconst routes: ParsedRoute[] = []\n\n\t// Build import map to resolve component identifiers\n\tconst importMap = buildImportMap(ast.program.body, routesFileDir)\n\n\t// Find routes array in the AST\n\tfor (const node of ast.program.body) {\n\t\t// Handle: export const routes = [...]\n\t\tif (\n\t\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t\t) {\n\t\t\tfor (const decl of node.declaration.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(\n\t\t\t\t\t\tdecl.init,\n\t\t\t\t\t\troutesFileDir,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t\timportMap,\n\t\t\t\t\t)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: const routes = [...]; export { routes }\n\t\tif (node.type === \"VariableDeclaration\") {\n\t\t\tfor (const decl of node.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(\n\t\t\t\t\t\tdecl.init,\n\t\t\t\t\t\troutesFileDir,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t\timportMap,\n\t\t\t\t\t)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export default [...]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(\n\t\t\t\tnode.declaration,\n\t\t\t\troutesFileDir,\n\t\t\t\twarnings,\n\t\t\t\timportMap,\n\t\t\t)\n\t\t\troutes.push(...parsed)\n\t\t}\n\n\t\t// Handle: export default [...] satisfies RouteRecordRaw[]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"TSSatisfiesExpression\" &&\n\t\t\tnode.declaration.expression.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(\n\t\t\t\tnode.declaration.expression,\n\t\t\t\troutesFileDir,\n\t\t\t\twarnings,\n\t\t\t\timportMap,\n\t\t\t)\n\t\t\troutes.push(...parsed)\n\t\t}\n\t}\n\n\t// Warn if no routes were found\n\tif (routes.length === 0) {\n\t\twarnings.push(\n\t\t\t\"No routes array found. Supported patterns: 'export const routes = [...]', 'export default [...]', or 'export default [...] satisfies RouteRecordRaw[]'\",\n\t\t)\n\t}\n\n\treturn { routes, warnings }\n}\n\n/**\n * Map of identifier names to their resolved file paths\n */\ntype ImportMap = Map<string, string>\n\n/**\n * Build a map of imported identifiers to their file paths\n */\nfunction buildImportMap(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tbody: any[],\n\tbaseDir: string,\n): ImportMap {\n\tconst importMap: ImportMap = new Map()\n\n\tfor (const node of body) {\n\t\tif (node.type !== \"ImportDeclaration\") continue\n\n\t\tconst source = node.source.value\n\t\tif (typeof source !== \"string\") continue\n\n\t\t// Resolve the import path\n\t\tconst resolvedPath = resolveImportPath(source, baseDir)\n\t\tif (!resolvedPath) continue\n\n\t\t// Map each imported specifier to the resolved path\n\t\tfor (const specifier of node.specifiers) {\n\t\t\tif (specifier.type === \"ImportDefaultSpecifier\") {\n\t\t\t\t// import Component from './path'\n\t\t\t\timportMap.set(specifier.local.name, resolvedPath)\n\t\t\t} else if (specifier.type === \"ImportSpecifier\") {\n\t\t\t\t// import { Component } from './path'\n\t\t\t\timportMap.set(specifier.local.name, resolvedPath)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn importMap\n}\n\n/**\n * Parse an array expression containing route objects\n */\nfunction parseRoutesArray(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tarrayNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n\timportMap: ImportMap,\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const element of arrayNode.elements) {\n\t\tif (!element) continue\n\n\t\t// Handle spread elements\n\t\tif (element.type === \"SpreadElement\") {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Spread operator detected${loc}. Routes from spread cannot be statically analyzed.`,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (element.type === \"ObjectExpression\") {\n\t\t\tconst route = parseRouteObject(element, baseDir, warnings, importMap)\n\t\t\tif (route) {\n\t\t\t\troutes.push(route)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Parse a single route object expression\n */\nfunction parseRouteObject(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tobjectNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n\timportMap: ImportMap,\n): ParsedRoute | null {\n\tconst route: Partial<ParsedRoute> = {}\n\tlet hasPath = false\n\n\tfor (const prop of objectNode.properties) {\n\t\tif (prop.type !== \"ObjectProperty\") continue\n\t\tif (prop.key.type !== \"Identifier\") continue\n\n\t\tconst key = prop.key.name\n\n\t\tswitch (key) {\n\t\t\tcase \"path\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\troute.path = prop.value.value\n\t\t\t\t\thasPath = true\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"name\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\troute.name = prop.value.value\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"redirect\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\troute.redirect = prop.value.value\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"component\":\n\t\t\t\troute.component = extractComponentPath(prop.value, baseDir, importMap)\n\t\t\t\tbreak\n\n\t\t\tcase \"children\":\n\t\t\t\tif (prop.value.type === \"ArrayExpression\") {\n\t\t\t\t\troute.children = parseRoutesArray(\n\t\t\t\t\t\tprop.value,\n\t\t\t\t\t\tbaseDir,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t\timportMap,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t// Skip routes without path property\n\t// Empty string \"\" is valid in Vue Router (matches parent path)\n\tif (!hasPath) {\n\t\treturn null\n\t}\n\n\treturn route as ParsedRoute\n}\n\n/**\n * Extract component path from various component definitions\n */\nfunction extractComponentPath(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\timportMap: ImportMap,\n): string | undefined {\n\t// Direct identifier: component: HomeView\n\tif (node.type === \"Identifier\") {\n\t\t// Look up the identifier in the import map\n\t\treturn importMap.get(node.name)\n\t}\n\n\t// Arrow function with import: () => import('./views/Home.vue')\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\t// () => import('./path')\n\t\tif (body.type === \"CallExpression\" && body.callee.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t}\n\n\t\t// () => import(/* webpackChunkName */ './path')\n\t\tif (body.type === \"CallExpression\" && body.callee.type === \"Import\") {\n\t\t\tfor (const arg of body.arguments) {\n\t\t\t\tif (arg.type === \"StringLiteral\") {\n\t\t\t\t\treturn resolveImportPath(arg.value, baseDir)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn undefined\n}\n","import type { ElementNode, TemplateChildNode } from \"@vue/compiler-core\"\nimport { NodeTypes } from \"@vue/compiler-core\"\nimport { parse } from \"@vue/compiler-sfc\"\nimport {\n\tanalyzeNavigation,\n\ttype ComponentAnalysisResult,\n\tcreateDetectedNavigation,\n\ttype DetectedNavigation,\n\tdeduplicateByScreenId,\n\tisValidInternalPath,\n} from \"./navigationAnalyzer.js\"\n\n/**\n * Result of analyzing a Vue SFC for navigation.\n * Type alias for the shared ComponentAnalysisResult.\n */\nexport type VueSFCAnalysisResult = ComponentAnalysisResult\n\n/**\n * Analyze a Vue Single File Component for navigation patterns.\n *\n * Detects:\n * - Template: `<RouterLink to=\"/path\">`, `<router-link to=\"/path\">`\n * - Template: `:to=\"'/path'\"` (static string binding)\n * - Script: `router.push(\"/path\")`, `router.replace(\"/path\")`\n * - Script: `router.push({ path: \"/path\" })` (object-based navigation)\n *\n * @param content - The Vue SFC content to analyze\n * @param filePath - The file path (used for error messages)\n * @returns Analysis result with detected navigations and warnings\n */\nexport function analyzeVueSFC(\n\tcontent: string,\n\tfilePath: string,\n): VueSFCAnalysisResult {\n\tconst templateNavigations: DetectedNavigation[] = []\n\tconst scriptNavigations: DetectedNavigation[] = []\n\tconst warnings: string[] = []\n\n\ttry {\n\t\tconst { descriptor, errors } = parse(content, {\n\t\t\tfilename: filePath,\n\t\t\tsourceMap: false,\n\t\t})\n\n\t\tfor (const error of errors) {\n\t\t\twarnings.push(`SFC parse error: ${error.message}`)\n\t\t}\n\n\t\tif (descriptor.template?.ast) {\n\t\t\tconst templateResult = analyzeTemplateAST(\n\t\t\t\tdescriptor.template.ast.children,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t\ttemplateNavigations.push(...templateResult)\n\t\t}\n\n\t\tconst scriptContent =\n\t\t\tdescriptor.scriptSetup?.content || descriptor.script?.content\n\t\tif (scriptContent) {\n\t\t\tconst scriptResult = analyzeNavigation(scriptContent, \"vue-router\")\n\t\t\tscriptNavigations.push(...scriptResult.navigations)\n\t\t\twarnings.push(...scriptResult.warnings)\n\t\t}\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\twarnings.push(`SFC syntax error in ${filePath}: ${error.message}`)\n\t\t} else if (error instanceof RangeError) {\n\t\t\twarnings.push(\n\t\t\t\t`${filePath}: File too complex for navigation analysis. Consider simplifying the template structure.`,\n\t\t\t)\n\t\t} else {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\twarnings.push(\n\t\t\t\t`${filePath}: Unexpected error during analysis: ${message}. Please report this as a bug.`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn {\n\t\ttemplateNavigations: deduplicateByScreenId(templateNavigations),\n\t\tscriptNavigations: deduplicateByScreenId(scriptNavigations),\n\t\twarnings,\n\t}\n}\n\n/**\n * Analyze template AST for RouterLink components.\n *\n * @param nodes - Array of template child nodes to traverse\n * @param warnings - Array to collect warnings during analysis (mutated)\n * @returns Array of detected navigation targets from RouterLink components\n */\nfunction analyzeTemplateAST(\n\tnodes: TemplateChildNode[],\n\twarnings: string[],\n): DetectedNavigation[] {\n\tconst navigations: DetectedNavigation[] = []\n\n\twalkTemplateNodes(nodes, (node) => {\n\t\tif (node.type === NodeTypes.ELEMENT) {\n\t\t\tconst elementNode = node as ElementNode\n\t\t\tif (isRouterLinkComponent(elementNode.tag)) {\n\t\t\t\tconst nav = extractRouterLinkNavigation(elementNode, warnings)\n\t\t\t\tif (nav) {\n\t\t\t\t\tnavigations.push(nav)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t})\n\n\treturn navigations\n}\n\n/**\n * Check if a tag is a RouterLink component\n */\nfunction isRouterLinkComponent(tag: string): boolean {\n\treturn tag === \"RouterLink\" || tag === \"router-link\"\n}\n\n/**\n * Extract navigation from RouterLink component.\n *\n * @param node - The element node representing a RouterLink component\n * @param warnings - Array to collect warnings (mutated)\n * @returns Detected navigation or null if no valid path found\n */\nfunction extractRouterLinkNavigation(\n\tnode: ElementNode,\n\twarnings: string[],\n): DetectedNavigation | null {\n\tfor (const prop of node.props) {\n\t\t// Static attribute: to=\"/path\"\n\t\tif (prop.type === NodeTypes.ATTRIBUTE && prop.name === \"to\") {\n\t\t\tif (prop.value) {\n\t\t\t\tconst path = prop.value.content\n\t\t\t\tif (isValidInternalPath(path)) {\n\t\t\t\t\treturn createDetectedNavigation(path, \"link\", prop.loc.start.line)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Dynamic binding: :to=\"'/path'\" or v-bind:to=\"'/path'\"\n\t\tif (prop.type === NodeTypes.DIRECTIVE && prop.name === \"bind\") {\n\t\t\t// Check if this is binding the \"to\" prop\n\t\t\tif (\n\t\t\t\tprop.arg?.type === NodeTypes.SIMPLE_EXPRESSION &&\n\t\t\t\tprop.arg.content === \"to\"\n\t\t\t) {\n\t\t\t\treturn extractDynamicToBinding(prop, node.loc.start.line, warnings)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null\n}\n\n/**\n * Extract path from dynamic :to binding.\n *\n * Handles expressions like `:to=\"'/path'\"` or `v-bind:to=\"'/path'\"`.\n * Complex expressions that cannot be statically analyzed generate warnings.\n *\n * @param directive - The Vue directive AST node\n * @param line - Line number for warning messages\n * @param warnings - Array to collect warnings (mutated)\n * @returns Detected navigation or null if path cannot be extracted\n */\nfunction extractDynamicToBinding(\n\t// biome-ignore lint/suspicious/noExplicitAny: Vue compiler types are complex\n\tdirective: any,\n\tline: number,\n\twarnings: string[],\n): DetectedNavigation | null {\n\tconst exp = directive.exp\n\n\tif (!exp) {\n\t\twarnings.push(\n\t\t\t`Empty :to binding at line ${line}. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t\treturn null\n\t}\n\n\tif (exp.type === NodeTypes.SIMPLE_EXPRESSION) {\n\t\tconst content = exp.content.trim()\n\n\t\t// Check if it's a static string literal\n\t\tif (isStaticStringLiteral(content)) {\n\t\t\tconst path = extractStringValue(content)\n\t\t\tif (path && isValidInternalPath(path)) {\n\t\t\t\treturn createDetectedNavigation(path, \"link\", line)\n\t\t\t}\n\t\t}\n\n\t\t// Dynamic variable or complex expression\n\t\twarnings.push(\n\t\t\t`Dynamic :to binding at line ${line} cannot be statically analyzed. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t} else {\n\t\t// Complex expression type (COMPOUND_EXPRESSION, etc.)\n\t\twarnings.push(\n\t\t\t`Complex :to binding at line ${line} uses an unsupported expression type. Add the target screen ID manually to the 'next' field in screen.meta.ts.`,\n\t\t)\n\t}\n\n\treturn null\n}\n\n/**\n * Check if expression content is a static string literal.\n *\n * Recognizes single quotes, double quotes, and template literals without interpolation.\n *\n * @param content - The expression content (e.g., \"'/path'\" or \"`/home`\")\n * @returns true if the content is a statically analyzable string literal\n */\nfunction isStaticStringLiteral(content: string): boolean {\n\t// Single quotes: '/path'\n\tif (content.startsWith(\"'\") && content.endsWith(\"'\")) {\n\t\treturn true\n\t}\n\t// Double quotes: \"/path\"\n\tif (content.startsWith('\"') && content.endsWith('\"')) {\n\t\treturn true\n\t}\n\t// Template literal without interpolation: `/path`\n\tif (\n\t\tcontent.startsWith(\"`\") &&\n\t\tcontent.endsWith(\"`\") &&\n\t\t!content.includes(\"${\")\n\t) {\n\t\treturn true\n\t}\n\treturn false\n}\n\n/**\n * Extract string value from a quoted string\n */\nfunction extractStringValue(content: string): string {\n\treturn content.slice(1, -1)\n}\n\n/**\n * Walk template AST nodes recursively in pre-order traversal.\n *\n * Traverses element nodes, v-if branches, and v-for loop bodies.\n *\n * @param nodes - Array of template child nodes to traverse\n * @param callback - Function called for each node visited\n */\nfunction walkTemplateNodes(\n\tnodes: TemplateChildNode[],\n\tcallback: (node: TemplateChildNode) => void,\n): void {\n\tfor (const node of nodes) {\n\t\tcallback(node)\n\n\t\t// Element nodes have children\n\t\tif (node.type === NodeTypes.ELEMENT) {\n\t\t\tconst elementNode = node as ElementNode\n\t\t\tif (elementNode.children) {\n\t\t\t\twalkTemplateNodes(elementNode.children, callback)\n\t\t\t}\n\t\t}\n\n\t\t// IF nodes have branches with children\n\t\tif (node.type === NodeTypes.IF) {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: Vue compiler types are complex\n\t\t\tconst ifNode = node as any\n\t\t\tfor (const branch of ifNode.branches || []) {\n\t\t\t\tif (branch.children) {\n\t\t\t\t\twalkTemplateNodes(branch.children, callback)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// FOR nodes have children\n\t\tif (node.type === NodeTypes.FOR) {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: Vue compiler types are complex\n\t\t\tconst forNode = node as any\n\t\t\tif (forNode.children) {\n\t\t\t\twalkTemplateNodes(forNode.children, callback)\n\t\t\t}\n\t\t}\n\t}\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { dirname, join, relative, resolve } from \"node:path\"\nimport type { ApiIntegrationConfig, Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport prompts from \"prompts\"\nimport { glob } from \"tinyglobby\"\nimport { parseAngularRouterConfig } from \"../utils/angularRouterParser.js\"\nimport { analyzeAngularComponent } from \"../utils/angularTemplateAnalyzer.js\"\nimport { analyzeApiImports } from \"../utils/apiImportAnalyzer.js\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger, setVerbose } from \"../utils/logger.js\"\nimport {\n\tanalyzeNavigation,\n\tdetectNavigationFramework,\n} from \"../utils/navigationAnalyzer.js\"\nimport {\n\tdetectRouterType,\n\tparseReactRouterConfig,\n} from \"../utils/reactRouterParser.js\"\nimport {\n\ttype FlatRoute,\n\tflattenRoutes,\n\ttype ParseResult,\n} from \"../utils/routeParserUtils.js\"\nimport { parseSolidRouterConfig } from \"../utils/solidRouterParser.js\"\nimport { parseTanStackRouterConfig } from \"../utils/tanstackRouterParser.js\"\nimport { parseVueRouterConfig } from \"../utils/vueRouterParser.js\"\nimport { analyzeVueSFC } from \"../utils/vueSFCTemplateAnalyzer.js\"\n\nexport const generateCommand = define({\n\tname: \"generate\",\n\tdescription: \"Auto-generate screen.meta.ts files from route files\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tdryRun: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"n\",\n\t\t\tdescription: \"Show what would be generated without writing files\",\n\t\t\tdefault: false,\n\t\t},\n\t\tforce: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Overwrite existing screen.meta.ts files\",\n\t\t\tdefault: false,\n\t\t},\n\t\tinteractive: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"i\",\n\t\t\tdescription: \"Interactively confirm or modify each screen\",\n\t\t\tdefault: false,\n\t\t},\n\t\tdetectApi: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"a\",\n\t\t\tdescription:\n\t\t\t\t\"Detect API dependencies from imports (requires apiIntegration config)\",\n\t\t\tdefault: false,\n\t\t},\n\t\tdetectNavigation: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"N\",\n\t\t\tdescription:\n\t\t\t\t\"Detect navigation targets from code (Link, router.push, navigate)\",\n\t\t\tdefault: false,\n\t\t},\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output including stack traces\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tsetVerbose(ctx.values.verbose)\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst cwd = process.cwd()\n\t\tconst dryRun = ctx.values.dryRun ?? false\n\t\tconst force = ctx.values.force ?? false\n\t\tconst interactive = ctx.values.interactive ?? false\n\t\tconst detectApi = ctx.values.detectApi ?? false\n\t\tconst detectNavigation = ctx.values.detectNavigation ?? false\n\n\t\t// Check for routes configuration\n\t\tif (!config.routesPattern && !config.routesFile) {\n\t\t\tlogger.errorWithHelp(ERRORS.ROUTES_PATTERN_MISSING)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Validate --detect-api requires apiIntegration config\n\t\tif (detectApi && !config.apiIntegration) {\n\t\t\tlogger.error(\n\t\t\t\t`${logger.bold(\"--detect-api\")} requires ${logger.code(\"apiIntegration\")} configuration`,\n\t\t\t)\n\t\t\tlogger.blank()\n\t\t\tlogger.log(\"Add to your screenbook.config.ts:\")\n\t\t\tlogger.blank()\n\t\t\tlogger.log(\n\t\t\t\tlogger.dim(` export default defineConfig({\n apiIntegration: {\n clientPackages: [\"@/api/generated\"],\n },\n })`),\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Use routesFile mode (config-based routing)\n\t\tif (config.routesFile) {\n\t\t\tawait generateFromRoutesFile(config.routesFile, cwd, {\n\t\t\t\tdryRun,\n\t\t\t\tforce,\n\t\t\t\tinteractive,\n\t\t\t\tdetectApi,\n\t\t\t\tdetectNavigation,\n\t\t\t\tapiIntegration: config.apiIntegration,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\t// Use routesPattern mode (file-based routing)\n\t\tawait generateFromRoutesPattern(config.routesPattern as string, cwd, {\n\t\t\tdryRun,\n\t\t\tforce,\n\t\t\tinteractive,\n\t\t\tignore: config.ignore,\n\t\t\tdetectApi,\n\t\t\tdetectNavigation,\n\t\t\tapiIntegration: config.apiIntegration,\n\t\t})\n\t},\n})\n\ninterface GenerateFromRoutesFileOptions {\n\treadonly dryRun: boolean\n\treadonly force: boolean\n\treadonly interactive: boolean\n\treadonly detectApi: boolean\n\treadonly detectNavigation: boolean\n\treadonly apiIntegration?: ApiIntegrationConfig\n}\n\n/**\n * Generate screen.meta.ts files from a router config file (Vue Router or React Router)\n */\nasync function generateFromRoutesFile(\n\troutesFile: string,\n\tcwd: string,\n\toptions: GenerateFromRoutesFileOptions,\n): Promise<void> {\n\tconst {\n\t\tdryRun,\n\t\tforce,\n\t\tinteractive,\n\t\tdetectApi,\n\t\tdetectNavigation,\n\t\tapiIntegration,\n\t} = options\n\tconst absoluteRoutesFile = resolve(cwd, routesFile)\n\n\t// Check if routes file exists\n\tif (!existsSync(absoluteRoutesFile)) {\n\t\tlogger.errorWithHelp(ERRORS.ROUTES_FILE_NOT_FOUND(routesFile))\n\t\tprocess.exit(1)\n\t}\n\n\t// Detect router type\n\tconst content = readFileSync(absoluteRoutesFile, \"utf-8\")\n\tconst routerType = detectRouterType(content)\n\n\tconst routerTypeDisplay =\n\t\trouterType === \"tanstack-router\"\n\t\t\t? \"TanStack Router\"\n\t\t\t: routerType === \"solid-router\"\n\t\t\t\t? \"Solid Router\"\n\t\t\t\t: routerType === \"angular-router\"\n\t\t\t\t\t? \"Angular Router\"\n\t\t\t\t\t: routerType === \"react-router\"\n\t\t\t\t\t\t? \"React Router\"\n\t\t\t\t\t\t: routerType === \"vue-router\"\n\t\t\t\t\t\t\t? \"Vue Router\"\n\t\t\t\t\t\t\t: \"unknown\"\n\n\tlogger.info(\n\t\t`Parsing routes from ${logger.path(routesFile)} (${routerTypeDisplay})...`,\n\t)\n\tlogger.blank()\n\n\t// Parse the routes file with the appropriate parser\n\tlet parseResult: ParseResult\n\ttry {\n\t\tif (routerType === \"tanstack-router\") {\n\t\t\tparseResult = parseTanStackRouterConfig(absoluteRoutesFile)\n\t\t} else if (routerType === \"solid-router\") {\n\t\t\tparseResult = parseSolidRouterConfig(absoluteRoutesFile)\n\t\t} else if (routerType === \"angular-router\") {\n\t\t\tparseResult = parseAngularRouterConfig(absoluteRoutesFile)\n\t\t} else if (routerType === \"react-router\") {\n\t\t\tparseResult = parseReactRouterConfig(absoluteRoutesFile)\n\t\t} else if (routerType === \"vue-router\") {\n\t\t\tparseResult = parseVueRouterConfig(absoluteRoutesFile)\n\t\t} else {\n\t\t\t// Unknown router type - warn user and attempt Vue Router parser as fallback\n\t\t\tlogger.warn(\n\t\t\t\t`Could not auto-detect router type for ${logger.path(routesFile)}. Attempting to parse as Vue Router.`,\n\t\t\t)\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"If parsing fails, check that your router imports are explicit.\")}`,\n\t\t\t)\n\t\t\tparseResult = parseVueRouterConfig(absoluteRoutesFile)\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tlogger.errorWithHelp(ERRORS.ROUTES_FILE_PARSE_ERROR(routesFile, message))\n\t\tprocess.exit(1)\n\t}\n\n\t// Show warnings\n\tfor (const warning of parseResult.warnings) {\n\t\tlogger.warn(warning)\n\t}\n\n\t// Flatten routes\n\tconst flatRoutes = flattenRoutes(parseResult.routes)\n\n\tif (flatRoutes.length === 0) {\n\t\tlogger.warn(\"No routes found in the config file\")\n\t\treturn\n\t}\n\n\tlogger.log(`Found ${flatRoutes.length} routes`)\n\tlogger.blank()\n\n\tlet created = 0\n\tlet skipped = 0\n\n\tfor (const route of flatRoutes) {\n\t\t// Determine where to place screen.meta.ts\n\t\tconst metaPath = determineMetaPath(route, cwd)\n\t\tconst absoluteMetaPath = resolve(cwd, metaPath)\n\n\t\tif (!force && existsSync(absoluteMetaPath)) {\n\t\t\tif (!interactive) {\n\t\t\t\tskipped++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlogger.itemWarn(\n\t\t\t\t`Exists: ${logger.path(metaPath)} (use --force to overwrite)`,\n\t\t\t)\n\t\t\tskipped++\n\t\t\tcontinue\n\t\t}\n\n\t\tconst screenMeta: InferredScreenMeta = {\n\t\t\tid: route.screenId,\n\t\t\ttitle: route.screenTitle,\n\t\t\troute: route.fullPath,\n\t\t}\n\n\t\t// Detect API dependencies if enabled\n\t\tlet detectedApis: string[] = []\n\t\tif (detectApi && apiIntegration && route.componentPath) {\n\t\t\tconst componentAbsPath = resolve(cwd, route.componentPath)\n\t\t\tif (existsSync(componentAbsPath)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst componentContent = readFileSync(componentAbsPath, \"utf-8\")\n\t\t\t\t\tconst result = analyzeApiImports(componentContent, apiIntegration)\n\t\t\t\t\tdetectedApis = result.imports.map((i) => i.dependsOnName)\n\t\t\t\t\tfor (const warning of result.warnings) {\n\t\t\t\t\t\tlogger.warn(`${logger.path(route.componentPath)}: ${warning}`)\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`${logger.path(route.componentPath)}: Could not analyze for API imports: ${message}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Detect navigation targets if enabled\n\t\tlet detectedNext: string[] = []\n\t\tif (detectNavigation && route.componentPath) {\n\t\t\tconst componentAbsPath = resolve(cwd, route.componentPath)\n\t\t\tif (existsSync(componentAbsPath)) {\n\t\t\t\tlet componentContent: string\n\t\t\t\ttry {\n\t\t\t\t\tcomponentContent = readFileSync(componentAbsPath, \"utf-8\")\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`${logger.path(route.componentPath)}: Could not read file for navigation analysis: ${message}`,\n\t\t\t\t\t)\n\t\t\t\t\tcomponentContent = \"\"\n\t\t\t\t}\n\n\t\t\t\tif (componentContent) {\n\t\t\t\t\t// Use Vue SFC analyzer for .vue files to detect RouterLink in templates\n\t\t\t\t\tif (route.componentPath.endsWith(\".vue\")) {\n\t\t\t\t\t\tconst result = analyzeVueSFC(componentContent, route.componentPath)\n\t\t\t\t\t\tdetectedNext = [\n\t\t\t\t\t\t\t...result.templateNavigations.map((n) => n.screenId),\n\t\t\t\t\t\t\t...result.scriptNavigations.map((n) => n.screenId),\n\t\t\t\t\t\t]\n\t\t\t\t\t\tfor (const warning of result.warnings) {\n\t\t\t\t\t\t\tlogger.warn(`${logger.path(route.componentPath)}: ${warning}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (route.componentPath.endsWith(\".component.ts\")) {\n\t\t\t\t\t\t// Use Angular component analyzer for .component.ts files\n\t\t\t\t\t\tconst result = analyzeAngularComponent(\n\t\t\t\t\t\t\tcomponentContent,\n\t\t\t\t\t\t\troute.componentPath,\n\t\t\t\t\t\t\tcwd,\n\t\t\t\t\t\t)\n\t\t\t\t\t\tdetectedNext = [\n\t\t\t\t\t\t\t...result.templateNavigations.map((n) => n.screenId),\n\t\t\t\t\t\t\t...result.scriptNavigations.map((n) => n.screenId),\n\t\t\t\t\t\t]\n\t\t\t\t\t\tfor (const warning of result.warnings) {\n\t\t\t\t\t\t\tlogger.warn(`${logger.path(route.componentPath)}: ${warning}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Use standard analyzer for non-Vue files\n\t\t\t\t\t\tconst { framework, detected } =\n\t\t\t\t\t\t\tdetectNavigationFramework(componentContent)\n\t\t\t\t\t\tif (!detected) {\n\t\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t\t`${logger.path(route.componentPath)}: Could not detect navigation framework, defaulting to Next.js patterns. Navigation detection may be incomplete.`,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst result = analyzeNavigation(componentContent, framework)\n\t\t\t\t\t\tdetectedNext = result.navigations.map((n) => n.screenId)\n\t\t\t\t\t\tfor (const warning of result.warnings) {\n\t\t\t\t\t\t\tlogger.warn(`${logger.path(route.componentPath)}: ${warning}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (interactive) {\n\t\t\tconst result = await promptForScreen(route.fullPath, screenMeta)\n\n\t\t\tif (result.skip) {\n\t\t\t\tlogger.itemWarn(`Skipped: ${logger.path(metaPath)}`)\n\t\t\t\tskipped++\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst content = generateScreenMetaContent(result.meta, {\n\t\t\t\towner: result.owner,\n\t\t\t\ttags: result.tags,\n\t\t\t\tdependsOn: detectedApis,\n\t\t\t\tnext: detectedNext,\n\t\t\t})\n\n\t\t\tif (dryRun) {\n\t\t\t\tlogDryRunOutput(\n\t\t\t\t\tmetaPath,\n\t\t\t\t\tresult.meta,\n\t\t\t\t\tresult.owner,\n\t\t\t\t\tresult.tags,\n\t\t\t\t\tdetectedApis,\n\t\t\t\t\tdetectedNext,\n\t\t\t\t)\n\t\t\t\tcreated++\n\t\t\t} else if (safeWriteFile(absoluteMetaPath, metaPath, content)) {\n\t\t\t\tcreated++\n\t\t\t}\n\t\t} else {\n\t\t\tconst content = generateScreenMetaContent(screenMeta, {\n\t\t\t\tdependsOn: detectedApis,\n\t\t\t\tnext: detectedNext,\n\t\t\t})\n\n\t\t\tif (dryRun) {\n\t\t\t\tlogDryRunOutput(\n\t\t\t\t\tmetaPath,\n\t\t\t\t\tscreenMeta,\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\tdetectedApis,\n\t\t\t\t\tdetectedNext,\n\t\t\t\t)\n\t\t\t\tcreated++\n\t\t\t} else if (safeWriteFile(absoluteMetaPath, metaPath, content)) {\n\t\t\t\tcreated++\n\t\t\t}\n\t\t}\n\t}\n\n\tlogSummary(created, skipped, dryRun)\n}\n\nexport interface GenerateFromRoutesPatternOptions {\n\treadonly dryRun: boolean\n\treadonly force: boolean\n\treadonly interactive: boolean\n\treadonly ignore: readonly string[]\n\treadonly detectApi: boolean\n\treadonly detectNavigation: boolean\n\treadonly apiIntegration?: ApiIntegrationConfig\n}\n\n/**\n * Generate screen.meta.ts files from route files matching a glob pattern\n */\nexport async function generateFromRoutesPattern(\n\troutesPattern: string,\n\tcwd: string,\n\toptions: GenerateFromRoutesPatternOptions,\n): Promise<void> {\n\tconst {\n\t\tdryRun,\n\t\tforce,\n\t\tinteractive,\n\t\tignore,\n\t\tdetectApi,\n\t\tdetectNavigation,\n\t\tapiIntegration,\n\t} = options\n\n\tlogger.info(\"Scanning for route files...\")\n\tlogger.blank()\n\n\t// Find all route files\n\tconst routeFiles = await glob(routesPattern, {\n\t\tcwd,\n\t\tignore,\n\t})\n\n\tif (routeFiles.length === 0) {\n\t\tlogger.warn(`No route files found matching: ${routesPattern}`)\n\t\treturn\n\t}\n\n\tlogger.log(`Found ${routeFiles.length} route files`)\n\tlogger.blank()\n\n\tlet created = 0\n\tlet skipped = 0\n\n\tfor (const routeFile of routeFiles) {\n\t\tconst routeDir = dirname(routeFile)\n\t\tconst metaPath = join(routeDir, \"screen.meta.ts\")\n\t\tconst absoluteMetaPath = join(cwd, metaPath)\n\n\t\tif (!force && existsSync(absoluteMetaPath)) {\n\t\t\tif (!interactive) {\n\t\t\t\tskipped++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlogger.itemWarn(\n\t\t\t\t`Exists: ${logger.path(metaPath)} (use --force to overwrite)`,\n\t\t\t)\n\t\t\tskipped++\n\t\t\tcontinue\n\t\t}\n\n\t\t// Generate screen metadata from path\n\t\tconst screenMeta = inferScreenMeta(routeDir, routesPattern)\n\n\t\t// Detect API dependencies if enabled\n\t\tlet detectedApis: string[] = []\n\t\tif (detectApi && apiIntegration) {\n\t\t\tconst absoluteRouteFile = join(cwd, routeFile)\n\t\t\tif (existsSync(absoluteRouteFile)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst routeContent = readFileSync(absoluteRouteFile, \"utf-8\")\n\t\t\t\t\tconst result = analyzeApiImports(routeContent, apiIntegration)\n\t\t\t\t\tdetectedApis = result.imports.map((i) => i.dependsOnName)\n\t\t\t\t\tfor (const warning of result.warnings) {\n\t\t\t\t\t\tlogger.warn(`${logger.path(routeFile)}: ${warning}`)\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`${logger.path(routeFile)}: Could not analyze for API imports: ${message}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Detect navigation targets if enabled\n\t\tlet detectedNext: string[] = []\n\t\tif (detectNavigation) {\n\t\t\tconst absoluteRouteFile = join(cwd, routeFile)\n\t\t\tif (existsSync(absoluteRouteFile)) {\n\t\t\t\tlet routeContent: string\n\t\t\t\ttry {\n\t\t\t\t\trouteContent = readFileSync(absoluteRouteFile, \"utf-8\")\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`${logger.path(routeFile)}: Could not read file for navigation analysis: ${message}`,\n\t\t\t\t\t)\n\t\t\t\t\trouteContent = \"\"\n\t\t\t\t}\n\n\t\t\t\tif (routeContent) {\n\t\t\t\t\tconst { framework, detected } =\n\t\t\t\t\t\tdetectNavigationFramework(routeContent)\n\t\t\t\t\tif (!detected) {\n\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t`${logger.path(routeFile)}: Could not detect navigation framework, defaulting to Next.js patterns. Navigation detection may be incomplete.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tconst result = analyzeNavigation(routeContent, framework)\n\t\t\t\t\tdetectedNext = result.navigations.map((n) => n.screenId)\n\t\t\t\t\tfor (const warning of result.warnings) {\n\t\t\t\t\t\tlogger.warn(`${logger.path(routeFile)}: ${warning}`)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (interactive) {\n\t\t\tconst result = await promptForScreen(routeFile, screenMeta)\n\n\t\t\tif (result.skip) {\n\t\t\t\tlogger.itemWarn(`Skipped: ${logger.path(metaPath)}`)\n\t\t\t\tskipped++\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst content = generateScreenMetaContent(result.meta, {\n\t\t\t\towner: result.owner,\n\t\t\t\ttags: result.tags,\n\t\t\t\tdependsOn: detectedApis,\n\t\t\t\tnext: detectedNext,\n\t\t\t})\n\n\t\t\tif (dryRun) {\n\t\t\t\tlogDryRunOutput(\n\t\t\t\t\tmetaPath,\n\t\t\t\t\tresult.meta,\n\t\t\t\t\tresult.owner,\n\t\t\t\t\tresult.tags,\n\t\t\t\t\tdetectedApis,\n\t\t\t\t\tdetectedNext,\n\t\t\t\t)\n\t\t\t\tcreated++\n\t\t\t} else if (safeWriteFile(absoluteMetaPath, metaPath, content)) {\n\t\t\t\tcreated++\n\t\t\t}\n\t\t} else {\n\t\t\tconst content = generateScreenMetaContent(screenMeta, {\n\t\t\t\tdependsOn: detectedApis,\n\t\t\t\tnext: detectedNext,\n\t\t\t})\n\n\t\t\tif (dryRun) {\n\t\t\t\tlogDryRunOutput(\n\t\t\t\t\tmetaPath,\n\t\t\t\t\tscreenMeta,\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\tdetectedApis,\n\t\t\t\t\tdetectedNext,\n\t\t\t\t)\n\t\t\t\tcreated++\n\t\t\t} else if (safeWriteFile(absoluteMetaPath, metaPath, content)) {\n\t\t\t\tcreated++\n\t\t\t}\n\t\t}\n\t}\n\n\tlogSummary(created, skipped, dryRun)\n}\n\n/**\n * Determine where to place screen.meta.ts for a route\n */\nfunction determineMetaPath(route: FlatRoute, cwd: string): string {\n\t// If component path is available, place screen.meta.ts next to it\n\tif (route.componentPath) {\n\t\tconst componentDir = dirname(route.componentPath)\n\t\tconst relativePath = relative(cwd, componentDir)\n\t\t// Ensure path doesn't escape cwd\n\t\tif (!relativePath.startsWith(\"..\")) {\n\t\t\treturn join(relativePath, \"screen.meta.ts\")\n\t\t}\n\t}\n\n\t// Fall back to src/screens/{screenId}/screen.meta.ts\n\tconst screenDir = route.screenId.replace(/\\./g, \"/\")\n\treturn join(\"src\", \"screens\", screenDir, \"screen.meta.ts\")\n}\n\n/**\n * Ensure the directory for a file exists\n */\nfunction ensureDirectoryExists(filePath: string): void {\n\tconst dir = dirname(filePath)\n\tif (!existsSync(dir)) {\n\t\ttry {\n\t\t\tmkdirSync(dir, { recursive: true })\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(`Failed to create directory \"${dir}\": ${message}`)\n\t\t}\n\t}\n}\n\n/**\n * Safely write a file with error handling\n * Returns true if successful, false if failed\n */\nfunction safeWriteFile(\n\tabsolutePath: string,\n\trelativePath: string,\n\tcontent: string,\n): boolean {\n\ttry {\n\t\tensureDirectoryExists(absolutePath)\n\t\twriteFileSync(absolutePath, content)\n\t\tlogger.itemSuccess(`Created: ${logger.path(relativePath)}`)\n\t\treturn true\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tlogger.itemError(\n\t\t\t`Failed to create ${logger.path(relativePath)}: ${message}`,\n\t\t)\n\t\treturn false\n\t}\n}\n\n/**\n * Log dry run output for a screen\n */\nfunction logDryRunOutput(\n\tmetaPath: string,\n\tmeta: InferredScreenMeta,\n\towner?: string[],\n\ttags?: string[],\n\tdependsOn?: string[],\n\tnext?: string[],\n): void {\n\tlogger.step(`Would create: ${logger.path(metaPath)}`)\n\tlogger.log(` ${logger.dim(`id: \"${meta.id}\"`)}`)\n\tlogger.log(` ${logger.dim(`title: \"${meta.title}\"`)}`)\n\tlogger.log(` ${logger.dim(`route: \"${meta.route}\"`)}`)\n\tif (owner && owner.length > 0) {\n\t\tlogger.log(\n\t\t\t` ${logger.dim(`owner: [${owner.map((o) => `\"${o}\"`).join(\", \")}]`)}`,\n\t\t)\n\t}\n\tif (tags && tags.length > 0) {\n\t\tlogger.log(\n\t\t\t` ${logger.dim(`tags: [${tags.map((t) => `\"${t}\"`).join(\", \")}]`)}`,\n\t\t)\n\t}\n\tif (dependsOn && dependsOn.length > 0) {\n\t\tlogger.log(\n\t\t\t` ${logger.dim(`dependsOn: [${dependsOn.map((d) => `\"${d}\"`).join(\", \")}]`)}`,\n\t\t)\n\t}\n\tif (next && next.length > 0) {\n\t\tlogger.log(\n\t\t\t` ${logger.dim(`next: [${next.map((n) => `\"${n}\"`).join(\", \")}]`)}`,\n\t\t)\n\t}\n\tlogger.blank()\n}\n\n/**\n * Log summary after generation\n */\nfunction logSummary(created: number, skipped: number, dryRun: boolean): void {\n\tlogger.blank()\n\tif (dryRun) {\n\t\tlogger.info(`Would create ${created} files (${skipped} already exist)`)\n\t\tlogger.blank()\n\t\tlogger.log(`Run without ${logger.code(\"--dry-run\")} to create files`)\n\t} else {\n\t\tlogger.done(`Created ${created} files (${skipped} skipped)`)\n\t\tif (created > 0) {\n\t\t\tlogger.blank()\n\t\t\tlogger.log(logger.bold(\"Next steps:\"))\n\t\t\tlogger.log(\" 1. Review and customize the generated screen.meta.ts files\")\n\t\t\tlogger.log(\n\t\t\t\t` 2. Run ${logger.code(\"screenbook dev\")} to view your screen catalog`,\n\t\t\t)\n\t\t}\n\t}\n}\n\n/**\n * Subset of Screen containing only auto-inferred fields.\n * Uses Pick to ensure type alignment with the core Screen type.\n */\ntype InferredScreenMeta = Pick<Screen, \"id\" | \"title\" | \"route\">\n\ninterface InteractiveResult {\n\tskip: boolean\n\tmeta: InferredScreenMeta\n\towner: string[]\n\ttags: string[]\n}\n\n/**\n * Parse comma-separated string into array\n */\nexport function parseCommaSeparated(input: string): string[] {\n\tif (!input.trim()) return []\n\treturn input\n\t\t.split(\",\")\n\t\t.map((s) => s.trim())\n\t\t.filter(Boolean)\n}\n\n/**\n * Prompt user for screen metadata in interactive mode\n */\nasync function promptForScreen(\n\trouteFile: string,\n\tinferred: InferredScreenMeta,\n): Promise<InteractiveResult> {\n\tlogger.blank()\n\tlogger.info(`Found: ${logger.path(routeFile)}`)\n\tlogger.blank()\n\tlogger.log(\n\t\t` ${logger.dim(\"ID:\")} ${inferred.id} ${logger.dim(\"(inferred)\")}`,\n\t)\n\tlogger.log(\n\t\t` ${logger.dim(\"Title:\")} ${inferred.title} ${logger.dim(\"(inferred)\")}`,\n\t)\n\tlogger.log(\n\t\t` ${logger.dim(\"Route:\")} ${inferred.route} ${logger.dim(\"(inferred)\")}`,\n\t)\n\tlogger.blank()\n\n\tconst response = await prompts([\n\t\t{\n\t\t\ttype: \"confirm\",\n\t\t\tname: \"proceed\",\n\t\t\tmessage: \"Generate this screen?\",\n\t\t\tinitial: true,\n\t\t},\n\t\t{\n\t\t\ttype: (prev) => (prev ? \"text\" : null),\n\t\t\tname: \"id\",\n\t\t\tmessage: \"ID\",\n\t\t\tinitial: inferred.id,\n\t\t},\n\t\t{\n\t\t\ttype: (_prev, values) => (values.proceed ? \"text\" : null),\n\t\t\tname: \"title\",\n\t\t\tmessage: \"Title\",\n\t\t\tinitial: inferred.title,\n\t\t},\n\t\t{\n\t\t\ttype: (_prev, values) => (values.proceed ? \"text\" : null),\n\t\t\tname: \"owner\",\n\t\t\tmessage: \"Owner (comma-separated)\",\n\t\t\tinitial: \"\",\n\t\t},\n\t\t{\n\t\t\ttype: (_prev, values) => (values.proceed ? \"text\" : null),\n\t\t\tname: \"tags\",\n\t\t\tmessage: \"Tags (comma-separated)\",\n\t\t\tinitial: inferred.id.split(\".\")[0] || \"\",\n\t\t},\n\t])\n\n\tif (!response.proceed) {\n\t\treturn { skip: true, meta: inferred, owner: [], tags: [] }\n\t}\n\n\treturn {\n\t\tskip: false,\n\t\tmeta: {\n\t\t\tid: response.id || inferred.id,\n\t\t\ttitle: response.title || inferred.title,\n\t\t\troute: inferred.route,\n\t\t},\n\t\towner: parseCommaSeparated(response.owner || \"\"),\n\t\ttags: parseCommaSeparated(response.tags || \"\"),\n\t}\n}\n\n/**\n * Infer screen metadata from the route file path\n */\nfunction inferScreenMeta(\n\trouteDir: string,\n\troutesPattern: string,\n): InferredScreenMeta {\n\t// Extract base directory from pattern (e.g., \"src/pages\" from \"src/pages/**/page.tsx\")\n\tconst patternBase = routesPattern.split(\"*\")[0]?.replace(/\\/$/, \"\") ?? \"\"\n\n\t// Get relative path from pattern base\n\tconst relativePath = relative(patternBase, routeDir)\n\n\t// Handle root route\n\tif (!relativePath || relativePath === \".\") {\n\t\treturn {\n\t\t\tid: \"home\",\n\t\t\ttitle: \"Home\",\n\t\t\troute: \"/\",\n\t\t}\n\t}\n\n\t// Clean up path segments (remove route groups like (marketing), handle dynamic segments)\n\tconst segments = relativePath\n\t\t.split(\"/\")\n\t\t.filter((s) => s && !s.startsWith(\"(\") && !s.endsWith(\")\"))\n\t\t.map((s) =>\n\t\t\ts.replace(/^\\[\\.\\.\\..*\\]$/, \"catchall\").replace(/^\\[(.+)\\]$/, \"$1\"),\n\t\t)\n\n\t// Generate ID from segments (e.g., \"billing.invoice.detail\")\n\tconst id = segments.join(\".\")\n\n\t// Generate title from last segment (e.g., \"Invoice Detail\" from \"invoice-detail\")\n\tconst lastSegment = segments[segments.length - 1] || \"home\"\n\tconst title = lastSegment\n\t\t.split(/[-_]/)\n\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n\t\t.join(\" \")\n\n\t// Generate route from path (e.g., \"/billing/invoice/:id\")\n\tconst routeSegments = relativePath\n\t\t.split(\"/\")\n\t\t.filter((s) => s && !s.startsWith(\"(\") && !s.endsWith(\")\"))\n\t\t.map((s) => {\n\t\t\t// Convert [id] to :id, [...slug] to *\n\t\t\tif (s.startsWith(\"[...\") && s.endsWith(\"]\")) {\n\t\t\t\treturn \"*\"\n\t\t\t}\n\t\t\tif (s.startsWith(\"[\") && s.endsWith(\"]\")) {\n\t\t\t\treturn `:${s.slice(1, -1)}`\n\t\t\t}\n\t\t\treturn s\n\t\t})\n\tconst route = `/${routeSegments.join(\"/\")}`\n\n\treturn { id, title, route }\n}\n\ntype GenerateOptions = Partial<\n\tPick<Screen, \"owner\" | \"tags\" | \"dependsOn\" | \"next\">\n>\n\n/**\n * Generate screen.meta.ts file content\n */\nfunction generateScreenMetaContent(\n\tmeta: InferredScreenMeta,\n\toptions?: GenerateOptions,\n): string {\n\t// Use provided values or infer defaults\n\tconst owner = options?.owner ?? []\n\tconst tags =\n\t\toptions?.tags && options.tags.length > 0\n\t\t\t? options.tags\n\t\t\t: [meta.id.split(\".\")[0] || \"general\"]\n\tconst dependsOn = options?.dependsOn ?? []\n\tconst next = options?.next ?? []\n\n\tconst ownerStr =\n\t\towner.length > 0 ? `[${owner.map((o) => `\"${o}\"`).join(\", \")}]` : \"[]\"\n\tconst tagsStr = `[${tags.map((t) => `\"${t}\"`).join(\", \")}]`\n\tconst dependsOnStr =\n\t\tdependsOn.length > 0\n\t\t\t? `[${dependsOn.map((d) => `\"${d}\"`).join(\", \")}]`\n\t\t\t: \"[]\"\n\tconst nextStr =\n\t\tnext.length > 0 ? `[${next.map((n) => `\"${n}\"`).join(\", \")}]` : \"[]\"\n\n\t// Generate dependsOn comment based on whether APIs were detected\n\tconst dependsOnComment =\n\t\tdependsOn.length > 0\n\t\t\t? \"// Auto-detected API dependencies (add more as needed)\"\n\t\t\t: `// APIs/services this screen depends on (for impact analysis)\n\t// Example: [\"UserAPI.getProfile\", \"PaymentService.checkout\"]`\n\n\t// Generate next comment based on whether navigation was detected\n\tconst nextComment =\n\t\tnext.length > 0\n\t\t\t? \"// Auto-detected navigation targets (add more as needed)\"\n\t\t\t: \"// Screen IDs this screen can navigate to\"\n\n\treturn `import { defineScreen } from \"@screenbook/core\"\n\nexport const screen = defineScreen({\n\tid: \"${meta.id}\",\n\ttitle: \"${meta.title}\",\n\troute: \"${meta.route}\",\n\n\t// Team or individual responsible for this screen\n\towner: ${ownerStr},\n\n\t// Tags for filtering in the catalog\n\ttags: ${tagsStr},\n\n\t${dependsOnComment}\n\tdependsOn: ${dependsOnStr},\n\n\t// Screen IDs that can navigate to this screen\n\tentryPoints: [],\n\n\t${nextComment}\n\tnext: ${nextStr},\n})\n`\n}\n","import type { Screen } from \"@screenbook/core\"\n\nexport interface TransitiveDependency {\n\tscreen: Screen\n\tpath: string[]\n}\n\nexport interface ImpactResult {\n\tapi: string\n\tdirect: Screen[]\n\ttransitive: TransitiveDependency[]\n\ttotalCount: number\n}\n\n/**\n * Check if a screen's dependsOn matches the API name (supports partial matching).\n * - \"InvoiceAPI\" matches \"InvoiceAPI.getDetail\"\n * - \"InvoiceAPI.getDetail\" matches \"InvoiceAPI.getDetail\"\n */\nfunction matchesDependency(dependency: string, apiName: string): boolean {\n\t// Exact match\n\tif (dependency === apiName) {\n\t\treturn true\n\t}\n\t// Partial match: apiName is a prefix of dependency\n\tif (dependency.startsWith(`${apiName}.`)) {\n\t\treturn true\n\t}\n\t// Partial match: dependency is a prefix of apiName\n\tif (apiName.startsWith(`${dependency}.`)) {\n\t\treturn true\n\t}\n\treturn false\n}\n\n/**\n * Find screens that directly depend on the given API.\n */\nfunction findDirectDependents(screens: Screen[], apiName: string): Screen[] {\n\treturn screens.filter((screen) =>\n\t\tscreen.dependsOn?.some((dep) => matchesDependency(dep, apiName)),\n\t)\n}\n\n/**\n * Build a reverse navigation graph: screenId -> screens that can navigate to it.\n * This is built from the `entryPoints` field.\n */\nfunction _buildReverseNavigationGraph(\n\tscreens: Screen[],\n): Map<string, Set<string>> {\n\tconst graph = new Map<string, Set<string>>()\n\n\tfor (const screen of screens) {\n\t\t// entryPoints lists screens that can navigate TO this screen\n\t\t// So we want to find screens that navigate FROM here\n\t\t// Actually, we need to reverse: if A.entryPoints includes B,\n\t\t// then B can navigate to A, so B is affected if A is affected\n\n\t\t// For transitive analysis:\n\t\t// If screen A depends on API, and screen B has entryPoints: [\"A\"],\n\t\t// then B can navigate to A, meaning users can reach A from B\n\t\t// So if A is impacted, we should show B as transitively impacted\n\n\t\tif (!screen.entryPoints) continue\n\n\t\tfor (const entryPoint of screen.entryPoints) {\n\t\t\tif (!graph.has(screen.id)) {\n\t\t\t\tgraph.set(screen.id, new Set())\n\t\t\t}\n\t\t\tgraph.get(screen.id)?.add(entryPoint)\n\t\t}\n\t}\n\n\treturn graph\n}\n\n/**\n * Build a navigation graph from `next` field: screenId -> screens it can navigate to.\n */\nfunction buildNavigationGraph(screens: Screen[]): Map<string, Set<string>> {\n\tconst graph = new Map<string, Set<string>>()\n\n\tfor (const screen of screens) {\n\t\tif (!screen.next) continue\n\n\t\tif (!graph.has(screen.id)) {\n\t\t\tgraph.set(screen.id, new Set())\n\t\t}\n\t\tfor (const nextId of screen.next) {\n\t\t\tgraph.get(screen.id)?.add(nextId)\n\t\t}\n\t}\n\n\treturn graph\n}\n\n/**\n * Find all transitive dependents using BFS.\n * A screen is transitively dependent if it can navigate to a directly dependent screen.\n */\nfunction findTransitiveDependents(\n\tscreens: Screen[],\n\tdirectDependentIds: Set<string>,\n\tmaxDepth: number,\n): TransitiveDependency[] {\n\tconst _screenMap = new Map(screens.map((s) => [s.id, s]))\n\tconst navigationGraph = buildNavigationGraph(screens)\n\tconst transitive: TransitiveDependency[] = []\n\tconst visited = new Set<string>()\n\n\t// For each screen, check if it can reach a directly dependent screen\n\tfor (const screen of screens) {\n\t\tif (directDependentIds.has(screen.id)) {\n\t\t\tcontinue // Skip direct dependents\n\t\t}\n\n\t\tconst path = findPathToDirectDependent(\n\t\t\tscreen.id,\n\t\t\tdirectDependentIds,\n\t\t\tnavigationGraph,\n\t\t\tmaxDepth,\n\t\t\tnew Set(),\n\t\t)\n\n\t\tif (path && !visited.has(screen.id)) {\n\t\t\tvisited.add(screen.id)\n\t\t\ttransitive.push({\n\t\t\t\tscreen,\n\t\t\t\tpath,\n\t\t\t})\n\t\t}\n\t}\n\n\treturn transitive\n}\n\n/**\n * Find a path from a screen to any directly dependent screen using BFS.\n */\nfunction findPathToDirectDependent(\n\tstartId: string,\n\ttargetIds: Set<string>,\n\tgraph: Map<string, Set<string>>,\n\tmaxDepth: number,\n\tvisited: Set<string>,\n): string[] | null {\n\tif (visited.has(startId)) {\n\t\treturn null\n\t}\n\n\tconst queue: Array<{ id: string; path: string[] }> = [\n\t\t{ id: startId, path: [startId] },\n\t]\n\tconst localVisited = new Set<string>([startId])\n\n\twhile (queue.length > 0) {\n\t\tconst current = queue.shift()\n\t\tif (!current) break\n\n\t\tif (current.path.length > maxDepth + 1) {\n\t\t\tcontinue\n\t\t}\n\n\t\tconst neighbors = graph.get(current.id)\n\t\tif (!neighbors) {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor (const neighborId of neighbors) {\n\t\t\tif (localVisited.has(neighborId)) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst newPath = [...current.path, neighborId]\n\n\t\t\t// Check if path exceeds maxDepth (path includes start, so maxDepth+1 is the max length)\n\t\t\tif (newPath.length > maxDepth + 1) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (targetIds.has(neighborId)) {\n\t\t\t\treturn newPath\n\t\t\t}\n\n\t\t\tlocalVisited.add(neighborId)\n\t\t\tqueue.push({ id: neighborId, path: newPath })\n\t\t}\n\t}\n\n\treturn null\n}\n\n/**\n * Analyze the impact of a change to an API on the screen catalog.\n */\nexport function analyzeImpact(\n\tscreens: Screen[],\n\tapiName: string,\n\tmaxDepth = 3,\n): ImpactResult {\n\t// Find direct dependents\n\tconst direct = findDirectDependents(screens, apiName)\n\tconst directIds = new Set(direct.map((s) => s.id))\n\n\t// Find transitive dependents\n\tconst transitive = findTransitiveDependents(screens, directIds, maxDepth)\n\n\treturn {\n\t\tapi: apiName,\n\t\tdirect,\n\t\ttransitive,\n\t\ttotalCount: direct.length + transitive.length,\n\t}\n}\n\n/**\n * Format the impact result as text output.\n */\nexport function formatImpactText(result: ImpactResult): string {\n\tconst lines: string[] = []\n\n\tlines.push(`Impact Analysis: ${result.api}`)\n\tlines.push(\"\")\n\n\tif (result.direct.length > 0) {\n\t\tlines.push(\n\t\t\t`Direct (${result.direct.length} screen${result.direct.length > 1 ? \"s\" : \"\"}):`,\n\t\t)\n\t\tfor (const screen of result.direct) {\n\t\t\tconst owner = screen.owner?.length ? ` [${screen.owner.join(\", \")}]` : \"\"\n\t\t\tlines.push(` - ${screen.id} ${screen.route}${owner}`)\n\t\t}\n\t\tlines.push(\"\")\n\t}\n\n\tif (result.transitive.length > 0) {\n\t\tlines.push(\n\t\t\t`Transitive (${result.transitive.length} screen${result.transitive.length > 1 ? \"s\" : \"\"}):`,\n\t\t)\n\t\tfor (const { path } of result.transitive) {\n\t\t\tlines.push(` - ${path.join(\" -> \")}`)\n\t\t}\n\t\tlines.push(\"\")\n\t}\n\n\tif (result.totalCount === 0) {\n\t\tlines.push(\"No screens depend on this API.\")\n\t\tlines.push(\"\")\n\t} else {\n\t\tlines.push(\n\t\t\t`Total: ${result.totalCount} screen${result.totalCount > 1 ? \"s\" : \"\"} affected`,\n\t\t)\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n\n/**\n * Format the impact result as JSON output.\n */\nexport function formatImpactJson(result: ImpactResult): string {\n\treturn JSON.stringify(\n\t\t{\n\t\t\tapi: result.api,\n\t\t\tsummary: {\n\t\t\t\tdirectCount: result.direct.length,\n\t\t\t\ttransitiveCount: result.transitive.length,\n\t\t\t\ttotalCount: result.totalCount,\n\t\t\t},\n\t\t\tdirect: result.direct.map((s) => ({\n\t\t\t\tid: s.id,\n\t\t\t\ttitle: s.title,\n\t\t\t\troute: s.route,\n\t\t\t\towner: s.owner,\n\t\t\t})),\n\t\t\ttransitive: result.transitive.map(({ screen, path }) => ({\n\t\t\t\tid: screen.id,\n\t\t\t\ttitle: screen.title,\n\t\t\t\troute: screen.route,\n\t\t\t\tpath,\n\t\t\t})),\n\t\t},\n\t\tnull,\n\t\t2,\n\t)\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport {\n\tanalyzeImpact,\n\tformatImpactJson,\n\tformatImpactText,\n} from \"../utils/impactAnalysis.js\"\nimport { logger, setVerbose } from \"../utils/logger.js\"\n\nexport const impactCommand = define({\n\tname: \"impact\",\n\tdescription: \"Analyze which screens depend on a specific API/service\",\n\targs: {\n\t\tapi: {\n\t\t\ttype: \"positional\",\n\t\t\tdescription:\n\t\t\t\t\"API or service name to analyze (e.g., InvoiceAPI.getDetail)\",\n\t\t\trequired: true,\n\t\t},\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tformat: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Output format: text (default) or json\",\n\t\t\tdefault: \"text\",\n\t\t},\n\t\tdepth: {\n\t\t\ttype: \"number\",\n\t\t\tshort: \"d\",\n\t\t\tdescription: \"Maximum depth for transitive dependencies\",\n\t\t\tdefault: 3,\n\t\t},\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output including stack traces\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tsetVerbose(ctx.values.verbose)\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst cwd = process.cwd()\n\n\t\tconst apiName = ctx.values.api\n\t\tif (!apiName) {\n\t\t\tlogger.errorWithHelp(ERRORS.API_NAME_REQUIRED)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst format = ctx.values.format ?? \"text\"\n\t\tconst depth = ctx.values.depth ?? 3\n\n\t\t// Load screens.json\n\t\tconst screensPath = join(cwd, config.outDir, \"screens.json\")\n\n\t\tif (!existsSync(screensPath)) {\n\t\t\tlogger.errorWithHelp(ERRORS.SCREENS_NOT_FOUND)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlet screens: Screen[]\n\t\ttry {\n\t\t\tconst content = readFileSync(screensPath, \"utf-8\")\n\t\t\tscreens = JSON.parse(content) as Screen[]\n\t\t} catch (error) {\n\t\t\tlogger.errorWithHelp({\n\t\t\t\t...ERRORS.SCREENS_PARSE_ERROR,\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tif (screens.length === 0) {\n\t\t\tlogger.warn(\"No screens found in the catalog.\")\n\t\t\tlogger.blank()\n\t\t\tlogger.log(\"Run 'screenbook generate' to create screen.meta.ts files,\")\n\t\t\tlogger.log(\"then 'screenbook build' to generate the catalog.\")\n\t\t\treturn\n\t\t}\n\n\t\t// Analyze impact\n\t\tconst result = analyzeImpact(screens, apiName, depth)\n\n\t\t// Output result\n\t\tif (format === \"json\") {\n\t\t\tlogger.log(formatImpactJson(result))\n\t\t} else {\n\t\t\tlogger.log(formatImpactText(result))\n\t\t}\n\t},\n})\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport prompts from \"prompts\"\n\nexport interface FrameworkInfo {\n\tname: string\n\troutesPattern: string\n\tmetaPattern: string\n}\n\ninterface FrameworkDefinition extends FrameworkInfo {\n\tpackages: string[]\n\tconfigFiles: string[]\n\t/**\n\t * Additional check to distinguish variants (e.g., App Router vs Pages Router)\n\t */\n\tcheck?: (cwd: string) => boolean\n}\n\nconst FRAMEWORKS: FrameworkDefinition[] = [\n\t{\n\t\tname: \"Next.js (App Router)\",\n\t\tpackages: [\"next\"],\n\t\tconfigFiles: [\"next.config.js\", \"next.config.mjs\", \"next.config.ts\"],\n\t\troutesPattern: \"app/**/page.tsx\",\n\t\tmetaPattern: \"app/**/screen.meta.ts\",\n\t\tcheck: (cwd) =>\n\t\t\texistsSync(join(cwd, \"app\")) || existsSync(join(cwd, \"src/app\")),\n\t},\n\t{\n\t\tname: \"Next.js (Pages Router)\",\n\t\tpackages: [\"next\"],\n\t\tconfigFiles: [\"next.config.js\", \"next.config.mjs\", \"next.config.ts\"],\n\t\troutesPattern: \"pages/**/*.tsx\",\n\t\tmetaPattern: \"pages/**/screen.meta.ts\",\n\t\tcheck: (cwd) =>\n\t\t\texistsSync(join(cwd, \"pages\")) || existsSync(join(cwd, \"src/pages\")),\n\t},\n\t{\n\t\tname: \"Remix\",\n\t\tpackages: [\"@remix-run/react\", \"remix\"],\n\t\tconfigFiles: [\"remix.config.js\", \"vite.config.ts\"],\n\t\troutesPattern: \"app/routes/**/*.tsx\",\n\t\tmetaPattern: \"app/routes/**/screen.meta.ts\",\n\t},\n\t{\n\t\tname: \"Nuxt\",\n\t\tpackages: [\"nuxt\"],\n\t\tconfigFiles: [\"nuxt.config.ts\", \"nuxt.config.js\", \"nuxt.config.mjs\"],\n\t\troutesPattern: \"pages/**/*.vue\",\n\t\tmetaPattern: \"pages/**/screen.meta.ts\",\n\t\tcheck: (cwd) => {\n\t\t\t// Nuxt 4 uses app/pages, Nuxt 3 uses pages\n\t\t\tif (existsSync(join(cwd, \"app/pages\"))) {\n\t\t\t\treturn false // Will be handled by Nuxt 4 definition\n\t\t\t}\n\t\t\treturn existsSync(join(cwd, \"pages\"))\n\t\t},\n\t},\n\t{\n\t\tname: \"Nuxt 4\",\n\t\tpackages: [\"nuxt\"],\n\t\tconfigFiles: [\"nuxt.config.ts\", \"nuxt.config.js\"],\n\t\troutesPattern: \"app/pages/**/*.vue\",\n\t\tmetaPattern: \"app/pages/**/screen.meta.ts\",\n\t\tcheck: (cwd) => existsSync(join(cwd, \"app/pages\")),\n\t},\n\t{\n\t\tname: \"Astro\",\n\t\tpackages: [\"astro\"],\n\t\tconfigFiles: [\n\t\t\t\"astro.config.mjs\",\n\t\t\t\"astro.config.js\",\n\t\t\t\"astro.config.ts\",\n\t\t\t\"astro.config.cjs\",\n\t\t],\n\t\troutesPattern: \"src/pages/**/*.astro\",\n\t\tmetaPattern: \"src/pages/**/screen.meta.ts\",\n\t},\n\t{\n\t\tname: \"SolidStart\",\n\t\tpackages: [\"@solidjs/start\"],\n\t\tconfigFiles: [\"app.config.ts\", \"app.config.js\"],\n\t\troutesPattern: \"src/routes/**/*.tsx\",\n\t\tmetaPattern: \"src/routes/**/screen.meta.ts\",\n\t\tcheck: (cwd) => existsSync(join(cwd, \"src/routes\")),\n\t},\n\t{\n\t\tname: \"QwikCity\",\n\t\tpackages: [\"@builder.io/qwik-city\"],\n\t\tconfigFiles: [\"vite.config.ts\", \"vite.config.js\", \"vite.config.mjs\"],\n\t\t// QwikCity uses index.tsx files as page components (e.g., about/index.tsx not about.tsx)\n\t\troutesPattern: \"src/routes/**/index.tsx\",\n\t\tmetaPattern: \"src/routes/**/screen.meta.ts\",\n\t\tcheck: (cwd) => existsSync(join(cwd, \"src/routes\")),\n\t},\n\t{\n\t\tname: \"TanStack Start\",\n\t\tpackages: [\"@tanstack/react-start\", \"@tanstack/start\"],\n\t\tconfigFiles: [\"app.config.ts\", \"app.config.js\"],\n\t\troutesPattern: \"src/routes/**/*.tsx\",\n\t\tmetaPattern: \"src/routes/**/screen.meta.ts\",\n\t\t// TanStack Start requires __root.tsx for file-based routing\n\t\tcheck: (cwd) => existsSync(join(cwd, \"src/routes/__root.tsx\")),\n\t},\n\t{\n\t\tname: \"Vite + Vue\",\n\t\tpackages: [\"vite\", \"vue\"],\n\t\tconfigFiles: [\"vite.config.ts\", \"vite.config.js\", \"vite.config.mjs\"],\n\t\troutesPattern: \"src/pages/**/*.vue\",\n\t\tmetaPattern: \"src/pages/**/screen.meta.ts\",\n\t\t// Check that react is NOT present to avoid matching React projects\n\t\tcheck: (cwd) => {\n\t\t\tconst packageJson = readPackageJson(cwd)\n\t\t\tif (!packageJson) return false\n\t\t\treturn !hasPackage(packageJson, \"react\")\n\t\t},\n\t},\n\t{\n\t\tname: \"Vite + React\",\n\t\tpackages: [\"vite\", \"react\"],\n\t\tconfigFiles: [\"vite.config.ts\", \"vite.config.js\", \"vite.config.mjs\"],\n\t\troutesPattern: \"src/pages/**/*.tsx\",\n\t\tmetaPattern: \"src/pages/**/screen.meta.ts\",\n\t},\n]\n\ninterface PackageJson {\n\tdependencies?: Record<string, string>\n\tdevDependencies?: Record<string, string>\n}\n\nfunction readPackageJson(cwd: string): PackageJson | null {\n\tconst packageJsonPath = join(cwd, \"package.json\")\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn null\n\t}\n\ttry {\n\t\tconst content = readFileSync(packageJsonPath, \"utf-8\")\n\t\treturn JSON.parse(content)\n\t} catch (error) {\n\t\tconsole.warn(\n\t\t\t`Warning: Failed to parse package.json at ${packageJsonPath}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t)\n\t\treturn null\n\t}\n}\n\nfunction hasPackage(packageJson: PackageJson, packageName: string): boolean {\n\treturn !!(\n\t\tpackageJson.dependencies?.[packageName] ||\n\t\tpackageJson.devDependencies?.[packageName]\n\t)\n}\n\nfunction hasConfigFile(cwd: string, configFiles: string[]): boolean {\n\treturn configFiles.some((file) => existsSync(join(cwd, file)))\n}\n\n/**\n * Auto-detect the frontend framework in a project directory.\n * Returns framework info if detected, null otherwise.\n */\nexport function detectFramework(cwd: string): FrameworkInfo | null {\n\tconst packageJson = readPackageJson(cwd)\n\tif (!packageJson) {\n\t\treturn null\n\t}\n\n\tfor (const framework of FRAMEWORKS) {\n\t\t// Check if required packages are present\n\t\tconst hasRequiredPackage = framework.packages.some((pkg) =>\n\t\t\thasPackage(packageJson, pkg),\n\t\t)\n\t\tif (!hasRequiredPackage) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Check for config files\n\t\tconst hasConfig = hasConfigFile(cwd, framework.configFiles)\n\t\tif (!hasConfig) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Run additional check if defined\n\t\tif (framework.check && !framework.check(cwd)) {\n\t\t\tcontinue\n\t\t}\n\n\t\treturn {\n\t\t\tname: framework.name,\n\t\t\troutesPattern: framework.routesPattern,\n\t\t\tmetaPattern: framework.metaPattern,\n\t\t}\n\t}\n\n\treturn null\n}\n\n/**\n * Interactive framework selection when auto-detection fails.\n */\nexport async function promptFrameworkSelection(): Promise<FrameworkInfo | null> {\n\tconst choices = FRAMEWORKS.filter(\n\t\t// Remove duplicates (e.g., Nuxt 4 vs Nuxt)\n\t\t(fw, idx, arr) =>\n\t\t\tarr.findIndex((f) => f.routesPattern === fw.routesPattern) === idx,\n\t).map((fw) => ({\n\t\ttitle: fw.name,\n\t\tvalue: fw,\n\t}))\n\n\tchoices.push({\n\t\ttitle: \"Other (manual configuration)\",\n\t\tvalue: null as unknown as FrameworkDefinition,\n\t})\n\n\tconst response = await prompts({\n\t\ttype: \"select\",\n\t\tname: \"framework\",\n\t\tmessage: \"Select your frontend framework:\",\n\t\tchoices,\n\t})\n\n\tif (!response.framework) {\n\t\treturn null\n\t}\n\n\treturn {\n\t\tname: response.framework.name,\n\t\troutesPattern: response.framework.routesPattern,\n\t\tmetaPattern: response.framework.metaPattern,\n\t}\n}\n\n/**\n * Detect framework or prompt user if detection fails.\n */\nexport async function detectOrPromptFramework(\n\tcwd: string,\n): Promise<FrameworkInfo | null> {\n\tconst detected = detectFramework(cwd)\n\tif (detected) {\n\t\treturn detected\n\t}\n\treturn promptFrameworkSelection()\n}\n","/**\n * Check if the current environment supports interactive prompts.\n * Returns false in CI environments or when stdin is not a TTY.\n */\nexport function isInteractive(): boolean {\n\t// Standard CI environment variables (check for any truthy value)\n\tif (process.env.CI || process.env.CONTINUOUS_INTEGRATION) {\n\t\treturn false\n\t}\n\n\t// CI-specific environment variables not covered by the generic CI flag\n\tif (\n\t\tprocess.env.GITHUB_ACTIONS ||\n\t\tprocess.env.GITLAB_CI ||\n\t\tprocess.env.JENKINS_URL\n\t) {\n\t\treturn false\n\t}\n\n\t// TTY check - false if stdin is not a terminal\n\treturn process.stdin.isTTY === true\n}\n","import { spawn } from \"node:child_process\"\nimport {\n\tcopyFileSync,\n\texistsSync,\n\tmkdirSync,\n\treadFileSync,\n\twriteFileSync,\n} from \"node:fs\"\nimport { createRequire } from \"node:module\"\nimport { dirname, join, resolve } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { createJiti } from \"jiti\"\nimport prompts from \"prompts\"\nimport { glob } from \"tinyglobby\"\nimport {\n\tdetectFramework,\n\ttype FrameworkInfo,\n\tpromptFrameworkSelection,\n} from \"../utils/detectFramework.js\"\nimport { isInteractive } from \"../utils/isInteractive.js\"\nimport { logger, setVerbose } from \"../utils/logger.js\"\nimport {\n\ttype GenerateFromRoutesPatternOptions,\n\tgenerateFromRoutesPattern,\n} from \"./generate.js\"\n\nexport function generateConfigTemplate(\n\tframework: FrameworkInfo | null,\n): string {\n\tif (framework) {\n\t\treturn `import { defineConfig } from \"@screenbook/core\"\n\nexport default defineConfig({\n\t// Auto-detected: ${framework.name}\n\tmetaPattern: \"${framework.metaPattern}\",\n\troutesPattern: \"${framework.routesPattern}\",\n\toutDir: \".screenbook\",\n})\n`\n\t}\n\n\t// Fallback template when no framework detected\n\treturn `import { defineConfig } from \"@screenbook/core\"\n\nexport default defineConfig({\n\t// Glob pattern for screen metadata files\n\tmetaPattern: \"src/**/screen.meta.ts\",\n\n\t// Glob pattern for route files (uncomment and adjust for your framework):\n\t// routesPattern: \"src/pages/**/page.tsx\", // Vite/React\n\t// routesPattern: \"app/**/page.tsx\", // Next.js App Router\n\t// routesPattern: \"pages/**/*.tsx\", // Next.js Pages Router\n\t// routesPattern: \"app/routes/**/*.tsx\", // Remix\n\t// routesPattern: \"pages/**/*.vue\", // Nuxt\n\t// routesPattern: \"src/pages/**/*.astro\", // Astro\n\n\toutDir: \".screenbook\",\n})\n`\n}\n\nfunction printValueProposition(): void {\n\tlogger.blank()\n\tlogger.log(logger.bold(\"What Screenbook gives you:\"))\n\tlogger.log(\" - Screen catalog with search & filter\")\n\tlogger.log(\" - Navigation graph visualization\")\n\tlogger.log(\" - Impact analysis (API -> affected screens)\")\n\tlogger.log(\" - CI lint for documentation coverage\")\n}\n\nfunction printNextSteps(hasRoutesPattern: boolean): void {\n\tlogger.blank()\n\tlogger.log(logger.bold(\"Next steps:\"))\n\tif (hasRoutesPattern) {\n\t\tlogger.log(\n\t\t\t` 1. Run ${logger.code(\"screenbook generate\")} to auto-create screen.meta.ts files`,\n\t\t)\n\t\tlogger.log(\n\t\t\t` 2. Run ${logger.code(\"screenbook dev\")} to start the UI server`,\n\t\t)\n\t} else {\n\t\tlogger.log(\" 1. Configure routesPattern in screenbook.config.ts\")\n\t\tlogger.log(\n\t\t\t` 2. Run ${logger.code(\"screenbook generate\")} to auto-create screen.meta.ts files`,\n\t\t)\n\t\tlogger.log(\n\t\t\t` 3. Run ${logger.code(\"screenbook dev\")} to start the UI server`,\n\t\t)\n\t}\n\tlogger.blank()\n\tlogger.log(\"screen.meta.ts files are created alongside your route files:\")\n\tlogger.blank()\n\tlogger.log(logger.dim(\" src/pages/dashboard/\"))\n\tlogger.log(logger.dim(\" page.tsx # Your route file\"))\n\tlogger.log(\n\t\tlogger.dim(\" screen.meta.ts # Auto-generated, customize as needed\"),\n\t)\n}\n\nexport interface ResolveOptionParams {\n\texplicitValue: boolean | undefined\n\tyesAll: boolean\n\tciMode: boolean\n\tciDefault: boolean\n\tpromptMessage: string\n}\n\n/**\n * Resolve a boolean option with priority order:\n * 1. Explicit flag (e.g., --generate or --no-generate) takes precedence\n * 2. -y flag enables all optional features\n * 3. Non-interactive environments (CI mode or no TTY) fall back to ciDefault\n * 4. Otherwise, prompt the user interactively\n */\nexport async function resolveOption(\n\tparams: ResolveOptionParams,\n): Promise<boolean> {\n\tconst { explicitValue, yesAll, ciMode, ciDefault, promptMessage } = params\n\n\t// Priority 1: Explicit flag takes precedence\n\tif (explicitValue !== undefined) {\n\t\treturn explicitValue\n\t}\n\n\t// Priority 2: -y flag answers yes to all\n\tif (yesAll) {\n\t\treturn true\n\t}\n\n\t// Priority 3: Non-interactive environments use ciDefault\n\tif (ciMode || !isInteractive()) {\n\t\treturn ciDefault\n\t}\n\n\t// Priority 4: Interactive prompt\n\tconst response = await prompts({\n\t\ttype: \"confirm\",\n\t\tname: \"value\",\n\t\tmessage: promptMessage,\n\t\tinitial: true,\n\t})\n\n\t// Handle user cancellation (Ctrl+C)\n\tif (response.value === undefined) {\n\t\tlogger.blank()\n\t\tlogger.info(\"Operation cancelled\")\n\t\tprocess.exit(0)\n\t}\n\n\treturn response.value\n}\n\nasync function countRouteFiles(\n\troutesPattern: string,\n\tcwd: string,\n): Promise<number> {\n\tconst files = await glob(routesPattern, { cwd })\n\treturn files.length\n}\n\nasync function runGenerate(routesPattern: string, cwd: string): Promise<void> {\n\tconst options: GenerateFromRoutesPatternOptions = {\n\t\tdryRun: false,\n\t\tforce: false,\n\t\tinteractive: false,\n\t\tignore: [\"**/node_modules/**\"],\n\t\tdetectApi: false,\n\t\tdetectNavigation: false,\n\t\tapiIntegration: undefined,\n\t}\n\n\tawait generateFromRoutesPattern(routesPattern, cwd, options)\n}\n\nasync function buildScreensForDev(\n\tmetaPattern: string,\n\toutDir: string,\n\tcwd: string,\n): Promise<void> {\n\tconst files = await glob(metaPattern, {\n\t\tcwd,\n\t\tignore: [\"**/node_modules/**\"],\n\t})\n\n\tif (files.length === 0) {\n\t\tlogger.warn(`No screen.meta.ts files found matching: ${metaPattern}`)\n\t\treturn\n\t}\n\n\ttype ScreenWithFilePath = Screen & { filePath: string }\n\n\tconst jiti = createJiti(cwd)\n\tconst screens: ScreenWithFilePath[] = []\n\n\tfor (const file of files) {\n\t\tconst absolutePath = resolve(cwd, file)\n\n\t\ttry {\n\t\t\tconst module = (await jiti.import(absolutePath)) as { screen?: Screen }\n\t\t\tif (module.screen) {\n\t\t\t\tscreens.push({ ...module.screen, filePath: absolutePath })\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Log failed files so users can diagnose issues\n\t\t\tlogger.itemWarn(`Failed to load ${file}`)\n\t\t\tif (error instanceof Error) {\n\t\t\t\tlogger.log(` ${logger.dim(error.message)}`)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst outputPath = join(cwd, outDir, \"screens.json\")\n\tconst outputDir = dirname(outputPath)\n\n\tif (!existsSync(outputDir)) {\n\t\tmkdirSync(outputDir, { recursive: true })\n\t}\n\n\twriteFileSync(outputPath, JSON.stringify(screens, null, 2))\n}\n\nfunction resolveUiPackage(): string | null {\n\ttry {\n\t\tconst require = createRequire(import.meta.url)\n\t\tconst uiPackageJson = require.resolve(\"@screenbook/ui/package.json\")\n\t\treturn dirname(uiPackageJson)\n\t} catch {\n\t\tconst possiblePaths = [\n\t\t\tjoin(process.cwd(), \"node_modules\", \"@screenbook\", \"ui\"),\n\t\t\tjoin(process.cwd(), \"..\", \"ui\"),\n\t\t\tjoin(process.cwd(), \"packages\", \"ui\"),\n\t\t]\n\n\t\tfor (const p of possiblePaths) {\n\t\t\tif (existsSync(join(p, \"package.json\"))) {\n\t\t\t\treturn p\n\t\t\t}\n\t\t}\n\n\t\treturn null\n\t}\n}\n\nasync function startDevServer(\n\tmetaPattern: string,\n\toutDir: string,\n\tcwd: string,\n\tport: string,\n): Promise<void> {\n\t// Build screens first\n\tawait buildScreensForDev(metaPattern, outDir, cwd)\n\n\t// Find the UI package\n\tconst uiPackagePath = resolveUiPackage()\n\n\tif (!uiPackagePath) {\n\t\tlogger.warn(\"Could not find @screenbook/ui package\")\n\t\tlogger.log(\n\t\t\t` Run ${logger.code(\"npm install @screenbook/ui\")} to install it`,\n\t\t)\n\t\treturn\n\t}\n\n\t// Copy screens.json to UI package\n\tconst screensJsonPath = join(cwd, outDir, \"screens.json\")\n\tconst uiScreensDir = join(uiPackagePath, \".screenbook\")\n\n\tif (!existsSync(uiScreensDir)) {\n\t\tmkdirSync(uiScreensDir, { recursive: true })\n\t}\n\n\tif (existsSync(screensJsonPath)) {\n\t\tcopyFileSync(screensJsonPath, join(uiScreensDir, \"screens.json\"))\n\t}\n\n\t// Start Astro dev server\n\tlogger.blank()\n\tlogger.info(\n\t\t`Starting UI server on ${logger.highlight(`http://localhost:${port}`)}`,\n\t)\n\tlogger.blank()\n\n\tconst astroProcess = spawn(\"npx\", [\"astro\", \"dev\", \"--port\", port], {\n\t\tcwd: uiPackagePath,\n\t\tstdio: \"inherit\",\n\t\tshell: true,\n\t})\n\n\tastroProcess.on(\"error\", (error) => {\n\t\tlogger.error(`Failed to start server: ${error.message}`)\n\t\tprocess.exit(1)\n\t})\n\n\tastroProcess.on(\"close\", (code) => {\n\t\tprocess.exit(code ?? 0)\n\t})\n\n\t// Handle graceful shutdown\n\tprocess.on(\"SIGINT\", () => {\n\t\tastroProcess.kill(\"SIGINT\")\n\t})\n\n\tprocess.on(\"SIGTERM\", () => {\n\t\tastroProcess.kill(\"SIGTERM\")\n\t})\n}\n\nexport const initCommand = define({\n\tname: \"init\",\n\tdescription: \"Initialize Screenbook in a project\",\n\targs: {\n\t\tforce: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Overwrite existing files\",\n\t\t\tdefault: false,\n\t\t},\n\t\tskipDetect: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Skip framework auto-detection\",\n\t\t\tdefault: false,\n\t\t},\n\t\tgenerate: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Auto-generate screen.meta.ts files (--no-generate to skip)\",\n\t\t\tdefault: undefined,\n\t\t\tnegatable: true,\n\t\t},\n\t\tdev: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Start development server after init (--no-dev to skip)\",\n\t\t\tdefault: undefined,\n\t\t\tnegatable: true,\n\t\t},\n\t\tyes: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"y\",\n\t\t\tdescription: \"Answer yes to all prompts\",\n\t\t\tdefault: false,\n\t\t},\n\t\tci: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"CI mode (no prompts, generate only)\",\n\t\t\tdefault: false,\n\t\t},\n\t\tport: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"p\",\n\t\t\tdescription: \"Port for the dev server\",\n\t\t\tdefault: \"4321\",\n\t\t},\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output including stack traces\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tsetVerbose(ctx.values.verbose)\n\t\tconst cwd = process.cwd()\n\t\tconst force = ctx.values.force ?? false\n\t\tconst skipDetect = ctx.values.skipDetect ?? false\n\t\tconst generateFlag = ctx.values.generate\n\t\tconst devFlag = ctx.values.dev\n\t\tconst yesAll = ctx.values.yes ?? false\n\t\tconst ciMode = ctx.values.ci ?? false\n\t\tconst port = ctx.values.port ?? \"4321\"\n\n\t\tlogger.info(\"Initializing Screenbook...\")\n\t\tlogger.blank()\n\n\t\t// Framework detection\n\t\tlet framework: FrameworkInfo | null = null\n\n\t\tif (!skipDetect) {\n\t\t\tframework = detectFramework(cwd)\n\n\t\t\tif (framework) {\n\t\t\t\tlogger.itemSuccess(`Detected: ${framework.name}`)\n\t\t\t} else {\n\t\t\t\tlogger.log(\" Could not auto-detect framework\")\n\n\t\t\t\t// Only prompt for framework selection in interactive mode\n\t\t\t\tif (!ciMode && isInteractive()) {\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tframework = await promptFrameworkSelection()\n\n\t\t\t\t\tif (framework) {\n\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\tlogger.itemSuccess(`Selected: ${framework.name}`)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Create screenbook.config.ts\n\t\tconst configPath = join(cwd, \"screenbook.config.ts\")\n\t\tif (!force && existsSync(configPath)) {\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"-\")} screenbook.config.ts already exists ${logger.dim(\"(skipped)\")}`,\n\t\t\t)\n\t\t} else {\n\t\t\tconst configContent = generateConfigTemplate(framework)\n\t\t\twriteFileSync(configPath, configContent)\n\t\t\tlogger.itemSuccess(\"Created screenbook.config.ts\")\n\t\t}\n\n\t\t// Update .gitignore\n\t\tconst gitignorePath = join(cwd, \".gitignore\")\n\t\tconst screenbookIgnore = \".screenbook\"\n\n\t\tif (existsSync(gitignorePath)) {\n\t\t\tconst gitignoreContent = readFileSync(gitignorePath, \"utf-8\")\n\t\t\tif (!gitignoreContent.includes(screenbookIgnore)) {\n\t\t\t\tconst newContent = `${gitignoreContent.trimEnd()}\\n\\n# Screenbook\\n${screenbookIgnore}\\n`\n\t\t\t\twriteFileSync(gitignorePath, newContent)\n\t\t\t\tlogger.itemSuccess(\"Added .screenbook to .gitignore\")\n\t\t\t} else {\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(\"-\")} .screenbook already in .gitignore ${logger.dim(\"(skipped)\")}`,\n\t\t\t\t)\n\t\t\t}\n\t\t} else {\n\t\t\twriteFileSync(gitignorePath, `# Screenbook\\n${screenbookIgnore}\\n`)\n\t\t\tlogger.itemSuccess(\"Created .gitignore with .screenbook\")\n\t\t}\n\n\t\tlogger.blank()\n\t\tlogger.done(\"Screenbook initialized successfully!\")\n\n\t\t// If no framework detected or no routesPattern, show traditional next steps\n\t\tif (!framework?.routesPattern) {\n\t\t\tprintValueProposition()\n\t\t\tprintNextSteps(false)\n\t\t\treturn\n\t\t}\n\n\t\t// Count route files\n\t\tconst routeFileCount = await countRouteFiles(framework.routesPattern, cwd)\n\n\t\tif (routeFileCount === 0) {\n\t\t\tprintValueProposition()\n\t\t\tprintNextSteps(true)\n\t\t\treturn\n\t\t}\n\n\t\t// Prompt for generate\n\t\tlogger.blank()\n\t\tconst shouldGenerate = await resolveOption({\n\t\t\texplicitValue: generateFlag,\n\t\t\tyesAll,\n\t\t\tciMode,\n\t\t\tciDefault: true,\n\t\t\tpromptMessage: `Found ${routeFileCount} route files. Generate screen.meta.ts files?`,\n\t\t})\n\n\t\tif (!shouldGenerate) {\n\t\t\tprintValueProposition()\n\t\t\tprintNextSteps(true)\n\t\t\treturn\n\t\t}\n\n\t\t// Run generate\n\t\tlogger.blank()\n\t\tlogger.info(\"Generating screen metadata...\")\n\t\tlogger.blank()\n\n\t\tawait runGenerate(framework.routesPattern, cwd)\n\n\t\t// In CI mode, skip dev server\n\t\tif (ciMode) {\n\t\t\tlogger.blank()\n\t\t\tlogger.done(\"Initialization complete!\")\n\t\t\treturn\n\t\t}\n\n\t\t// Prompt for dev server\n\t\tlogger.blank()\n\t\tconst shouldDev = await resolveOption({\n\t\t\texplicitValue: devFlag,\n\t\t\tyesAll,\n\t\t\tciMode,\n\t\t\tciDefault: false,\n\t\t\tpromptMessage: \"Start the development server?\",\n\t\t})\n\n\t\tif (!shouldDev) {\n\t\t\tlogger.blank()\n\t\t\tlogger.log(logger.bold(\"Next step:\"))\n\t\t\tlogger.log(\n\t\t\t\t` Run ${logger.code(\"screenbook dev\")} to start the UI server`,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\t// Start dev server\n\t\tlogger.blank()\n\t\tlogger.info(\"Starting development server...\")\n\n\t\tawait startDevServer(framework.metaPattern, \".screenbook\", cwd, port)\n\t},\n})\n","import { isAbsolute, resolve } from \"node:path\"\nimport SwaggerParser from \"@apidevtools/swagger-parser\"\nimport type { OpenAPI, OpenAPIV3 } from \"openapi-types\"\n\n/**\n * Parsed OpenAPI specification with extracted API identifiers\n */\nexport interface ParsedOpenApiSpec {\n\t/** Source path/URL of the OpenAPI spec */\n\treadonly source: string\n\t/** Set of valid operationIds (e.g., \"getInvoiceById\") */\n\treadonly operationIds: ReadonlySet<string>\n\t/** Set of valid HTTP method + path combos (e.g., \"GET /api/invoices/{id}\") */\n\treadonly httpEndpoints: ReadonlySet<string>\n\t/** Map from lowercase key to original format for case-insensitive matching */\n\treadonly normalizedToOriginal: ReadonlyMap<string, string>\n}\n\n/**\n * Result of parsing multiple OpenAPI sources\n */\nexport interface OpenApiParseResult {\n\t/** Successfully parsed specifications */\n\treadonly specs: readonly ParsedOpenApiSpec[]\n\t/** Errors encountered during parsing */\n\treadonly errors: readonly OpenApiParseError[]\n}\n\n/**\n * Error encountered while parsing an OpenAPI source\n */\nexport interface OpenApiParseError {\n\t/** Source path/URL that failed to parse */\n\treadonly source: string\n\t/** Error message */\n\treadonly message: string\n}\n\n/**\n * Check if a string looks like a URL\n */\nfunction isUrl(source: string): boolean {\n\treturn source.startsWith(\"http://\") || source.startsWith(\"https://\")\n}\n\n/**\n * Resolve a source path relative to the current working directory\n */\nfunction resolveSource(source: string, cwd: string): string {\n\tif (isUrl(source)) {\n\t\treturn source\n\t}\n\tif (isAbsolute(source)) {\n\t\treturn source\n\t}\n\treturn resolve(cwd, source)\n}\n\n/**\n * HTTP methods to extract from OpenAPI specs\n */\nconst HTTP_METHODS = [\n\t\"get\",\n\t\"post\",\n\t\"put\",\n\t\"delete\",\n\t\"patch\",\n\t\"options\",\n\t\"head\",\n] as const\n\n/**\n * Extract API identifiers from a parsed OpenAPI document\n */\nfunction extractApiIdentifiers(\n\tapi: OpenAPI.Document,\n\tsource: string,\n): ParsedOpenApiSpec {\n\tconst operationIds = new Set<string>()\n\tconst httpEndpoints = new Set<string>()\n\tconst normalizedToOriginal = new Map<string, string>()\n\n\tconst paths = api.paths\n\tif (!paths) {\n\t\treturn { source, operationIds, httpEndpoints, normalizedToOriginal }\n\t}\n\n\tfor (const [path, pathItem] of Object.entries(paths)) {\n\t\tif (!pathItem || typeof pathItem !== \"object\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor (const method of HTTP_METHODS) {\n\t\t\tconst operation = (\n\t\t\t\tpathItem as Record<string, OpenAPIV3.OperationObject | undefined>\n\t\t\t)[method]\n\t\t\tif (!operation) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Extract operationId\n\t\t\tif (operation.operationId) {\n\t\t\t\toperationIds.add(operation.operationId)\n\t\t\t\t// Store lowercase mapping for case-insensitive matching\n\t\t\t\tnormalizedToOriginal.set(\n\t\t\t\t\toperation.operationId.toLowerCase(),\n\t\t\t\t\toperation.operationId,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Generate HTTP endpoint format: \"GET /api/invoices/{id}\"\n\t\t\tconst httpEndpoint = `${method.toUpperCase()} ${path}`\n\t\t\thttpEndpoints.add(httpEndpoint)\n\t\t\t// Store normalized version for matching (lowercase method)\n\t\t\tnormalizedToOriginal.set(httpEndpoint.toLowerCase(), httpEndpoint)\n\t\t}\n\t}\n\n\treturn { source, operationIds, httpEndpoints, normalizedToOriginal }\n}\n\n/**\n * Parse a single OpenAPI source (file or URL)\n */\nasync function parseOpenApiSource(\n\tsource: string,\n\tcwd: string,\n): Promise<ParsedOpenApiSpec> {\n\tconst resolvedSource = resolveSource(source, cwd)\n\tconst api = await SwaggerParser.parse(resolvedSource)\n\treturn extractApiIdentifiers(api, source)\n}\n\n/**\n * Parse multiple OpenAPI specification sources\n *\n * @param sources - Array of file paths or URLs to OpenAPI specifications\n * @param cwd - Current working directory for resolving relative paths\n * @returns Parsed specifications and any errors encountered\n *\n * @example\n * ```ts\n * const result = await parseOpenApiSpecs(\n * [\"./openapi.yaml\", \"https://api.example.com/openapi.json\"],\n * process.cwd()\n * )\n *\n * for (const spec of result.specs) {\n * console.log(`Parsed ${spec.source}:`)\n * console.log(` ${spec.operationIds.size} operation IDs`)\n * console.log(` ${spec.httpEndpoints.size} HTTP endpoints`)\n * }\n *\n * for (const error of result.errors) {\n * console.error(`Failed to parse ${error.source}: ${error.message}`)\n * }\n * ```\n */\nexport async function parseOpenApiSpecs(\n\tsources: readonly string[],\n\tcwd: string,\n): Promise<OpenApiParseResult> {\n\tconst specs: ParsedOpenApiSpec[] = []\n\tconst errors: OpenApiParseError[] = []\n\n\tfor (const source of sources) {\n\t\ttry {\n\t\t\tconst spec = await parseOpenApiSource(source, cwd)\n\t\t\tspecs.push(spec)\n\t\t} catch (error) {\n\t\t\terrors.push({\n\t\t\t\tsource,\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t}\n\t}\n\n\treturn { specs, errors }\n}\n\n/**\n * Get all valid API identifiers from parsed specs (for suggestions)\n */\nexport function getAllApiIdentifiers(\n\tspecs: readonly ParsedOpenApiSpec[],\n): string[] {\n\tconst identifiers: string[] = []\n\n\tfor (const spec of specs) {\n\t\tidentifiers.push(...spec.operationIds)\n\t\tidentifiers.push(...spec.httpEndpoints)\n\t}\n\n\treturn identifiers\n}\n","import type { Screen } from \"@screenbook/core\"\nimport {\n\tgetAllApiIdentifiers,\n\ttype ParsedOpenApiSpec,\n} from \"./openApiParser.js\"\nimport { findBestMatch } from \"./suggestions.js\"\n\n/**\n * Validation error for an invalid dependsOn reference\n */\nexport interface DependsOnValidationError {\n\t/** ID of the screen with the invalid reference */\n\treadonly screenId: string\n\t/** The invalid API reference */\n\treadonly invalidApi: string\n\t/** Suggested correction (if found via fuzzy matching) */\n\treadonly suggestion?: string\n}\n\n/**\n * Successful validation result (all references are valid)\n */\ninterface DependsOnValidationSuccess {\n\treadonly valid: true\n\treadonly errors: readonly []\n}\n\n/**\n * Failed validation result (some references are invalid)\n */\ninterface DependsOnValidationFailure {\n\treadonly valid: false\n\treadonly errors: readonly DependsOnValidationError[]\n}\n\n/**\n * Result of validating dependsOn references (discriminated union)\n */\nexport type DependsOnValidationResult =\n\t| DependsOnValidationSuccess\n\t| DependsOnValidationFailure\n\n/**\n * Normalize a dependsOn value for matching\n * - HTTP format: lowercase the method, keep path as-is\n * - operationId: return as-is for exact match first, then case-insensitive fallback\n */\nfunction normalizeForMatching(value: string): {\n\tnormalized: string\n\tisHttpFormat: boolean\n} {\n\t// Check if it looks like HTTP format: \"GET /path\" or \"POST /path\"\n\tconst httpMatch = value.match(/^(GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD)\\s+/i)\n\tif (httpMatch?.[1]) {\n\t\t// Normalize: lowercase method, keep path\n\t\tconst method = httpMatch[1].toLowerCase()\n\t\tconst path = value.slice(httpMatch[0].length)\n\t\treturn {\n\t\t\tnormalized: `${method} ${path}`,\n\t\t\tisHttpFormat: true,\n\t\t}\n\t}\n\n\t// Treat as operationId - return as-is (exact match attempted first, then case-insensitive)\n\treturn {\n\t\tnormalized: value,\n\t\tisHttpFormat: false,\n\t}\n}\n\n/**\n * Check if a dependsOn value matches any API in the parsed specs\n */\nfunction matchesOpenApiSpec(\n\tdependsOnValue: string,\n\tspecs: readonly ParsedOpenApiSpec[],\n): boolean {\n\tconst { normalized, isHttpFormat } = normalizeForMatching(dependsOnValue)\n\n\tfor (const spec of specs) {\n\t\tif (isHttpFormat) {\n\t\t\t// For HTTP format, check httpEndpoints (case-insensitive method via normalized)\n\t\t\t// We store normalized keys in normalizedToOriginal map\n\t\t\tif (spec.normalizedToOriginal.has(normalized)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t} else {\n\t\t\t// For operationId, exact match first\n\t\t\tif (spec.operationIds.has(dependsOnValue)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\t// Then case-insensitive match\n\t\t\tif (spec.normalizedToOriginal.has(normalized.toLowerCase())) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false\n}\n\n/**\n * Find a suggestion for an invalid API reference using fuzzy matching\n */\nfunction findApiSuggestion(\n\tinvalidApi: string,\n\tspecs: readonly ParsedOpenApiSpec[],\n): string | undefined {\n\tconst allIdentifiers = getAllApiIdentifiers(specs)\n\tif (allIdentifiers.length === 0) {\n\t\treturn undefined\n\t}\n\n\t// Use higher tolerance for API names since they can vary significantly\n\treturn findBestMatch(invalidApi, allIdentifiers, 0.5)\n}\n\n/**\n * Validate dependsOn references against OpenAPI specifications\n *\n * @param screens - Array of screen definitions to validate\n * @param specs - Parsed OpenAPI specifications to validate against\n * @returns Validation result with any errors found\n *\n * @example\n * ```ts\n * const screens = [\n * { id: \"invoice.detail\", dependsOn: [\"getInvoiceById\", \"unknownApi\"] },\n * ]\n * const specs = await parseOpenApiSpecs([\"./openapi.yaml\"], cwd)\n *\n * const result = validateDependsOnReferences(screens, specs.specs)\n * if (!result.valid) {\n * for (const error of result.errors) {\n * console.log(`${error.screenId}: ${error.invalidApi}`)\n * if (error.suggestion) {\n * console.log(` Did you mean \"${error.suggestion}\"?`)\n * }\n * }\n * }\n * ```\n */\nexport function validateDependsOnReferences(\n\tscreens: readonly Screen[],\n\tspecs: readonly ParsedOpenApiSpec[],\n): DependsOnValidationResult {\n\tconst errors: DependsOnValidationError[] = []\n\n\tfor (const screen of screens) {\n\t\tif (!screen.dependsOn || screen.dependsOn.length === 0) {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor (const dep of screen.dependsOn) {\n\t\t\tif (!matchesOpenApiSpec(dep, specs)) {\n\t\t\t\tconst suggestion = findApiSuggestion(dep, specs)\n\t\t\t\terrors.push({\n\t\t\t\t\tscreenId: screen.id,\n\t\t\t\t\tinvalidApi: dep,\n\t\t\t\t\tsuggestion,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\tif (errors.length === 0) {\n\t\treturn { valid: true, errors: [] as const }\n\t}\n\treturn { valid: false, errors }\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { dirname, join, relative, resolve } from \"node:path\"\nimport type { AdoptionConfig, Config, Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { minimatch } from \"minimatch\"\nimport { glob } from \"tinyglobby\"\nimport { parseAngularRouterConfig } from \"../utils/angularRouterParser.js\"\nimport { loadConfig } from \"../utils/config.js\"\nimport {\n\tdetectCycles,\n\tformatCycleWarnings,\n\tgetCycleSummary,\n} from \"../utils/cycleDetection.js\"\nimport { validateDependsOnReferences } from \"../utils/dependsOnValidation.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger, setVerbose } from \"../utils/logger.js\"\nimport { parseOpenApiSpecs } from \"../utils/openApiParser.js\"\nimport {\n\tdetectRouterType,\n\tparseReactRouterConfig,\n} from \"../utils/reactRouterParser.js\"\nimport type { FlatRoute, ParseResult } from \"../utils/routeParserUtils.js\"\nimport { flattenRoutes } from \"../utils/routeParserUtils.js\"\nimport { parseSolidRouterConfig } from \"../utils/solidRouterParser.js\"\nimport { parseTanStackRouterConfig } from \"../utils/tanstackRouterParser.js\"\nimport { parseVueRouterConfig } from \"../utils/vueRouterParser.js\"\n\nexport const lintCommand = define({\n\tname: \"lint\",\n\tdescription: \"Detect routes without screen.meta.ts files\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tallowCycles: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Suppress circular navigation warnings\",\n\t\t\tdefault: false,\n\t\t},\n\t\tstrict: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"s\",\n\t\t\tdescription: \"Fail on disallowed cycles\",\n\t\t\tdefault: false,\n\t\t},\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output including stack traces\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tsetVerbose(ctx.values.verbose)\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst cwd = process.cwd()\n\t\tconst adoption = config.adoption ?? { mode: \"full\" }\n\t\tlet hasWarnings = false\n\n\t\t// Check for routes configuration\n\t\tif (!config.routesPattern && !config.routesFile) {\n\t\t\tlogger.errorWithHelp(ERRORS.ROUTES_PATTERN_MISSING)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlogger.info(\"Linting screen metadata coverage...\")\n\t\tif (adoption.mode === \"progressive\") {\n\t\t\tlogger.log(`Mode: Progressive adoption`)\n\t\t\tif (adoption.includePatterns?.length) {\n\t\t\t\tlogger.log(`Checking: ${adoption.includePatterns.join(\", \")}`)\n\t\t\t}\n\t\t\tif (adoption.minimumCoverage != null) {\n\t\t\t\tlogger.log(`Minimum coverage: ${adoption.minimumCoverage}%`)\n\t\t\t}\n\t\t}\n\t\tlogger.blank()\n\n\t\t// Use routesFile mode (config-based routing)\n\t\tif (config.routesFile) {\n\t\t\tawait lintRoutesFile(\n\t\t\t\tconfig.routesFile,\n\t\t\t\tcwd,\n\t\t\t\tconfig,\n\t\t\t\tadoption,\n\t\t\t\tctx.values.allowCycles ?? false,\n\t\t\t\tctx.values.strict ?? false,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\t// Use routesPattern mode (file-based routing)\n\t\t// Find all route files\n\t\tlet routeFiles = await glob(config.routesPattern as string, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\n\t\t// In progressive mode, filter to only included patterns\n\t\tif (adoption.mode === \"progressive\" && adoption.includePatterns?.length) {\n\t\t\trouteFiles = routeFiles.filter((file) =>\n\t\t\t\tadoption.includePatterns?.some((pattern) => minimatch(file, pattern)),\n\t\t\t)\n\t\t}\n\n\t\tif (routeFiles.length === 0) {\n\t\t\tlogger.warn(`No route files found matching: ${config.routesPattern}`)\n\t\t\tif (adoption.mode === \"progressive\" && adoption.includePatterns?.length) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(`(filtered by includePatterns: ${adoption.includePatterns.join(\", \")})`)}`,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// Find all screen.meta.ts files\n\t\tconst metaFiles = await glob(config.metaPattern, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\n\t\t// Build a set of directories that have screen.meta.ts\n\t\tconst metaDirs = new Set<string>()\n\t\tfor (const metaFile of metaFiles) {\n\t\t\tmetaDirs.add(dirname(metaFile))\n\t\t}\n\n\t\t// Check each route file - simple colocation check\n\t\tconst missingMeta: string[] = []\n\t\tconst covered: string[] = []\n\n\t\tfor (const routeFile of routeFiles) {\n\t\t\tconst routeDir = dirname(routeFile)\n\n\t\t\t// Check if there's a screen.meta.ts in the same directory\n\t\t\tif (metaDirs.has(routeDir)) {\n\t\t\t\tcovered.push(routeFile)\n\t\t\t} else {\n\t\t\t\tmissingMeta.push(routeFile)\n\t\t\t}\n\t\t}\n\n\t\t// Report results\n\t\tconst total = routeFiles.length\n\t\tconst coveredCount = covered.length\n\t\tconst missingCount = missingMeta.length\n\t\tconst coveragePercent = Math.round((coveredCount / total) * 100)\n\n\t\tlogger.log(`Found ${total} route files`)\n\t\tlogger.log(`Coverage: ${coveredCount}/${total} (${coveragePercent}%)`)\n\t\tlogger.blank()\n\n\t\t// Determine if lint should fail\n\t\tconst minimumCoverage = adoption.minimumCoverage ?? 100\n\t\tconst passedCoverage = coveragePercent >= minimumCoverage\n\n\t\tif (missingCount > 0) {\n\t\t\tlogger.log(`Missing screen.meta.ts (${missingCount} files):`)\n\t\t\tlogger.blank()\n\n\t\t\tfor (const file of missingMeta) {\n\t\t\t\tconst suggestedMetaPath = join(dirname(file), \"screen.meta.ts\")\n\t\t\t\tlogger.itemError(file)\n\t\t\t\tlogger.log(` ${logger.dim(\"→\")} ${logger.path(suggestedMetaPath)}`)\n\t\t\t}\n\n\t\t\tlogger.blank()\n\t\t}\n\n\t\tif (!passedCoverage) {\n\t\t\tlogger.error(\n\t\t\t\t`Lint failed: Coverage ${coveragePercent}% is below minimum ${minimumCoverage}%`,\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t} else if (missingCount > 0) {\n\t\t\tlogger.success(\n\t\t\t\t`Coverage ${coveragePercent}% meets minimum ${minimumCoverage}%`,\n\t\t\t)\n\t\t\tif (adoption.mode === \"progressive\") {\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(\"Tip:\")} Increase minimumCoverage in config to gradually improve coverage`,\n\t\t\t\t)\n\t\t\t}\n\t\t} else {\n\t\t\tlogger.done(\"All routes have screen.meta.ts files\")\n\t\t}\n\n\t\t// Check for orphan screens (unreachable screens) and cycles\n\t\tconst screensPath = join(cwd, config.outDir, \"screens.json\")\n\t\tif (existsSync(screensPath)) {\n\t\t\ttry {\n\t\t\t\tconst content = readFileSync(screensPath, \"utf-8\")\n\t\t\t\tconst screens = JSON.parse(content) as Screen[]\n\n\t\t\t\t// Check for orphan screens\n\t\t\t\tconst orphans = findOrphanScreens(screens)\n\n\t\t\t\tif (orphans.length > 0) {\n\t\t\t\t\thasWarnings = true\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.warn(`Orphan screens detected (${orphans.length}):`)\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.log(\" These screens have no entryPoints and are not\")\n\t\t\t\t\tlogger.log(\" referenced in any other screen's 'next' array.\")\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tfor (const orphan of orphans) {\n\t\t\t\t\t\tlogger.itemWarn(`${orphan.id} ${logger.dim(orphan.route)}`)\n\t\t\t\t\t}\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t` ${logger.dim(\"Consider adding entryPoints or removing these screens.\")}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\t// Check for circular navigation\n\t\t\t\tif (!ctx.values.allowCycles) {\n\t\t\t\t\tconst cycleResult = detectCycles(screens)\n\t\t\t\t\tif (cycleResult.hasCycles) {\n\t\t\t\t\t\thasWarnings = true\n\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\tlogger.warn(getCycleSummary(cycleResult))\n\t\t\t\t\t\tlogger.log(formatCycleWarnings(cycleResult.cycles))\n\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\tif (cycleResult.disallowedCycles.length > 0) {\n\t\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t\t` ${logger.dim(\"Use 'allowCycles: true' in screen.meta.ts to allow intentional cycles.\")}`,\n\t\t\t\t\t\t\t)\n\n\t\t\t\t\t\t\tif (ctx.values.strict) {\n\t\t\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\t\t\tlogger.errorWithHelp(\n\t\t\t\t\t\t\t\t\tERRORS.CYCLES_DETECTED(cycleResult.disallowedCycles.length),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tprocess.exit(1)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Check for invalid navigation references\n\t\t\t\tconst invalidNavs = findInvalidNavigations(screens)\n\t\t\t\tif (invalidNavs.length > 0) {\n\t\t\t\t\thasWarnings = true\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.warn(`Invalid navigation targets (${invalidNavs.length}):`)\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\" These navigation references point to non-existent screens.\",\n\t\t\t\t\t)\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tfor (const inv of invalidNavs) {\n\t\t\t\t\t\tlogger.itemWarn(\n\t\t\t\t\t\t\t`${inv.screenId} → ${logger.dim(inv.field)}: \"${inv.target}\"`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t` ${logger.dim(\"Check that these screen IDs exist in your codebase.\")}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\t// Check dependsOn references against OpenAPI specs (if configured)\n\t\t\t\tif (config.apiIntegration?.openapi?.sources?.length) {\n\t\t\t\t\tconst openApiResult = await validateDependsOnAgainstOpenApi(\n\t\t\t\t\t\tscreens,\n\t\t\t\t\t\tconfig.apiIntegration.openapi.sources,\n\t\t\t\t\t\tcwd,\n\t\t\t\t\t\tctx.values.strict ?? false,\n\t\t\t\t\t)\n\t\t\t\t\tif (openApiResult.hasWarnings) {\n\t\t\t\t\t\thasWarnings = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Handle specific error types\n\t\t\t\tif (error instanceof SyntaxError) {\n\t\t\t\t\tlogger.warn(\"Failed to parse screens.json - file may be corrupted\")\n\t\t\t\t\tlogger.log(` ${logger.dim(\"Run 'screenbook build' to regenerate.\")}`)\n\t\t\t\t\thasWarnings = true\n\t\t\t\t} else if (error instanceof Error) {\n\t\t\t\t\tlogger.warn(`Failed to analyze screens.json: ${error.message}`)\n\t\t\t\t\thasWarnings = true\n\t\t\t\t} else {\n\t\t\t\t\tlogger.warn(`Failed to analyze screens.json: ${String(error)}`)\n\t\t\t\t\thasWarnings = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (hasWarnings) {\n\t\t\tlogger.blank()\n\t\t\tlogger.warn(\"Lint completed with warnings.\")\n\t\t}\n\t},\n})\n\n/**\n * Lint screen.meta.ts coverage for routesFile mode (config-based routing)\n */\nasync function lintRoutesFile(\n\troutesFile: string,\n\tcwd: string,\n\tconfig: Pick<Config, \"metaPattern\" | \"outDir\" | \"ignore\" | \"apiIntegration\">,\n\tadoption: AdoptionConfig,\n\tallowCycles: boolean,\n\tstrict: boolean,\n): Promise<boolean> {\n\tlet hasWarnings = false\n\tconst absoluteRoutesFile = resolve(cwd, routesFile)\n\n\t// Check if routes file exists\n\tif (!existsSync(absoluteRoutesFile)) {\n\t\tlogger.errorWithHelp(ERRORS.ROUTES_FILE_NOT_FOUND(routesFile))\n\t\tprocess.exit(1)\n\t}\n\n\tlogger.log(`Parsing routes from ${logger.path(routesFile)}...`)\n\tlogger.blank()\n\n\t// Parse the routes file with auto-detection\n\tlet flatRoutes: FlatRoute[]\n\ttry {\n\t\tconst content = readFileSync(absoluteRoutesFile, \"utf-8\")\n\t\tconst routerType = detectRouterType(content)\n\n\t\tlet parseResult: ParseResult\n\t\tif (routerType === \"tanstack-router\") {\n\t\t\tparseResult = parseTanStackRouterConfig(absoluteRoutesFile, content)\n\t\t} else if (routerType === \"solid-router\") {\n\t\t\tparseResult = parseSolidRouterConfig(absoluteRoutesFile, content)\n\t\t} else if (routerType === \"angular-router\") {\n\t\t\tparseResult = parseAngularRouterConfig(absoluteRoutesFile, content)\n\t\t} else if (routerType === \"react-router\") {\n\t\t\tparseResult = parseReactRouterConfig(absoluteRoutesFile, content)\n\t\t} else if (routerType === \"vue-router\") {\n\t\t\tparseResult = parseVueRouterConfig(absoluteRoutesFile, content)\n\t\t} else {\n\t\t\t// Unknown router type - warn user and attempt Vue Router parser as fallback\n\t\t\tlogger.warn(\n\t\t\t\t`Could not auto-detect router type for ${logger.path(routesFile)}. Attempting to parse as Vue Router.`,\n\t\t\t)\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"If parsing fails, check that your router imports are explicit.\")}`,\n\t\t\t)\n\t\t\thasWarnings = true\n\t\t\tparseResult = parseVueRouterConfig(absoluteRoutesFile, content)\n\t\t}\n\n\t\t// Show warnings\n\t\tfor (const warning of parseResult.warnings) {\n\t\t\tlogger.warn(warning)\n\t\t\thasWarnings = true\n\t\t}\n\n\t\tflatRoutes = flattenRoutes(parseResult.routes)\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tlogger.errorWithHelp(ERRORS.ROUTES_FILE_PARSE_ERROR(routesFile, message))\n\t\tprocess.exit(1)\n\t}\n\n\tif (flatRoutes.length === 0) {\n\t\tlogger.warn(\"No routes found in the config file\")\n\t\treturn hasWarnings\n\t}\n\n\t// Find all screen.meta.ts files\n\tconst metaFiles = await glob(config.metaPattern, {\n\t\tcwd,\n\t\tignore: config.ignore,\n\t})\n\n\t// Build a set of directories that have screen.meta.ts\n\tconst metaDirs = new Set<string>()\n\t// Also build a map from directory basename to full path for component name matching\n\tconst metaDirsByName = new Map<string, string>()\n\tfor (const metaFile of metaFiles) {\n\t\tconst dir = dirname(metaFile)\n\t\tmetaDirs.add(dir)\n\t\t// Store lowercase basename for case-insensitive matching\n\t\tconst baseName = dir.split(\"/\").pop()?.toLowerCase() || \"\"\n\t\tif (baseName) {\n\t\t\tmetaDirsByName.set(baseName, dir)\n\t\t}\n\t}\n\n\t// Check each route for screen.meta.ts coverage\n\tconst missingMeta: FlatRoute[] = []\n\tconst covered: FlatRoute[] = []\n\n\tfor (const route of flatRoutes) {\n\t\t// Skip layout routes (components ending with \"Layout\" that typically don't need screen.meta)\n\t\tif (route.componentPath?.endsWith(\"Layout\")) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Try multiple matching strategies\n\t\tlet matched = false\n\n\t\t// Strategy 1: Check by determineMetaDir (path-based matching)\n\t\tconst metaPath = determineMetaDir(route, cwd)\n\t\tif (metaDirs.has(metaPath)) {\n\t\t\tmatched = true\n\t\t}\n\n\t\t// Strategy 2: Match by component name (for React Router)\n\t\tif (!matched && route.componentPath) {\n\t\t\tconst componentName = route.componentPath.toLowerCase()\n\t\t\tif (metaDirsByName.has(componentName)) {\n\t\t\t\tmatched = true\n\t\t\t}\n\n\t\t\t// Also try matching the last word of component name\n\t\t\t// e.g., \"UserProfile\" -> check for \"profile\" directory\n\t\t\tif (!matched) {\n\t\t\t\t// Split by uppercase letters to get parts\n\t\t\t\tconst parts = route.componentPath.split(/(?=[A-Z])/)\n\t\t\t\tconst lastPart = parts[parts.length - 1]\n\t\t\t\tif (parts.length > 1 && lastPart) {\n\t\t\t\t\tif (metaDirsByName.has(lastPart.toLowerCase())) {\n\t\t\t\t\t\tmatched = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Strategy 3: Match by screenId path pattern\n\t\tif (!matched) {\n\t\t\tconst screenPath = route.screenId.replace(/\\./g, \"/\")\n\t\t\tfor (const dir of metaDirs) {\n\t\t\t\tif (dir.endsWith(screenPath)) {\n\t\t\t\t\tmatched = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (matched) {\n\t\t\tcovered.push(route)\n\t\t} else {\n\t\t\tmissingMeta.push(route)\n\t\t}\n\t}\n\n\t// Report results\n\tconst total = covered.length + missingMeta.length\n\tconst coveredCount = covered.length\n\tconst missingCount = missingMeta.length\n\tconst coveragePercent = Math.round((coveredCount / total) * 100)\n\n\tlogger.log(`Found ${total} routes`)\n\tlogger.log(`Coverage: ${coveredCount}/${total} (${coveragePercent}%)`)\n\tlogger.blank()\n\n\t// Determine if lint should fail\n\tconst minimumCoverage = adoption.minimumCoverage ?? 100\n\tconst passedCoverage = coveragePercent >= minimumCoverage\n\n\tif (missingCount > 0) {\n\t\tlogger.log(`Missing screen.meta.ts (${missingCount} routes):`)\n\t\tlogger.blank()\n\n\t\tfor (const route of missingMeta) {\n\t\t\tconst suggestedMetaPath = determineSuggestedMetaPath(route, cwd)\n\t\t\tlogger.itemError(\n\t\t\t\t`${route.fullPath} ${logger.dim(`(${route.screenId})`)}`,\n\t\t\t)\n\t\t\tlogger.log(` ${logger.dim(\"→\")} ${logger.path(suggestedMetaPath)}`)\n\t\t}\n\n\t\tlogger.blank()\n\t}\n\n\tif (!passedCoverage) {\n\t\tlogger.error(\n\t\t\t`Lint failed: Coverage ${coveragePercent}% is below minimum ${minimumCoverage}%`,\n\t\t)\n\t\tprocess.exit(1)\n\t} else if (missingCount > 0) {\n\t\tlogger.success(\n\t\t\t`Coverage ${coveragePercent}% meets minimum ${minimumCoverage}%`,\n\t\t)\n\t\tif (adoption.mode === \"progressive\") {\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"Tip:\")} Increase minimumCoverage in config to gradually improve coverage`,\n\t\t\t)\n\t\t}\n\t} else {\n\t\tlogger.done(\"All routes have screen.meta.ts files\")\n\t}\n\n\t// Check for orphan screens and cycles using screens.json\n\tconst screensPath = join(cwd, config.outDir, \"screens.json\")\n\tif (existsSync(screensPath)) {\n\t\ttry {\n\t\t\tconst content = readFileSync(screensPath, \"utf-8\")\n\t\t\tconst screens = JSON.parse(content) as Screen[]\n\n\t\t\t// Check for orphan screens\n\t\t\tconst orphans = findOrphanScreens(screens)\n\n\t\t\tif (orphans.length > 0) {\n\t\t\t\thasWarnings = true\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.warn(`Orphan screens detected (${orphans.length}):`)\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(\" These screens have no entryPoints and are not\")\n\t\t\t\tlogger.log(\" referenced in any other screen's 'next' array.\")\n\t\t\t\tlogger.blank()\n\t\t\t\tfor (const orphan of orphans) {\n\t\t\t\t\tlogger.itemWarn(`${orphan.id} ${logger.dim(orphan.route)}`)\n\t\t\t\t}\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(\"Consider adding entryPoints or removing these screens.\")}`,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Check for circular navigation\n\t\t\tif (!allowCycles) {\n\t\t\t\tconst cycleResult = detectCycles(screens)\n\t\t\t\tif (cycleResult.hasCycles) {\n\t\t\t\t\thasWarnings = true\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.warn(getCycleSummary(cycleResult))\n\t\t\t\t\tlogger.log(formatCycleWarnings(cycleResult.cycles))\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tif (cycleResult.disallowedCycles.length > 0) {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` ${logger.dim(\"Use 'allowCycles: true' in screen.meta.ts to allow intentional cycles.\")}`,\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\tif (strict) {\n\t\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\t\tlogger.errorWithHelp(\n\t\t\t\t\t\t\t\tERRORS.CYCLES_DETECTED(cycleResult.disallowedCycles.length),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\tprocess.exit(1)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check for invalid navigation references\n\t\t\tconst invalidNavs = findInvalidNavigations(screens)\n\t\t\tif (invalidNavs.length > 0) {\n\t\t\t\thasWarnings = true\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.warn(`Invalid navigation targets (${invalidNavs.length}):`)\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(\n\t\t\t\t\t\" These navigation references point to non-existent screens.\",\n\t\t\t\t)\n\t\t\t\tlogger.blank()\n\t\t\t\tfor (const inv of invalidNavs) {\n\t\t\t\t\tlogger.itemWarn(\n\t\t\t\t\t\t`${inv.screenId} → ${logger.dim(inv.field)}: \"${inv.target}\"`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(\"Check that these screen IDs exist in your codebase.\")}`,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Check dependsOn references against OpenAPI specs (if configured)\n\t\t\tif (config.apiIntegration?.openapi?.sources?.length) {\n\t\t\t\tconst openApiResult = await validateDependsOnAgainstOpenApi(\n\t\t\t\t\tscreens,\n\t\t\t\t\tconfig.apiIntegration.openapi.sources,\n\t\t\t\t\tcwd,\n\t\t\t\t\tstrict,\n\t\t\t\t)\n\t\t\t\tif (openApiResult.hasWarnings) {\n\t\t\t\t\thasWarnings = true\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (error instanceof SyntaxError) {\n\t\t\t\tlogger.warn(\"Failed to parse screens.json - file may be corrupted\")\n\t\t\t\tlogger.log(` ${logger.dim(\"Run 'screenbook build' to regenerate.\")}`)\n\t\t\t\thasWarnings = true\n\t\t\t} else if (error instanceof Error) {\n\t\t\t\tlogger.warn(`Failed to analyze screens.json: ${error.message}`)\n\t\t\t\thasWarnings = true\n\t\t\t} else {\n\t\t\t\tlogger.warn(`Failed to analyze screens.json: ${String(error)}`)\n\t\t\t\thasWarnings = true\n\t\t\t}\n\t\t}\n\t}\n\n\tif (hasWarnings) {\n\t\tlogger.blank()\n\t\tlogger.warn(\"Lint completed with warnings.\")\n\t}\n\n\treturn hasWarnings\n}\n\n/**\n * Determine the directory where screen.meta.ts should be for a route\n */\nfunction determineMetaDir(route: FlatRoute, cwd: string): string {\n\t// If component path is available, check relative to component directory\n\tif (route.componentPath) {\n\t\tconst componentDir = dirname(route.componentPath)\n\t\tconst relativePath = relative(cwd, componentDir)\n\t\t// Ensure path doesn't escape cwd\n\t\tif (!relativePath.startsWith(\"..\")) {\n\t\t\treturn relativePath\n\t\t}\n\t}\n\n\t// Fall back to src/screens/{screenId} convention\n\tconst screenDir = route.screenId.replace(/\\./g, \"/\")\n\treturn join(\"src\", \"screens\", screenDir)\n}\n\n/**\n * Determine the suggested screen.meta.ts path for a route\n */\nfunction determineSuggestedMetaPath(route: FlatRoute, cwd: string): string {\n\tconst metaDir = determineMetaDir(route, cwd)\n\treturn join(metaDir, \"screen.meta.ts\")\n}\n\ninterface InvalidNavigation {\n\tscreenId: string\n\tfield: string\n\ttarget: string\n}\n\n/**\n * Find navigation references that point to non-existent screens.\n * Checks `next`, `entryPoints` arrays and mock navigation targets.\n */\nfunction findInvalidNavigations(screens: Screen[]): InvalidNavigation[] {\n\tconst screenIds = new Set(screens.map((s) => s.id))\n\tconst invalid: InvalidNavigation[] = []\n\n\tfor (const screen of screens) {\n\t\t// Check next array\n\t\tif (screen.next) {\n\t\t\tfor (const target of screen.next) {\n\t\t\t\tif (!screenIds.has(target)) {\n\t\t\t\t\tinvalid.push({ screenId: screen.id, field: \"next\", target })\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Check entryPoints array\n\t\tif (screen.entryPoints) {\n\t\t\tfor (const target of screen.entryPoints) {\n\t\t\t\tif (!screenIds.has(target)) {\n\t\t\t\t\tinvalid.push({ screenId: screen.id, field: \"entryPoints\", target })\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn invalid\n}\n\n/**\n * Find screens that are unreachable (orphans).\n * A screen is an orphan if:\n * - It has no entryPoints defined\n * - AND it's not referenced in any other screen's `next` array\n */\nfunction findOrphanScreens(screens: Screen[]): Screen[] {\n\t// Build a set of all screen IDs that are referenced in `next` arrays\n\tconst referencedIds = new Set<string>()\n\tfor (const screen of screens) {\n\t\tif (screen.next) {\n\t\t\tfor (const nextId of screen.next) {\n\t\t\t\treferencedIds.add(nextId)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Find orphan screens\n\tconst orphans: Screen[] = []\n\tfor (const screen of screens) {\n\t\tconst hasEntryPoints = screen.entryPoints && screen.entryPoints.length > 0\n\t\tconst isReferenced = referencedIds.has(screen.id)\n\n\t\t// A screen is an orphan if it has no entry points AND is not referenced\n\t\tif (!hasEntryPoints && !isReferenced) {\n\t\t\torphans.push(screen)\n\t\t}\n\t}\n\n\treturn orphans\n}\n\n/**\n * Validate dependsOn references against OpenAPI specifications\n */\nasync function validateDependsOnAgainstOpenApi(\n\tscreens: readonly Screen[],\n\tsources: readonly string[],\n\tcwd: string,\n\tstrict: boolean,\n): Promise<{ hasWarnings: boolean }> {\n\tlet hasWarnings = false\n\n\t// Parse OpenAPI specs (errors are collected internally, not thrown)\n\tconst parseResult = await parseOpenApiSpecs(sources, cwd)\n\n\t// Report parse errors as warnings\n\tfor (const error of parseResult.errors) {\n\t\thasWarnings = true\n\t\tlogger.blank()\n\t\tlogger.warn(`Failed to parse OpenAPI spec: ${error.source}`)\n\t\tlogger.log(` ${logger.dim(error.message)}`)\n\t}\n\n\t// Skip validation if no specs were parsed successfully\n\tif (parseResult.specs.length === 0) {\n\t\treturn { hasWarnings }\n\t}\n\n\t// Validate dependsOn references\n\tconst validationResult = validateDependsOnReferences(\n\t\tscreens,\n\t\tparseResult.specs,\n\t)\n\n\tif (validationResult.errors.length > 0) {\n\t\thasWarnings = true\n\t\tlogger.blank()\n\t\tlogger.warn(`Invalid API dependencies (${validationResult.errors.length}):`)\n\t\tlogger.blank()\n\t\tlogger.log(\n\t\t\t\" These dependsOn references don't match any OpenAPI operation.\",\n\t\t)\n\t\tlogger.blank()\n\n\t\tfor (const error of validationResult.errors) {\n\t\t\tlogger.itemWarn(`${error.screenId}: \"${error.invalidApi}\"`)\n\t\t\tif (error.suggestion) {\n\t\t\t\tlogger.log(` ${logger.dim(`Did you mean \"${error.suggestion}\"?`)}`)\n\t\t\t}\n\t\t}\n\n\t\t// Fail in strict mode\n\t\tif (strict) {\n\t\t\tlogger.blank()\n\t\t\tlogger.errorWithHelp(\n\t\t\t\tERRORS.INVALID_API_DEPENDENCIES(validationResult.errors.length),\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\t}\n\n\treturn { hasWarnings }\n}\n","import { basename, dirname } from \"node:path\"\nimport type { ImpactResult } from \"./impactAnalysis.js\"\n\n/**\n * Extract potential API names from changed file paths.\n * Looks for common API file patterns.\n */\nexport function extractApiNames(files: string[]): string[] {\n\tconst apis = new Set<string>()\n\n\tfor (const file of files) {\n\t\tconst fileName = basename(file, \".ts\")\n\t\t\t.replace(/\\.tsx?$/, \"\")\n\t\t\t.replace(/\\.js$/, \"\")\n\t\t\t.replace(/\\.jsx?$/, \"\")\n\n\t\tconst dirName = basename(dirname(file))\n\n\t\t// Pattern: src/api/InvoiceAPI.ts -> InvoiceAPI\n\t\tif (\n\t\t\tfile.includes(\"/api/\") ||\n\t\t\tfile.includes(\"/apis/\") ||\n\t\t\tfile.includes(\"/services/\")\n\t\t) {\n\t\t\tif (\n\t\t\t\tfileName.endsWith(\"API\") ||\n\t\t\t\tfileName.endsWith(\"Api\") ||\n\t\t\t\tfileName.endsWith(\"Service\")\n\t\t\t) {\n\t\t\t\tapis.add(fileName)\n\t\t\t}\n\t\t}\n\n\t\t// Pattern: src/services/invoice/index.ts -> InvoiceService\n\t\tif (\n\t\t\tfile.includes(\"/services/\") &&\n\t\t\t(fileName === \"index\" || fileName === dirName)\n\t\t) {\n\t\t\tconst serviceName = `${capitalize(dirName)}Service`\n\t\t\tapis.add(serviceName)\n\t\t}\n\n\t\t// Pattern: src/api/invoice.ts -> InvoiceAPI\n\t\tif (file.includes(\"/api/\") || file.includes(\"/apis/\")) {\n\t\t\tif (!fileName.endsWith(\"API\") && !fileName.endsWith(\"Api\")) {\n\t\t\t\tconst apiName = `${capitalize(fileName)}API`\n\t\t\t\tapis.add(apiName)\n\t\t\t}\n\t\t}\n\n\t\t// Pattern: explicit API/Service file names\n\t\tif (\n\t\t\tfileName.toLowerCase().includes(\"api\") ||\n\t\t\tfileName.toLowerCase().includes(\"service\")\n\t\t) {\n\t\t\tapis.add(fileName)\n\t\t}\n\t}\n\n\treturn Array.from(apis).sort()\n}\n\nexport function capitalize(str: string): string {\n\treturn str.charAt(0).toUpperCase() + str.slice(1)\n}\n\n/**\n * Format the results as Markdown for PR comments.\n */\nexport function formatMarkdown(\n\tchangedFiles: string[],\n\tdetectedApis: string[],\n\tresults: ImpactResult[],\n): string {\n\tconst lines: string[] = []\n\n\tlines.push(\"## Screenbook Impact Analysis\")\n\tlines.push(\"\")\n\n\tif (results.length === 0) {\n\t\tlines.push(\"No screen impacts detected from the API changes in this PR.\")\n\t\tlines.push(\"\")\n\t\tlines.push(\"<details>\")\n\t\tlines.push(\"<summary>Detected APIs (no screen dependencies)</summary>\")\n\t\tlines.push(\"\")\n\t\tfor (const api of detectedApis) {\n\t\t\tlines.push(`- \\`${api}\\``)\n\t\t}\n\t\tlines.push(\"\")\n\t\tlines.push(\"</details>\")\n\t\treturn lines.join(\"\\n\")\n\t}\n\n\t// Summary\n\tconst totalDirect = results.reduce((sum, r) => sum + r.direct.length, 0)\n\tconst totalTransitive = results.reduce(\n\t\t(sum, r) => sum + r.transitive.length,\n\t\t0,\n\t)\n\tconst totalScreens = totalDirect + totalTransitive\n\n\tlines.push(\n\t\t`**${totalScreens} screen${totalScreens > 1 ? \"s\" : \"\"} affected** by changes to ${results.length} API${results.length > 1 ? \"s\" : \"\"}`,\n\t)\n\tlines.push(\"\")\n\n\t// Per-API breakdown\n\tfor (const result of results) {\n\t\tlines.push(`### ${result.api}`)\n\t\tlines.push(\"\")\n\n\t\tif (result.direct.length > 0) {\n\t\t\tlines.push(`**Direct dependencies** (${result.direct.length}):`)\n\t\t\tlines.push(\"\")\n\t\t\tlines.push(\"| Screen | Route | Owner |\")\n\t\t\tlines.push(\"|--------|-------|-------|\")\n\t\t\tfor (const screen of result.direct) {\n\t\t\t\tconst owner = screen.owner?.join(\", \") ?? \"-\"\n\t\t\t\tlines.push(`| ${screen.id} | \\`${screen.route}\\` | ${owner} |`)\n\t\t\t}\n\t\t\tlines.push(\"\")\n\t\t}\n\n\t\tif (result.transitive.length > 0) {\n\t\t\tlines.push(`**Transitive dependencies** (${result.transitive.length}):`)\n\t\t\tlines.push(\"\")\n\t\t\tfor (const { path } of result.transitive) {\n\t\t\t\tlines.push(`- ${path.join(\" → \")}`)\n\t\t\t}\n\t\t\tlines.push(\"\")\n\t\t}\n\t}\n\n\t// Changed files summary\n\tlines.push(\"<details>\")\n\tlines.push(`<summary>Changed files (${changedFiles.length})</summary>`)\n\tlines.push(\"\")\n\tfor (const file of changedFiles.slice(0, 20)) {\n\t\tlines.push(`- \\`${file}\\``)\n\t}\n\tif (changedFiles.length > 20) {\n\t\tlines.push(`- ... and ${changedFiles.length - 20} more`)\n\t}\n\tlines.push(\"\")\n\tlines.push(\"</details>\")\n\n\treturn lines.join(\"\\n\")\n}\n","import { execSync } from \"node:child_process\"\nimport { existsSync, readFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { analyzeImpact, type ImpactResult } from \"../utils/impactAnalysis.js\"\nimport { logger, setVerbose } from \"../utils/logger.js\"\nimport { extractApiNames, formatMarkdown } from \"../utils/prImpact.js\"\n\nexport const prImpactCommand = define({\n\tname: \"pr-impact\",\n\tdescription: \"Analyze impact of changed files in a PR\",\n\targs: {\n\t\tbase: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"b\",\n\t\t\tdescription: \"Base branch to compare against (default: main)\",\n\t\t\tdefault: \"main\",\n\t\t},\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tformat: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Output format: markdown (default) or json\",\n\t\t\tdefault: \"markdown\",\n\t\t},\n\t\tdepth: {\n\t\t\ttype: \"number\",\n\t\t\tshort: \"d\",\n\t\t\tdescription: \"Maximum depth for transitive dependencies\",\n\t\t\tdefault: 3,\n\t\t},\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output including stack traces\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tsetVerbose(ctx.values.verbose)\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst cwd = process.cwd()\n\n\t\tconst baseBranch = ctx.values.base ?? \"main\"\n\t\tconst format = ctx.values.format ?? \"markdown\"\n\t\tconst depth = ctx.values.depth ?? 3\n\n\t\t// Get changed files from git\n\t\tlet changedFiles: string[]\n\t\ttry {\n\t\t\tconst gitOutput = execSync(\n\t\t\t\t`git diff --name-only ${baseBranch}...HEAD 2>/dev/null || git diff --name-only ${baseBranch} HEAD`,\n\t\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t\t)\n\t\t\tchangedFiles = gitOutput\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.map((f) => f.trim())\n\t\t\t\t.filter((f) => f.length > 0)\n\t\t} catch {\n\t\t\tlogger.errorWithHelp(ERRORS.GIT_CHANGED_FILES_ERROR(baseBranch))\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tif (changedFiles.length === 0) {\n\t\t\tlogger.info(\"No changed files found.\")\n\t\t\treturn\n\t\t}\n\n\t\t// Extract potential API names from changed files\n\t\tconst apiNames = extractApiNames(changedFiles)\n\n\t\tif (apiNames.length === 0) {\n\t\t\tif (format === \"markdown\") {\n\t\t\t\tlogger.log(\"## Screenbook Impact Analysis\")\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(\"No API-related changes detected in this PR.\")\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(`Changed files: ${changedFiles.length}`)\n\t\t\t} else {\n\t\t\t\tlogger.log(\n\t\t\t\t\tJSON.stringify({ apis: [], results: [], changedFiles }, null, 2),\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// Load screens.json\n\t\tconst screensPath = join(cwd, config.outDir, \"screens.json\")\n\n\t\tif (!existsSync(screensPath)) {\n\t\t\tlogger.errorWithHelp(ERRORS.SCREENS_NOT_FOUND)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlet screens: Screen[]\n\t\ttry {\n\t\t\tconst content = readFileSync(screensPath, \"utf-8\")\n\t\t\tscreens = JSON.parse(content) as Screen[]\n\t\t} catch (error) {\n\t\t\tlogger.errorWithHelp({\n\t\t\t\t...ERRORS.SCREENS_PARSE_ERROR,\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Analyze impact for each API\n\t\tconst results: ImpactResult[] = []\n\t\tfor (const apiName of apiNames) {\n\t\t\tconst result = analyzeImpact(screens, apiName, depth)\n\t\t\tif (result.totalCount > 0) {\n\t\t\t\tresults.push(result)\n\t\t\t}\n\t\t}\n\n\t\t// Output results\n\t\tif (format === \"json\") {\n\t\t\tlogger.log(\n\t\t\t\tJSON.stringify(\n\t\t\t\t\t{\n\t\t\t\t\t\tchangedFiles,\n\t\t\t\t\t\tdetectedApis: apiNames,\n\t\t\t\t\t\tresults: results.map((r) => ({\n\t\t\t\t\t\t\tapi: r.api,\n\t\t\t\t\t\t\tdirectCount: r.direct.length,\n\t\t\t\t\t\t\ttransitiveCount: r.transitive.length,\n\t\t\t\t\t\t\ttotalCount: r.totalCount,\n\t\t\t\t\t\t\tdirect: r.direct.map((s) => ({\n\t\t\t\t\t\t\t\tid: s.id,\n\t\t\t\t\t\t\t\ttitle: s.title,\n\t\t\t\t\t\t\t\troute: s.route,\n\t\t\t\t\t\t\t\towner: s.owner,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\ttransitive: r.transitive.map(({ screen, path }) => ({\n\t\t\t\t\t\t\t\tid: screen.id,\n\t\t\t\t\t\t\t\ttitle: screen.title,\n\t\t\t\t\t\t\t\troute: screen.route,\n\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t})),\n\t\t\t\t\t},\n\t\t\t\t\tnull,\n\t\t\t\t\t2,\n\t\t\t\t),\n\t\t\t)\n\t\t} else {\n\t\t\tlogger.log(formatMarkdown(changedFiles, apiNames, results))\n\t\t}\n\t},\n})\n","#!/usr/bin/env node\n\nimport { readFileSync } from \"node:fs\"\nimport { dirname, join } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport { cli, define } from \"gunshi\"\nimport { badgeCommand } from \"./commands/badge.js\"\nimport { buildCommand } from \"./commands/build.js\"\nimport { devCommand } from \"./commands/dev.js\"\nimport { doctorCommand } from \"./commands/doctor.js\"\nimport { generateCommand } from \"./commands/generate.js\"\nimport { impactCommand } from \"./commands/impact.js\"\nimport { initCommand } from \"./commands/init.js\"\nimport { lintCommand } from \"./commands/lint.js\"\nimport { prImpactCommand } from \"./commands/pr-impact.js\"\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst packageJson = JSON.parse(\n\treadFileSync(join(__dirname, \"..\", \"package.json\"), \"utf-8\"),\n)\nconst version: string = packageJson.version\n\nconst mainCommand = define({\n\tname: \"screenbook\",\n\tdescription: \"Screen catalog and navigation graph generator\",\n\trun: () => {\n\t\tconsole.log(\"Usage: screenbook <command>\")\n\t\tconsole.log(\"\")\n\t\tconsole.log(\"Commands:\")\n\t\tconsole.log(\" init Initialize Screenbook in a project\")\n\t\tconsole.log(\" generate Auto-generate screen.meta.ts from routes\")\n\t\tconsole.log(\" build Build screen metadata JSON\")\n\t\tconsole.log(\" dev Start the development server\")\n\t\tconsole.log(\" lint Detect routes without screen.meta\")\n\t\tconsole.log(\" impact Analyze API dependency impact\")\n\t\tconsole.log(\" pr-impact Analyze PR changes impact\")\n\t\tconsole.log(\" badge Generate coverage badge for README\")\n\t\tconsole.log(\" doctor Diagnose common setup issues\")\n\t\tconsole.log(\"\")\n\t\tconsole.log(\"Run 'screenbook <command> --help' for more information\")\n\t},\n})\n\nawait cli(process.argv.slice(2), mainCommand, {\n\tname: \"screenbook\",\n\tversion,\n\tsubCommands: {\n\t\tinit: initCommand,\n\t\tgenerate: generateCommand,\n\t\tbuild: buildCommand,\n\t\tdev: devCommand,\n\t\tlint: lintCommand,\n\t\timpact: impactCommand,\n\t\t\"pr-impact\": prImpactCommand,\n\t\tbadge: badgeCommand,\n\t\tdoctor: doctorCommand,\n\t},\n})\n"],"mappings":"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"}