UNPKG

@catalystlabs/awm

Version:

Appwrite Migration Tool - Schema management and code generation for Appwrite databases

182 lines (147 loc) • 5.49 kB
#!/usr/bin/env node import fs from 'fs/promises'; import path from 'path'; /** * AWM TypeScript Type Generator * Generates TypeScript types from Appwrite schema */ class TypeGenerator { constructor(schemaPath = './appwrite.schema') { this.schemaPath = schemaPath; this.collections = []; } async parseSchema() { const content = await fs.readFile(this.schemaPath, 'utf-8'); const lines = content.split('\n'); let currentCollection = null; let inCollection = false; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); // Skip comments and empty lines if (!line || line.startsWith('//') || line.startsWith('/*')) continue; // Start of collection if (line.startsWith('collection ')) { const name = line.split(' ')[1].replace('{', '').trim(); currentCollection = { name, attributes: [], indexes: [], uniques: [] }; inCollection = true; continue; } // End of collection if (inCollection && line === '}') { this.collections.push(currentCollection); currentCollection = null; inCollection = false; continue; } // Parse attributes if (inCollection && currentCollection && !line.startsWith('@@')) { const attrMatch = line.match(/^(\w+)\s+(String|Int|Float|Boolean|DateTime)(\[\])?\s*(.*)/); if (attrMatch) { const [_, name, type, isArray, decorators] = attrMatch; const attribute = { name, type: this.mapToTypeScriptType(type), isArray: !!isArray, required: decorators.includes('@required'), unique: decorators.includes('@unique'), size: this.extractSize(decorators) }; // Handle relationship decorators if (decorators.includes('@relationship')) { attribute.isRelationship = true; attribute.tsType = 'string'; // Relationship IDs are strings } currentCollection.attributes.push(attribute); } } } } mapToTypeScriptType(appwriteType) { const typeMap = { 'String': 'string', 'Int': 'number', 'Float': 'number', 'Boolean': 'boolean', 'DateTime': 'Date | string' }; return typeMap[appwriteType] || 'any'; } extractSize(decorators) { const sizeMatch = decorators.match(/@size\((\d+)\)/); return sizeMatch ? parseInt(sizeMatch[1]) : null; } generateTypeScript() { let output = `// Generated by AWM Type Generator // DO NOT EDIT - This file is auto-generated from appwrite.schema `; // Generate interfaces for each collection for (const collection of this.collections) { const interfaceName = this.toPascalCase(collection.name); output += `export interface ${interfaceName} {\n`; output += ` $id?: string;\n`; output += ` $createdAt?: string;\n`; output += ` $updatedAt?: string;\n`; output += ` $permissions?: string[];\n`; output += ` $databaseId?: string;\n`; output += ` $collectionId?: string;\n\n`; for (const attr of collection.attributes) { const optional = attr.required ? '' : '?'; const type = attr.isArray ? `${attr.tsType || attr.type}[]` : (attr.tsType || attr.type); const comment = attr.size ? ` // max size: ${attr.size}` : ''; output += ` ${attr.name}${optional}: ${type};${comment}\n`; } output += `}\n\n`; } // Generate collection name constants output += `// Collection names\n`; output += `export const Collections = {\n`; for (const collection of this.collections) { const constantName = this.toUpperSnakeCase(collection.name); output += ` ${constantName}: '${collection.name}',\n`; } output += `} as const;\n\n`; // Generate type for collection names output += `export type CollectionName = typeof Collections[keyof typeof Collections];\n`; return output; } toPascalCase(str) { return str.split('_') .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) .join(''); } toUpperSnakeCase(str) { return str.toUpperCase(); } async generate(outputPath = './types/appwrite.types.ts') { console.log('šŸ” Parsing schema...'); await this.parseSchema(); console.log(`šŸ“ Found ${this.collections.length} collections`); const typescript = this.generateTypeScript(); // Ensure directory exists const dir = path.dirname(outputPath); await fs.mkdir(dir, { recursive: true }); // Write TypeScript file await fs.writeFile(outputPath, typescript); console.log(`āœ… TypeScript types generated: ${outputPath}`); return { collections: this.collections.length, outputPath }; } } // CLI usage if (import.meta.url === `file://${process.argv[1]}`) { const generator = new TypeGenerator(process.argv[2] || './appwrite.schema'); const outputPath = process.argv[3] || './types/appwrite.types.ts'; generator.generate(outputPath) .then(result => { console.log(`\n✨ Successfully generated types for ${result.collections} collections`); }) .catch(error => { console.error('āŒ Error generating types:', error); process.exit(1); }); } export default TypeGenerator;