From 40c1f1aef3487c839d92c1b9e280dcdb97624ce8 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Wed, 4 Feb 2026 21:55:17 +0000 Subject: [PATCH] Add validators, generators, and utils modules --- config_converter/generators/typescript.py | 125 ++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 config_converter/generators/typescript.py diff --git a/config_converter/generators/typescript.py b/config_converter/generators/typescript.py new file mode 100644 index 0000000..79e9fee --- /dev/null +++ b/config_converter/generators/typescript.py @@ -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", + SchemaType.OBJECT: "Record", + } + + 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)