hey-api-builders
Version:
A custom plugin for @hey-api/openapi-ts that generates mock data builders with a lightweight custom runtime, Zod integration, or static mock generation.
201 lines (162 loc) • 5.96 kB
text/typescript
import type { Schema } from 'json-schema-faker';
import type { ExtendedSchema, JsonValue } from './types';
export function generateStaticMockCode(schema: Schema, typeName: string): string {
return generateStaticMockInternal(schema as ExtendedSchema, typeName, 0);
}
function generateStaticMockInternal(schema: ExtendedSchema, typeName: string, depth: number): string {
if (!schema || typeof schema !== 'object') {
return 'null';
}
const indent = ' '.repeat(depth);
if (schema.anyOf && Array.isArray(schema.anyOf)) {
const enumValues = schema.anyOf
.filter(item => item && typeof item === 'object' && 'const' in item)
.map(item => (item as { const: JsonValue }).const);
if (enumValues.length > 0) {
return JSON.stringify(enumValues[0]);
}
}
if (schema.enum && Array.isArray(schema.enum)) {
return JSON.stringify(schema.enum[0]);
}
if (Array.isArray(schema.type)) {
const types = schema.type.filter(t => t !== 'null');
if (types.length > 0 && types[0]) {
return generateStaticForSingleType(types[0], schema, typeName, depth);
}
}
if (typeof schema.type === 'string') {
return generateStaticForSingleType(schema.type, schema, typeName, depth);
}
if (schema.properties && !schema.type) {
return generateStaticObject(schema, typeName, depth);
}
if (schema.items && !schema.type) {
return generateStaticArray(schema, typeName, depth);
}
return 'null';
}
function generateStaticForSingleType(type: string, schema: ExtendedSchema, typeName: string, depth: number): string {
switch (type) {
case 'string':
return generateStaticString(schema);
case 'number':
return generateStaticNumber(schema);
case 'integer':
return generateStaticInteger(schema);
case 'boolean':
return 'true';
case 'null':
return 'null';
case 'array':
return generateStaticArray(schema, typeName, depth);
case 'object':
return generateStaticObject(schema, typeName, depth);
default:
return 'null';
}
}
function generateStaticString(schema: ExtendedSchema): string {
if (schema.format) {
switch (schema.format) {
case 'uuid':
return '"550e8400-e29b-41d4-a716-446655440000"';
case 'email':
return '"user@example.com"';
case 'uri':
case 'url':
return '"https:
case 'date':
return '"2024-01-01"';
case 'date-time':
return '"2024-01-01T00:00:00.000Z"';
case 'phone':
return '"+15551234567"';
}
}
if (schema.default !== undefined) {
return JSON.stringify(schema.default);
}
if (schema.examples && Array.isArray(schema.examples) && schema.examples.length > 0) {
return JSON.stringify(schema.examples[0]);
}
if (schema.pattern) {
return '"pattern-match"';
}
const minLength = typeof schema.minLength === 'number' ? schema.minLength : 5;
const targetLength = Math.max(minLength, 5);
return JSON.stringify('a'.repeat(targetLength));
}
function generateStaticNumber(schema: ExtendedSchema): string {
if (typeof schema.default === 'number') {
return String(schema.default);
}
if (schema.examples && Array.isArray(schema.examples) && schema.examples.length > 0) {
const example = schema.examples[0];
if (typeof example === 'number') {
return String(example);
}
}
const min = typeof schema.minimum === 'number' ? schema.minimum : 0;
const max = typeof schema.maximum === 'number' ? schema.maximum : 100;
const value = min + Math.floor((max - min) / 2);
return String(value);
}
function generateStaticInteger(schema: ExtendedSchema): string {
if (typeof schema.default === 'number') {
return String(Math.floor(schema.default));
}
if (schema.examples && Array.isArray(schema.examples) && schema.examples.length > 0) {
const example = schema.examples[0];
if (typeof example === 'number') {
return String(Math.floor(example));
}
}
const min = typeof schema.minimum === 'number' ? Math.ceil(schema.minimum) : 0;
const max = typeof schema.maximum === 'number' ? Math.floor(schema.maximum) : 100;
const value = min + Math.floor((max - min) / 2);
return String(value);
}
function generateStaticArray(schema: ExtendedSchema, typeName: string, depth: number): string {
if (!schema.items) {
return '[]';
}
const indent = ' '.repeat(depth);
const nextIndent = ' '.repeat(depth + 1);
const minItems = typeof schema.minItems === 'number' ? schema.minItems : 1;
const itemCount = Math.max(minItems, 1);
if (Array.isArray(schema.items)) {
const items = schema.items.map(item =>
generateStaticMockInternal(item as ExtendedSchema, typeName, depth + 1)
);
return `[\n${nextIndent}${items.join(`,\n${nextIndent}`)}\n${indent}]`;
} else {
const itemMock = generateStaticMockInternal(schema.items as ExtendedSchema, typeName, depth + 1);
if (itemCount === 1) {
return `[${itemMock}]`;
}
const items = Array(itemCount).fill(itemMock);
return `[\n${nextIndent}${items.join(`,\n${nextIndent}`)}\n${indent}]`;
}
}
function generateStaticObject(schema: ExtendedSchema, typeName: string, depth: number): string {
if (!schema.properties) {
return '{}';
}
const indent = ' '.repeat(depth);
const nextIndent = ' '.repeat(depth + 1);
const properties: string[] = [];
const required = new Set(schema.required || []);
for (const [key, propSchema] of Object.entries(schema.properties)) {
if (!required.has(key)) {
continue;
}
const propValue = generateStaticMockInternal(propSchema as ExtendedSchema, typeName, depth + 1);
const safePropName = /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key) ? key : `"${key}"`;
properties.push(`${safePropName}: ${propValue}`);
}
if (properties.length === 0) {
return '{}';
}
return `{\n${nextIndent}${properties.join(`,\n${nextIndent}`)}\n${indent}}`;
}