@gati-framework/cli
Version:
CLI tool for Gati framework - create, develop, build and deploy cloud-native applications
203 lines • 7.16 kB
JavaScript
/**
* @module cli/codegen/typedef-generator
* @description Generate TypeScript type definitions from GType schemas
*/
/**
* Generate TypeScript type definition from GType schema
*/
export class TypeDefGenerator {
generate(schema, options = {}) {
const opts = {
includeComments: true,
exportType: true,
typeName: 'GeneratedType',
useInterface: true,
...options,
};
const lines = [];
// JSDoc comment
if (opts.includeComments && schema.description) {
lines.push('/**');
lines.push(` * ${schema.description}`);
lines.push(' */');
}
// Generate type definition
const typeDeclaration = this.generateTypeDeclaration(schema, opts);
if (opts.exportType) {
lines.push(`export ${typeDeclaration}`);
}
else {
lines.push(typeDeclaration);
}
return {
code: lines.join('\n'),
typeName: opts.typeName,
};
}
generateTypeDeclaration(schema, options) {
const typeName = options.typeName;
// For objects, prefer interface unless it has nullable/optional modifiers at root level
if (schema.kind === 'object' && options.useInterface && !schema.nullable) {
return this.generateInterface(schema, typeName);
}
const typeExpr = this.generateTypeExpression(schema);
return `type ${typeName} = ${typeExpr};`;
}
generateInterface(schema, typeName) {
const lines = [];
lines.push(`interface ${typeName} {`);
for (const [key, propSchema] of Object.entries(schema.properties)) {
// JSDoc for property
if (propSchema.description) {
lines.push(` /** ${propSchema.description} */`);
}
// Property is optional if explicitly marked optional OR not in required list
const isInRequiredList = schema.required?.includes(key) ?? false;
const isOptional = propSchema.optional === true || (!isInRequiredList && schema.required !== undefined);
const optionalMarker = isOptional ? '?' : '';
const propType = this.generateTypeExpression(propSchema);
lines.push(` ${key}${optionalMarker}: ${propType};`);
}
lines.push('}');
return lines.join('\n');
}
generateTypeExpression(schema) {
let baseType;
switch (schema.kind) {
case 'primitive':
baseType = this.generatePrimitiveType(schema);
break;
case 'literal':
baseType = this.generateLiteralType(schema);
break;
case 'object':
baseType = this.generateObjectType(schema);
break;
case 'array':
baseType = this.generateArrayType(schema);
break;
case 'tuple':
baseType = this.generateTupleType(schema);
break;
case 'union':
baseType = this.generateUnionType(schema);
break;
case 'intersection':
baseType = this.generateIntersectionType(schema);
break;
case 'enum':
baseType = this.generateEnumType(schema);
break;
default:
baseType = 'unknown';
}
// Apply nullable modifier
if (schema.nullable) {
baseType = `${baseType} | null`;
}
return baseType;
}
generatePrimitiveType(schema) {
const typeMap = {
string: 'string',
number: 'number',
boolean: 'boolean',
null: 'null',
undefined: 'undefined',
};
return typeMap[schema.primitiveType] || 'unknown';
}
generateLiteralType(schema) {
if (typeof schema.value === 'string') {
return `'${schema.value}'`;
}
return String(schema.value);
}
generateObjectType(schema) {
if (Object.keys(schema.properties).length === 0) {
return 'Record<string, never>';
}
const props = [];
for (const [key, propSchema] of Object.entries(schema.properties)) {
// Property is optional if explicitly marked optional OR not in required list
const isInRequiredList = schema.required?.includes(key) ?? false;
const isOptional = propSchema.optional === true || (!isInRequiredList && schema.required !== undefined);
const optionalMarker = isOptional ? '?' : '';
const propType = this.generateTypeExpression(propSchema);
props.push(`${key}${optionalMarker}: ${propType}`);
}
return `{ ${props.join('; ')} }`;
}
generateArrayType(schema) {
const itemType = this.generateTypeExpression(schema.items);
return `Array<${itemType}>`;
}
generateTupleType(schema) {
const itemTypes = schema.items.map(item => this.generateTypeExpression(item));
return `[${itemTypes.join(', ')}]`;
}
generateUnionType(schema) {
const types = schema.types.map(type => {
const expr = this.generateTypeExpression(type);
// Wrap complex types in parentheses
if (type.kind === 'intersection' || type.kind === 'union') {
return `(${expr})`;
}
return expr;
});
return types.join(' | ');
}
generateIntersectionType(schema) {
const types = schema.types.map(type => {
const expr = this.generateTypeExpression(type);
// Wrap unions in parentheses
if (type.kind === 'union') {
return `(${expr})`;
}
return expr;
});
return types.join(' & ');
}
generateEnumType(schema) {
const values = schema.values.map(value => {
if (typeof value === 'string') {
return `'${value}'`;
}
return String(value);
});
return values.join(' | ');
}
/**
* Generate multiple type definitions from a schema map
*/
generateMultiple(schemas, options = {}) {
const lines = [];
for (const [name, schema] of Object.entries(schemas)) {
const result = this.generate(schema, {
...options,
typeName: name,
});
lines.push(result.code);
lines.push('');
}
return lines.join('\n');
}
/**
* Generate branded type
*/
generateBrandedType(schema, typeName, brandName) {
const baseType = this.generateTypeExpression(schema);
const code = `export type ${typeName} = ${baseType} & { __brand: '${brandName}' };`;
return {
code,
typeName,
};
}
}
/**
* Create type definition generator instance
*/
export function createTypeDefGenerator() {
return new TypeDefGenerator();
}
//# sourceMappingURL=typedef-generator.js.map