@docyrus/tanstack-db-generator
Version:
Code generator utilities for TanStack Query / Database integration with Docyrus API
116 lines (115 loc) • 4.46 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateInterfacesForNames = generateInterfacesForNames;
function resolveSchema(schema, spec) {
if (schema && schema.$ref) {
const ref = schema.$ref;
const path = ref.replace('#/', '').split('/');
let current = spec;
for (const segment of path) {
current = current?.[segment];
if (!current)
break;
}
return (current || {});
}
return (schema || {});
}
function resolveSchemaProperties(schema, spec) {
let properties = {};
if (schema.allOf) {
for (const subSchema of schema.allOf) {
const resolved = resolveSchema(subSchema, spec);
if (resolved.properties) {
const resolvedProps = {};
for (const [key, value] of Object.entries(resolved.properties)) {
resolvedProps[key] = resolveSchema(value, spec);
}
properties = { ...properties, ...resolvedProps };
}
}
}
if (schema.properties) {
const resolvedProps = {};
for (const [key, value] of Object.entries(schema.properties)) {
resolvedProps[key] = resolveSchema(value, spec);
}
properties = { ...properties, ...resolvedProps };
}
return properties;
}
function getTypeScriptType(schema, spec) {
if (schema.oneOf) {
const types = schema.oneOf
.map((s) => getTypeScriptType(resolveSchema(s, spec), spec))
.filter((type, index, self) => self.indexOf(type) === index);
return types.join(' | ');
}
switch (schema.type) {
case 'string':
if (schema.enum) {
return schema.enum.map((v) => `'${v}'`).join(' | ');
}
return 'string';
case 'number':
case 'integer':
return 'number';
case 'boolean':
return 'boolean';
case 'array':
if (schema.items) {
return `Array<${getTypeScriptType(resolveSchema(schema.items, spec), spec)}>`;
}
return 'Array<any>';
case 'object':
if (schema.properties) {
const props = Object.entries(schema.properties)
.map(([key, value]) => `${key}: ${getTypeScriptType(resolveSchema(value, spec), spec)}`)
.join('; ');
return `{ ${props} }`;
}
return 'Record<string, any>';
default:
// If there is no explicit type, but $ref existed, we already resolved it above.
if (schema.properties) {
const props = Object.entries(schema.properties)
.map(([key, value]) => `${key}: ${getTypeScriptType(resolveSchema(value, spec), spec)}`)
.join('; ');
return `{ ${props} }`;
}
return 'any';
}
}
function generateInterfaceFromSchema(name, schema, spec) {
const properties = resolveSchemaProperties(schema, spec);
const lines = [];
for (const [propName, propSchema] of Object.entries(properties)) {
const resolved = resolveSchema(propSchema, spec);
const type = getTypeScriptType(resolved, spec);
const isOptional = !schema.required?.includes(propName) || !!resolved.readOnly;
const propDesc = resolved.description ? `\n /** ${sanitizeJsDoc(resolved.description)} */` : '';
lines.push(`${propDesc}\n ${propName}${isOptional ? '?' : ''}: ${type};`);
}
const ifaceDesc = schema.description ? `/** ${sanitizeJsDoc(schema.description)} */\n` : '';
return `${ifaceDesc}export interface ${name} {
${lines.join('\n')}
}`;
}
function generateInterfacesForNames(spec, names) {
const out = [];
const unique = Array.from(new Set(names)).filter((n) => !!n);
for (const name of unique) {
const schema = spec.components?.schemas?.[name];
if (schema) {
out.push(generateInterfaceFromSchema(name, schema, spec));
}
else {
// Fallback placeholder for refs that do not exist in components.schemas
out.push(`/** Unresolved schema for ${name}. */\nexport interface ${name} { [key: string]: any }`);
}
}
return out.join('\n\n');
}
function sanitizeJsDoc(text) {
return String(text).replace(/\*\//g, '*\/').trim();
}