Add validators, generators, and utils modules
Some checks failed
CI / test (push) Has been cancelled

This commit is contained in:
2026-02-04 21:55:17 +00:00
parent 1bdf2c5b48
commit 40c1f1aef3

View File

@@ -0,0 +1,125 @@
"""TypeScript interface generator."""
from typing import Any, Dict, List, Optional
from config_converter.validators.schema import InferredSchema, SchemaProperty, SchemaInferrer, SchemaType
class TypeScriptGenerator:
"""Generates TypeScript interfaces from inferred schemas."""
PYTHON_TO_TS: Dict[str, str] = {
SchemaType.NULL: "null",
SchemaType.BOOLEAN: "boolean",
SchemaType.NUMBER: "number",
SchemaType.INTEGER: "number",
SchemaType.STRING: "string",
SchemaType.ARRAY: "Array<unknown>",
SchemaType.OBJECT: "Record<string, unknown>",
}
def __init__(self, interface_name: str = "Config"):
self.interface_name = interface_name
self.inferrer = SchemaInferrer()
def generate(self, data: Any) -> str:
"""Generate TypeScript interface from data."""
schema = self.inferrer.infer(data)
return self._generate_interface(schema, self.interface_name)
def generate_from_schema(self, schema: InferredSchema, interface_name: Optional[str] = None) -> str:
"""Generate TypeScript interface from schema."""
name = interface_name or self.interface_name
return self._generate_interface(schema, name)
def _generate_interface(self, schema: InferredSchema, name: str) -> str:
"""Generate a TypeScript interface."""
if schema.root_type == SchemaType.ARRAY:
return self._generate_array_interface(schema, name)
elif schema.root_type == SchemaType.OBJECT:
return self._generate_object_interface(schema, name)
else:
return self._generate_simple_interface(schema, name)
def _generate_object_interface(self, schema: InferredSchema, name: str) -> str:
"""Generate TypeScript interface for object type."""
lines = [f"export interface {name} {{"]
nested_interfaces: List[str] = []
for prop in schema.properties or []:
prop_type = self._get_property_type(prop, nested_interfaces)
lines.append(f" {prop.name}: {prop_type};")
lines.append("}")
if nested_interfaces:
lines.append("")
lines.extend(nested_interfaces)
return "\n".join(lines)
def _generate_array_interface(self, schema: InferredSchema, name: str) -> str:
"""Generate TypeScript interface for array type."""
if schema.items:
if schema.items.type == SchemaType.OBJECT and schema.items.properties:
item_name = f"{name}Item"
nested_interfaces: List[str] = []
item_type = self._get_property_type(schema.items, nested_interfaces, item_name)
lines = [f"export type {name} = {item_type};"]
if nested_interfaces:
lines.append("")
lines.extend(nested_interfaces)
return "\n".join(lines) if lines else f"export type {name} = unknown[];"
return f"export type {name} = {self.PYTHON_TO_TS.get(schema.root_type, 'unknown[]')};"
def _generate_simple_interface(self, schema: InferredSchema, name: str) -> str:
"""Generate TypeScript type alias for simple type."""
ts_type = self.PYTHON_TO_TS.get(schema.root_type, "unknown")
return f"export type {name} = {ts_type};"
def _get_property_type(
self, prop: SchemaProperty, nested_interfaces: List[str], prefix: str = ""
) -> str:
"""Get TypeScript type for a property."""
ts_type = self.PYTHON_TO_TS.get(prop.type, "unknown")
if prop.type == SchemaType.OBJECT and prop.properties:
interface_name = prefix or self._to_camel_case(prop.name)
interface_code = self._generate_object_interface(
InferredSchema(root_type=SchemaType.OBJECT, properties=prop.properties),
interface_name,
)
if interface_code not in nested_interfaces:
nested_interfaces.append(interface_code)
return interface_name
if prop.type == SchemaType.ARRAY and prop.items:
if prop.items.type == SchemaType.OBJECT and prop.items.properties:
item_name = f"{self._to_camel_case(prop.name)}Item"
item_interface = self._generate_object_interface(
InferredSchema(root_type=SchemaType.OBJECT, properties=prop.items.properties),
item_name,
)
if item_interface not in nested_interfaces:
nested_interfaces.append(item_interface)
ts_type = f"{item_name}[]"
else:
item_type = self.PYTHON_TO_TS.get(prop.items.type, "unknown")
ts_type = f"Array<{item_type}>"
return ts_type
def _to_camel_case(self, name: str) -> str:
"""Convert snake_case or kebab-case to PascalCase."""
parts = name.replace("-", "_").split("_")
return "".join(part.capitalize() for part in parts if part)
def generate_from_json(self, json_str: str) -> str:
"""Generate TypeScript from JSON string."""
import json
data = json.loads(json_str)
return self.generate(data)