Class: Synthra::Export::Typescript
- Defined in:
- lib/synthra/export/typescript.rb
Overview
Exports schema as TypeScript interfaces
Constant Summary collapse
- TYPE_MAPPINGS =
Type mappings from DSL types to TypeScript types
{ # Strings "uuid" => "string", "ulid" => "string", "email" => "string", "url" => "string", "ip" => "string", "ipv6" => "string", "date" => "string", "past_date" => "string", "future_date" => "string", "timestamp" => "string", "text" => "string", "name" => "string", "full_name" => "string", "first_name" => "string", "last_name" => "string", "phone" => "string", "city" => "string", "country" => "string", "country_code" => "string", "postal_code" => "string", "state" => "string", "street" => "string", "address" => "string", "username" => "string", "social_handle" => "string", "domain" => "string", "user_agent" => "string", "mac_address" => "string", "iban" => "string", "credit_card" => "string", "currency" => "string", "currency_code" => "string", "paragraph" => "string", "sentence" => "string", "word" => "string", "message_text" => "string", "hashtag_text" => "string", "snowflake_id" => "string", "numeric_string_id" => "string", # Numbers (integers + floats both map to TS number) "number" => "number", "integer" => "number", "age" => "number", "airport_elevation" => "number", "discount" => "number", "formula" => "number", "row_number" => "number", "sequence" => "number", "id_sequence" => "number", "float" => "number", "money" => "number", "latitude" => "number", "longitude" => "number", "airport_latitude" => "number", "airport_longitude" => "number", "product_price" => "number", "tax_rate" => "number", "transaction_amount" => "number", # Boolean "boolean" => "boolean", # Date (string in JSON) "date_between" => "string", "mobile_device_release_date" => "string", "now" => "string", "datetime" => "string", # Objects "object" => "Record<string, unknown>", "map_by_field" => "Record<string, unknown>", # Arrays "array" => "unknown[]", "empty_array" => "never[]", "json_array" => "unknown[]", "indices_pair" => "[number, number]" }.freeze
Class Method Summary collapse
-
.export_all(registry, **options) ⇒ String
Export all schemas in a registry.
Instance Method Summary collapse
- #build_array_type(args) ⇒ Object private
- #build_const_type(args) ⇒ Object private
- #build_enum_type(args) ⇒ Object private
- #build_field_comment(field) ⇒ Object private
- #build_interface(target_schema) ⇒ Object
- #build_map_by_field_type(args) ⇒ Object private
- #build_one_of_type(args) ⇒ Object private
- #collect_field_schemas(field, schemas) ⇒ Object private
- #collect_referenced_schemas ⇒ Object private
- #export ⇒ Object
- #field_to_typescript(field) ⇒ Object private
Constructor Details
This class inherits a constructor from Synthra::Export::Base
Class Method Details
.export_all(registry, **options) ⇒ String
Export all schemas in a registry
125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/synthra/export/typescript.rb', line 125 def self.export_all(registry, **) lines = [] lines << "// Generated from Synthra schemas" lines << "// Schemas: #{registry.names.join(', ')}" lines << "" registry.schemas.each do |name, schema| exporter = new(schema, registry: registry, **) lines << exporter.build_interface(schema) lines << "" end lines.join("\n") end |
Instance Method Details
#build_array_type(args) ⇒ Object (private)
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/synthra/export/typescript.rb', line 246 def build_array_type(args) element = args[:element] if element if schema_reference?(element.to_s) "#{element}[]" elsif TYPE_MAPPINGS[element.to_s] "#{TYPE_MAPPINGS[element.to_s]}[]" else # Unknown element types are string-producing types (e.g. array(string)). "string[]" end else "unknown[]" end end |
#build_const_type(args) ⇒ Object (private)
232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/synthra/export/typescript.rb', line 232 def build_const_type(args) value = args[:value] case value when String "\"#{value}\"" when TrueClass, FalseClass value.to_s when Numeric value.to_s else "\"#{value}\"" end end |
#build_enum_type(args) ⇒ Object (private)
227 228 229 230 |
# File 'lib/synthra/export/typescript.rb', line 227 def build_enum_type(args) values = args[:values]&.map { |v| v.respond_to?(:value) ? v.value : v } || [] values.map { |v| "\"#{v}\"" }.join(" | ") end |
#build_field_comment(field) ⇒ Object (private)
286 287 288 289 290 291 292 293 294 295 296 |
# File 'lib/synthra/export/typescript.rb', line 286 def build_field_comment(field) return nil unless [:comments] comments = [] comments << "@type #{field.type_name}" if field.type_name comments << "@optional" if field.optional? comments << "@nullable" if field.nullable? return nil if comments.empty? "/** #{comments.join(' ')} */" end |
#build_interface(target_schema) ⇒ Object
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/synthra/export/typescript.rb', line 140 def build_interface(target_schema) lines = [] if target_schema.deprecated? lines << "/** @deprecated #{target_schema. || 'This interface is deprecated'} */" end lines << "interface #{target_schema.name} {" target_schema.fields.each do |field| ts_type = field_to_typescript(field) optional = field.optional? ? "?" : "" nullable = field.nullable? ? " | null" : "" # Add JSDoc for complex fields comment = build_field_comment(field) lines << " #{comment}" if comment lines << " #{field.name}#{optional}: #{ts_type}#{nullable};" end lines << "}" lines.join("\n") end |
#build_map_by_field_type(args) ⇒ Object (private)
271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/synthra/export/typescript.rb', line 271 def build_map_by_field_type(args) entries = args[:entries] || {} if entries.empty? "Record<string, unknown>" else # Create a more specific type for known entries entry_types = entries.map do |name, info| schema_name = info.is_a?(Hash) ? (info[:schema] || info["schema"]) : info.to_s type = schema_reference?(schema_name) ? schema_name : "unknown" "#{name}: #{type}" end "{ [key: string]: { #{entry_types.join('; ')} }[typeof key] }" end end |
#build_one_of_type(args) ⇒ Object (private)
262 263 264 265 266 267 268 269 |
# File 'lib/synthra/export/typescript.rb', line 262 def build_one_of_type(args) schemas = args[:schemas] || [] types = schemas.map do |s| schema_name = s.is_a?(Hash) ? (s[:schema] || s["schema"]) : s.to_s schema_reference?(schema_name) ? schema_name : "unknown" end types.join(" | ") end |
#collect_field_schemas(field, schemas) ⇒ Object (private)
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/synthra/export/typescript.rb', line 175 def collect_field_schemas(field, schemas) type_name = field.type_name if schema_reference?(type_name) ref_schema = get_schema(type_name) if ref_schema && !schemas.any? { |s| s.name == ref_schema.name } schemas << ref_schema # Recursively collect nested schemas ref_schema.fields.each { |f| collect_field_schemas(f, schemas) } end end if type_name == "array" element = field.type_args[:element] if element && schema_reference?(element.to_s) ref_schema = get_schema(element.to_s) if ref_schema && !schemas.any? { |s| s.name == ref_schema.name } schemas << ref_schema ref_schema.fields.each { |f| collect_field_schemas(f, schemas) } end end end end |
#collect_referenced_schemas ⇒ Object (private)
167 168 169 170 171 172 173 |
# File 'lib/synthra/export/typescript.rb', line 167 def collect_referenced_schemas schemas = [] schema.fields.each do |field| collect_field_schemas(field, schemas) end schemas.uniq(&:name) end |
#export ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/synthra/export/typescript.rb', line 96 def export lines = [] # Add header comment lines << "// Generated from Synthra schema: #{schema.name}" lines << "// Version: #{schema.version}" if schema.version lines << "// DEPRECATED: #{schema.}" if schema.deprecated? lines << "" # Export referenced schemas first exported = Set.new collect_referenced_schemas.each do |ref_schema| lines << build_interface(ref_schema) lines << "" exported << ref_schema.name end # Export main schema lines << build_interface(schema) unless exported.include?(schema.name) lines.join("\n") end |
#field_to_typescript(field) ⇒ Object (private)
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/synthra/export/typescript.rb', line 199 def field_to_typescript(field) type_name = field.type_name type_args = field.type_args case type_name when "enum" build_enum_type(type_args) when "const" build_const_type(type_args) when "array" build_array_type(type_args) when "one_of" build_one_of_type(type_args) when "map_by_field" build_map_by_field_type(type_args) else if TYPE_MAPPINGS[type_name] TYPE_MAPPINGS[type_name] elsif schema_reference?(type_name) type_name else # Most Synthra types generate strings, so an unmapped, non-structural # type is a string rather than unknown. "string" end end end |