import { parseArgs } from "@std/cli/parse-args"; import { resolve, dirname } from "@std/path"; import { ensureDir } from "@std/fs"; import { generateTypeScript } from "@slices/codegen"; import { logger } from "../utils/logger.ts"; import { findLexiconFiles, validateLexiconFiles, printValidationSummary, } from "../utils/lexicon.ts"; import { SlicesConfigLoader, mergeConfig } from "../utils/config.ts"; function showCodegenHelp() { console.log(` slices codegen - Generate TypeScript client from lexicon files USAGE: slices codegen [OPTIONS] OPTIONS: --lexicons Directory containing lexicon files (default: ./lexicons or from slices.json) --output Output file path (default: ./generated_client.ts or from slices.json) --slice Target slice URI (required, or from slices.json) -h, --help Show this help message EXAMPLES: slices codegen --slice at://did:plc:example/slice slices codegen --lexicons ./my-lexicons --output ./src/client.ts --slice at://did:plc:example/slice slices codegen # Uses config from slices.json `); } export async function codegenCommand( commandArgs: unknown[], _globalArgs: Record ): Promise { const args = parseArgs(commandArgs as string[], { boolean: ["help", "include-slices"], string: ["lexicons", "output", "slice"], alias: { h: "help", }, }); if (args.help) { showCodegenHelp(); return; } // Load config file const configLoader = new SlicesConfigLoader(); const slicesConfig = await configLoader.load(); const mergedConfig = mergeConfig(slicesConfig, args); // Validate required arguments if (!mergedConfig.slice) { logger.error("--slice is required"); if (!slicesConfig.slice) { logger.info( "Tip: Create a slices.json file with your slice URI to avoid passing --slice every time" ); } console.log("\nRun 'slices codegen --help' for usage information."); Deno.exit(1); } const lexiconsPath = resolve(mergedConfig.lexiconPath!); const outputPath = resolve(mergedConfig.clientOutputPath!); const sliceUri = mergedConfig.slice!; try { const lexiconFiles = await findLexiconFiles(lexiconsPath); if (lexiconFiles.length === 0) { logger.warn(`No .json files found in ${lexiconsPath}`); return; } const validationResult = await validateLexiconFiles(lexiconFiles, false); if (validationResult.invalidFiles > 0) { printValidationSummary(validationResult); logger.error("Cannot generate client with invalid lexicon files"); Deno.exit(1); } if (validationResult.validFiles === 0) { logger.error("No valid lexicon files found"); Deno.exit(1); } const validLexicons = validationResult.files .filter((f) => f.valid) .map((f) => f.content); const generatedCode = await generateTypeScript(validLexicons, { sliceUri, }); const outputDir = dirname(outputPath); await ensureDir(outputDir); await Deno.writeTextFile(outputPath, generatedCode); logger.success(`Generated client: ${outputPath}`); } catch (error) { const err = error as Error; logger.error(`Code generation failed: ${err.message}`); Deno.exit(1); } }