#!/usr/bin/env node /** * cli:scaffold-routes — Generate PageRegistry entries */ import { parseArgs } from 'node:util' import { mkdirSync, writeFileSync } from 'node:fs' import { dirname, resolve } from 'node:path' import { validate } from './validate.js' import { generate } from './generate.js' import { generateEnvelope, failGenerate, printEnvelope } from '../../../../../lib/output.js' import { readSpecArg } from '../../../../../lib/spec-arg.js' import { assertWebProjectRoot, safeJoinPath } from '../../../../../lib/fs.js' const COMMAND = 'scaffold-routes' function main(): void { const { values } = parseArgs({ options: { spec: { type: 'string' }, 'spec-file': { type: 'string' }, outdir: { type: 'string' }, dry_run: { type: 'boolean', default: false } }, strict: true }) const specSrc = readSpecArg(values) if ('error' in specSrc) { printEnvelope(failGenerate(COMMAND, [specSrc.error])); process.exit(1) } let raw: unknown try { raw = JSON.parse(specSrc.raw) } catch { printEnvelope(failGenerate(COMMAND, ['Invalid JSON'])); process.exit(1) } const validation = validate(raw) if (!validation.valid) { printEnvelope(failGenerate(COMMAND, validation.errors)); process.exit(1) } const spec = raw as any const files = generate(spec) if (values.dry_run) { printEnvelope(generateEnvelope(COMMAND, { data: { dryRun: true, files: files.map(f => f.path) }, warnings: validation.warnings })) process.exit(0) } const outdir = values.outdir ?? spec.projectPath try { assertWebProjectRoot(outdir) } catch (e) { printEnvelope(failGenerate(COMMAND, [e instanceof Error ? e.message : String(e)])); process.exit(1) } const written: string[] = [] for (const file of files) { const p = resolve(safeJoinPath(outdir, file.path)); mkdirSync(dirname(p), { recursive: true }); writeFileSync(p, file.content, 'utf-8'); written.push(p) } // Derive the import hint from the ACTUAL emitted registry file (app-scoped // `{app}-{module}Registry.ts`) so this next-step can never drift from the // filename the generator writes. const registryImport = files.find((f) => f.path.endsWith('Registry.ts'))?.path.replace(/^src\//, './').replace(/\.ts$/, '') ?? `./extensions/${spec.module}Registry` const nextSteps: string[] = [ `Import the registry in src/main.tsx BEFORE createRoot: import '${registryImport}';`, 'Without this import, PageRegistry.register() never runs and every route in this module renders blank.', ] if (validation.warnings.length > 0) nextSteps.unshift('Scaffold missing pages (see warnings) BEFORE writing the registry — phantom imports crash Vite at launch') printEnvelope(generateEnvelope(COMMAND, { data: { module: spec.module, fileCount: written.length }, filesCreated: written, warnings: validation.warnings, nextSteps, })) } main()