module

Vow::Codegen

Constants

PRIMITIVE_TS = {"String" => "string", "Bool" => "boolean", "Char" => "string", "Nil" => "null", "Int8" => "number", "Int16" => "number", "Int32" => "number", "Int64" => "number", "Int128" => "number", "UInt8" => "number", "UInt16" => "number", "UInt32" => "number", "UInt64" => "number", "UInt128" => "number", "Float32" => "number", "Float64" => "number", "JSON::Any" => "any"}
PRIMITIVE_TYPES = ["String", "Bool", "Char", "Nil", "Int8", "Int16", "Int32", "Int64", "Int128", "UInt8", "UInt16", "UInt32", "UInt64", "UInt128", "Float32", "Float64", "JSON::Any"]

Crystal types that map to a TypeScript built-in — they don't need to be captured as surface types, and they're the only non-serializable leaves allowed to cross the boundary. Anything else that isn't JSON::Serializable is rejected (see the else branch below). Kept in sync with Vow::Codegen.crystal_to_ts.

Class methods

build_tree(procedures : Array(ProcedureDescriptor)) : Hash(String, Tree)
Source
crystal_to_ts(type : String, known : Hash(String, String) = {} of String => String) : String

Maps a raw Crystal type string (as captured in the manifest) to a TypeScript type, recursively. known maps a captured type's full Crystal name to the TS interface name to use for it.

"Array(Int32)" -> "number[]" "Hash(String, Bool)" -> "Record<string, boolean>" "(Inner | Nil)" -> "Inner | null" "CounterState" (known) -> "CounterState"

An identifier that is neither a primitive nor in known raises UnmappableType — it never falls back to any.

Source
opts_literal(opts : Hash(String, JSON::Any)) : String

Renders a procedure's opaque opts bag as a JS/TS object literal the stub passes to the transport — {} when empty, otherwise {"key": value, ...} with quoted keys (so any opt name is valid) and each value emitted via JSON::Any#to_json (valid JS for strings, numbers, and booleans alike). Both targets call this so the .js runtime and its .d.ts stay byte-for- byte aligned on opts.

Source
render_tree(node : Hash(String, Tree), indent : Int32, type_mode : Bool, leaf : Proc(String, ProcedureDescriptor, String)) : String

Renders the namespace tree to a nested literal. leaf turns a (segment, procedure) pair into one member line (no trailing separator); type_mode switches the member separator and so the literal kind — ; for a TypeScript type literal (a .d.ts shape), , for a value object literal (a .ts/.js runtime object). Branches recurse with the same leaf, so all three targets share one walk.

Source
return_to_ts(type : String, known : Hash(String, String) = {} of String => String) : String

Render a return type for a client stub: Nil becomes void (rather than null), everything else maps normally.

Source

Macros

collect(acc, t, seen)

Appends a Vow::TypeDescriptor to acc for every JSON::Serializable type reachable from t, transitively, walking through generic type args (Array(T), Hash(K, V), …), NamedTuple members (by key), and unions (T?). seen is a |-joined path of visited type names that prevents infinite recursion on self-referential types; DAG diamonds may emit a type more than once, so callers dedup by crystal_name.

The fail-loud contract lives in the else branch: a leaf that is neither a built-in primitive nor JSON::Serializable can't honestly cross the boundary, so we raise at compile time with a fix rather than let codegen invent a type for it.

Unions are unwrapped inline at every recursion site (not delegated) because interpolating a union TypeNode through a macro-call argument renders it as a parenthesized expression that can no longer .resolve.

Source

Nested types