import { Command } from 'commander'; import { writeFile } from 'node:fs/promises'; import { resolve } from 'node:path'; import { generateTypes } from './index.js'; import { getCustomTypes, modifyAndInjectCustomSTypes } from './modiiers/custom-type-generation.js'; import { checkAndCreateNestsedDir, readOrCreateFile } from './utils/fs.js'; // Import package.json for version and name import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const packageJson = JSON.parse( readFileSync(join(__dirname, '../../package.json'), 'utf8') ); interface CliOptions { url?: string; email?: string; password?: string; dir?: string; filter?: string; ignore?: string; include?: string; type?: string; // 'zod' or 'ts' or 'ts,zod' for thoth verbose?: boolean; } const program = new Command(); program .name(packageJson.name) .version(packageJson.version) .description('Generate types for the PocketBase JavaScript SDK') .option( '-u, --url ', 'URL to your hosted pocketbase instance', 'http://127.0.0.1:8090' ) .option( '-e, --email ', 'email for an admin pocketbase user', process.env.POCKETBASE_EMAIL ) .option( '-p, --password ', 'password for an admin pocketbase user', process.env.POCKETBASE_PASSWORD ) .option( '-d, --dir ', 'directory path to save the typescript files' ) .option( '-f, --filter ', 'only generate types from specified collection', '' ) .option( '-t, --type ', 'output type of the generated types, either "zod" or "ts" or "ts,zod"', 'ts' ) .option( '-v, --verbose', 'enable verbose output for debugging', false ) .option('--ignore ', 'regex pattern to ignore collections ', '') .option('--include ', 'regex pattern to include collections', '') .action(async (options: CliOptions) => { // Helper function for verbose logging const verboseLog = (message: string, ...args: any[]) => { if (options.verbose) { console.log(`[VERBOSE] ${message}`, ...args); } }; verboseLog('Starting type generation with options:', options); if (!options.url) { // console.log("url", options.url); error(`required option '-u, --url' not specified`); } if (!options.email) { error( `required option '-e, --email' not specified and 'POCKETBASE_EMAIL' env not set` ); } if (!options.password) { error( `required option '-p, --password' not specified and 'POCKETBASE_PASSWORD' env not set` ); } verboseLog('Connecting to PocketBase at:', options.url); verboseLog('Using patterns - ignore:', options.ignore, 'include:', options.include); const { types: raw_typed_pb_types, zodSchemas } = await generateTypes({ url: options.url, email: options.email, password: options.password, ignorePattern: options.ignore, includePattern: options.include }); verboseLog('Successfully generated types from PocketBase'); verboseLog('Raw types length:', raw_typed_pb_types.length, 'characters'); verboseLog('Zod schemas length:', zodSchemas.length, 'characters'); if (options.dir) { verboseLog('Output directory specified:', options.dir); const [optA, optB] = options.type?.split(',') || ['ts']; verboseLog('Output types requested:', { optA, optB }); // Validate options - optB can be undefined for single options const validOptions = ['ts', 'zod']; if ( !validOptions.includes(optA) || (optB && !validOptions.includes(optB)) ) { error( `Invalid type option. Use 'ts', 'zod' or 'ts,zod'. Provided: ${options.type}` ); } const DEFAULT_PB_FILES_DIR = resolve(options.dir); verboseLog('Creating output directory:', DEFAULT_PB_FILES_DIR); await checkAndCreateNestsedDir(DEFAULT_PB_FILES_DIR); const PB_TYPES_PATH = resolve(DEFAULT_PB_FILES_DIR, 'pb-types.ts'); const PB_ZOD_PATH = resolve(DEFAULT_PB_FILES_DIR, 'pb-zod.ts'); const CUSTOM_PB_TYPES_PATH = resolve( DEFAULT_PB_FILES_DIR, 'custom-pb-types.ts' ); if (optA === 'ts' || optB === 'ts') { verboseLog('Generating TypeScript types...'); const text_output = raw_typed_pb_types; verboseLog('Reading/creating custom types file:', CUSTOM_PB_TYPES_PATH); const custom_db_types_string = await readOrCreateFile(CUSTOM_PB_TYPES_PATH); verboseLog('Extracting custom types from generated content...'); const { extracted_custom_db_types, extracted_custom_db_types_array } = await getCustomTypes(text_output, custom_db_types_string); verboseLog('Writing custom types file...'); await writeFile( CUSTOM_PB_TYPES_PATH, extracted_custom_db_types, 'utf-8' ); verboseLog('Modifying and injecting custom types...'); const final_db_types = await modifyAndInjectCustomSTypes({ content: text_output, extracted_custom_db_types, extracted_custom_db_types_array }); verboseLog('Writing final TypeScript types file:', PB_TYPES_PATH); await writeFile(PB_TYPES_PATH, final_db_types, 'utf-8'); console.log(`Generated types for PocketBase at ${PB_TYPES_PATH}`); if (options.verbose) { console.log(' ====>', final_db_types.split('// ===== Schema =====')[1]); } } if (optB === 'zod' || optA === 'zod') { verboseLog('Generating Zod schemas...'); verboseLog('Writing Zod schemas file:', PB_ZOD_PATH); await writeFile(PB_ZOD_PATH, zodSchemas, 'utf-8'); console.log(`Generated Zod schemas for PocketBase at ${PB_ZOD_PATH}`); if (options.verbose) { console.log(`\n\n${zodSchemas}`); } } } else { verboseLog('No output directory specified, printing to console'); console.log(raw_typed_pb_types); } // Summary log after everything is complete const outputInfo = options.dir ? `saved to ${options.dir}` : 'printed to console'; console.log(`✅ Type generation completed successfully - ${outputInfo}`); }); program.parse(); function error(msg: string): never { console.error(msg); process.exit(1); }