@catalystlabs/awm
Version:
Appwrite Migration Tool - Schema management and code generation for Appwrite databases
182 lines (147 loc) ⢠5.49 kB
JavaScript
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;