Skip to main content

ts_gen/
ir.rs

1//! Intermediate Representation for TypeScript declarations.
2//!
3//! This module defines the IR that sits between the parsed TypeScript AST
4//! and the generated Rust code. The pipeline is:
5//!
6//! ```text
7//! .d.ts → oxc_parser AST → First Pass (collect + populate) → IR → Codegen → .rs
8//! ```
9
10use std::collections::HashMap;
11
12use crate::context::TypeId;
13use crate::parse::scope::ScopeId;
14
15// ─── Module Context ──────────────────────────────────────────────────
16
17/// Where a declaration lives — either as a global ambient declaration
18/// or inside a specific JS module.
19///
20/// Uses `Rc<str>` for the module name to make cloning cheap (ref count bump
21/// instead of heap allocation), since `ModuleContext` is cloned extensively
22/// during IR construction.
23#[derive(Clone, Debug, PartialEq, Eq, Hash)]
24pub enum ModuleContext {
25    /// No `module=` attribute. Accessed as a global.
26    Global,
27    /// From a specific JS module specifier (e.g., `"cloudflare:sockets"`).
28    Module(std::rc::Rc<str>),
29}
30
31// ─── Type References ─────────────────────────────────────────────────
32
33/// A reference to a TypeScript type, resolved from the AST.
34#[derive(Clone, Debug, PartialEq)]
35
36pub enum TypeRef {
37    // === Primitives ===
38    Boolean,
39    Number,
40    String,
41    BigInt,
42    Void,
43    Undefined,
44    Null,
45    Any,
46    Unknown,
47    Object,
48    Symbol,
49
50    // === Typed Arrays ===
51    Int8Array,
52    Uint8Array,
53    Uint8ClampedArray,
54    Int16Array,
55    Uint16Array,
56    Int32Array,
57    Uint32Array,
58    Float32Array,
59    Float64Array,
60    BigInt64Array,
61    BigUint64Array,
62    ArrayBuffer,
63    ArrayBufferView,
64    DataView,
65
66    // === Built-in Generic Containers ===
67    Promise(Box<TypeRef>),
68    Array(Box<TypeRef>),
69    Record(Box<TypeRef>, Box<TypeRef>),
70    Map(Box<TypeRef>, Box<TypeRef>),
71    Set(Box<TypeRef>),
72
73    // === Structural Types ===
74    Nullable(Box<TypeRef>),
75    Union(Vec<TypeRef>),
76    Intersection(Vec<TypeRef>),
77    Tuple(Vec<TypeRef>),
78    Function(FunctionSig),
79
80    // === Literal Types ===
81    StringLiteral(String),
82    NumberLiteral(f64),
83    BooleanLiteral(bool),
84
85    // === Named References ===
86    /// Reference to a type by name. Resolved during first pass.
87    Named(String),
88    /// Generic instantiation: `Named<T1, T2, ...>`
89    GenericInstantiation(String, Vec<TypeRef>),
90
91    // === Special ===
92    Date,
93    RegExp,
94    Error,
95
96    // === Fallback ===
97    /// For TS constructs we can't represent (conditional types, mapped types,
98    /// template literals, `keyof`, etc.) — erased to `JsValue`.
99    Unresolved(String),
100}
101
102// ─── Function Signatures ─────────────────────────────────────────────
103
104#[derive(Clone, Debug, PartialEq)]
105
106pub struct FunctionSig {
107    pub params: Vec<Param>,
108    pub return_type: Box<TypeRef>,
109}
110
111#[derive(Clone, Debug, PartialEq)]
112
113pub struct Param {
114    pub name: String,
115    pub type_ref: TypeRef,
116    pub optional: bool,
117    pub variadic: bool,
118}
119
120// ─── Type Parameters (Generics) ──────────────────────────────────────
121
122#[derive(Clone, Debug, PartialEq)]
123
124pub struct TypeParam {
125    pub name: String,
126    /// `T extends Foo` — used for classification only.
127    pub constraint: Option<TypeRef>,
128    /// `T = Bar` — used for default instantiation.
129    pub default: Option<TypeRef>,
130}
131
132// ─── Top-level Module ────────────────────────────────────────────────
133
134/// A parsed module — references types and scopes in the `GlobalContext`.
135#[derive(Clone, Debug)]
136
137pub struct Module {
138    /// Type ids for declarations from the input files (what to generate code for).
139    pub types: Vec<TypeId>,
140    /// Optional library name from `--lib-name`.
141    pub lib_name: Option<String>,
142    /// Builtin (root) scope id.
143    pub builtin_scope: ScopeId,
144    /// Input file scope ids (for scope-based codegen iteration).
145    pub file_scopes: Vec<ScopeId>,
146}
147
148#[derive(Clone, Debug)]
149
150pub struct TypeDeclaration {
151    pub kind: TypeKind,
152    pub module_context: ModuleContext,
153    pub doc: Option<String>,
154    /// The scope this declaration was defined in (for type reference resolution).
155    pub scope_id: crate::parse::scope::ScopeId,
156    /// Whether this declaration was explicitly exported.
157    /// In script mode, all declarations are implicitly public.
158    /// In module mode, only exported declarations are public.
159    pub exported: bool,
160}
161
162#[derive(Clone, Debug)]
163
164pub enum TypeKind {
165    Class(ClassDecl),
166    Interface(InterfaceDecl),
167    TypeAlias(TypeAliasDecl),
168    StringEnum(StringEnumDecl),
169    NumericEnum(NumericEnumDecl),
170    Function(FunctionDecl),
171    Variable(VariableDecl),
172    Namespace(NamespaceDecl),
173}
174
175// ─── Class ───────────────────────────────────────────────────────────
176
177#[derive(Clone, Debug)]
178
179pub struct ClassDecl {
180    pub name: String,
181    /// Original JS name (may differ from Rust name after case conversion).
182    pub js_name: String,
183    pub type_params: Vec<TypeParam>,
184    /// Immediate parent class.
185    pub extends: Option<TypeRef>,
186    pub implements: Vec<TypeRef>,
187    pub is_abstract: bool,
188    pub members: Vec<Member>,
189    /// Where the type declaration itself should live.
190    pub type_module_context: ModuleContext,
191}
192
193// ─── Interface ───────────────────────────────────────────────────────
194
195#[derive(Clone, Debug)]
196
197pub struct InterfaceDecl {
198    pub name: String,
199    pub js_name: String,
200    pub type_params: Vec<TypeParam>,
201    pub extends: Vec<TypeRef>,
202    pub members: Vec<Member>,
203    /// Classification determined during assembly.
204    pub classification: InterfaceClassification,
205}
206
207#[derive(Clone, Debug, PartialEq, Eq)]
208
209pub enum InterfaceClassification {
210    /// Has methods, used as a class-like type.
211    ClassLike,
212    /// All optional properties, no methods → options bag / dictionary.
213    Dictionary,
214    /// Mixed or unclear — treat as class-like.
215    Unclassified,
216}
217
218// ─── Type Alias ──────────────────────────────────────────────────────
219
220#[derive(Clone, Debug)]
221
222pub struct TypeAliasDecl {
223    pub name: String,
224    pub type_params: Vec<TypeParam>,
225    pub target: TypeRef,
226    /// If this alias is a re-export from an external module, the module specifier.
227    /// Used by codegen to emit `pub use <external>::Foo;` instead of `pub type`.
228    pub from_module: Option<String>,
229}
230
231// ─── String Enum ─────────────────────────────────────────────────────
232
233#[derive(Clone, Debug)]
234
235pub struct StringEnumDecl {
236    pub name: String,
237    pub variants: Vec<StringEnumVariant>,
238}
239
240#[derive(Clone, Debug)]
241
242pub struct StringEnumVariant {
243    /// PascalCase variant name.
244    pub rust_name: String,
245    /// Original string value.
246    pub js_value: String,
247}
248
249// ─── Numeric Enum ────────────────────────────────────────────────────
250
251#[derive(Clone, Debug)]
252
253pub struct NumericEnumDecl {
254    pub name: String,
255    pub variants: Vec<NumericEnumVariant>,
256}
257
258#[derive(Clone, Debug)]
259
260pub struct NumericEnumVariant {
261    /// PascalCase variant name.
262    pub rust_name: String,
263    /// Original JS member name.
264    pub js_name: String,
265    /// Numeric discriminant value.
266    pub value: i64,
267    /// Doc comment for this variant.
268    pub doc: Option<String>,
269}
270
271// ─── Function ────────────────────────────────────────────────────────
272
273#[derive(Clone, Debug)]
274
275pub struct FunctionDecl {
276    pub name: String,
277    pub js_name: String,
278    pub type_params: Vec<TypeParam>,
279    pub params: Vec<Param>,
280    pub return_type: TypeRef,
281    pub overloads: Vec<FunctionOverload>,
282}
283
284#[derive(Clone, Debug)]
285
286pub struct FunctionOverload {
287    pub params: Vec<Param>,
288    pub return_type: TypeRef,
289}
290
291// ─── Variable ────────────────────────────────────────────────────────
292
293#[derive(Clone, Debug)]
294
295pub struct VariableDecl {
296    pub name: String,
297    pub js_name: String,
298    pub type_ref: TypeRef,
299    pub is_const: bool,
300}
301
302// ─── Namespace ───────────────────────────────────────────────────────
303
304#[derive(Clone, Debug)]
305
306pub struct NamespaceDecl {
307    pub name: String,
308    pub declarations: Vec<TypeDeclaration>,
309    /// The child scope containing this namespace's member types.
310    pub child_scope: crate::parse::scope::ScopeId,
311}
312
313// ─── Members ─────────────────────────────────────────────────────────
314
315#[derive(Clone, Debug)]
316
317pub enum Member {
318    Getter(GetterMember),
319    Setter(SetterMember),
320    Method(MethodMember),
321    Constructor(ConstructorMember),
322    IndexSignature(IndexSigMember),
323    StaticGetter(StaticGetterMember),
324    StaticSetter(StaticSetterMember),
325    StaticMethod(StaticMethodMember),
326}
327
328/// Instance property getter binding.
329///
330/// Emits: `fn <name>(this: &T) -> R;` with `#[wasm_bindgen(method, getter)]`.
331#[derive(Clone, Debug)]
332
333pub struct GetterMember {
334    /// Original JS property name (source of truth for naming).
335    pub js_name: String,
336    /// Return type of the getter.
337    pub type_ref: TypeRef,
338    /// Whether declared with `?:` syntax — wraps return in `Option`.
339    pub optional: bool,
340    pub doc: Option<String>,
341}
342
343/// Instance property setter binding.
344///
345/// Emits: `fn set_<name>(this: &T, val: V);` with `#[wasm_bindgen(method, setter)]`.
346#[derive(Clone, Debug)]
347
348pub struct SetterMember {
349    /// Original JS property name (source of truth for naming).
350    pub js_name: String,
351    /// Parameter type of the setter.
352    pub type_ref: TypeRef,
353    pub doc: Option<String>,
354}
355
356#[derive(Clone, Debug)]
357
358pub struct MethodMember {
359    pub name: String,
360    pub js_name: String,
361    pub type_params: Vec<TypeParam>,
362    pub params: Vec<Param>,
363    pub return_type: TypeRef,
364    pub optional: bool,
365    pub doc: Option<String>,
366}
367
368#[derive(Clone, Debug)]
369
370pub struct ConstructorMember {
371    pub params: Vec<Param>,
372    pub doc: Option<String>,
373}
374
375#[derive(Clone, Debug)]
376
377pub struct IndexSigMember {
378    pub key_type: TypeRef,
379    pub value_type: TypeRef,
380    pub readonly: bool,
381}
382
383/// Static property getter binding.
384///
385/// Emits: `fn <name>() -> R;` with `#[wasm_bindgen(static_method_of = T, getter)]`.
386#[derive(Clone, Debug)]
387
388pub struct StaticGetterMember {
389    /// Original JS property name (source of truth for naming).
390    pub js_name: String,
391    pub type_ref: TypeRef,
392    pub doc: Option<String>,
393}
394
395/// Static property setter binding.
396///
397/// Emits: `fn set_<name>(val: V);` with `#[wasm_bindgen(static_method_of = T, setter)]`.
398#[derive(Clone, Debug)]
399
400pub struct StaticSetterMember {
401    /// Original JS property name (source of truth for naming).
402    pub js_name: String,
403    pub type_ref: TypeRef,
404    pub doc: Option<String>,
405}
406
407#[derive(Clone, Debug)]
408
409pub struct StaticMethodMember {
410    pub name: String,
411    pub js_name: String,
412    pub type_params: Vec<TypeParam>,
413    pub params: Vec<Param>,
414    pub return_type: TypeRef,
415    pub doc: Option<String>,
416}
417
418// ─── Type Registry (First Pass) ──────────────────────────────────────
419
420/// Registry built during Phase 1 of the first pass, mapping type names
421/// to their kind and primary declaration context.
422#[derive(Clone, Debug, Default)]
423
424pub struct TypeRegistry {
425    pub types: HashMap<String, TypeInfo>,
426}
427
428#[derive(Clone, Debug)]
429
430pub struct TypeInfo {
431    pub kind: RegisteredKind,
432    /// Where is this type's "primary" declaration?
433    pub primary_context: ModuleContext,
434}
435
436#[derive(Clone, Debug, PartialEq, Eq)]
437
438pub enum RegisteredKind {
439    Class,
440    Interface,
441    /// `var` + `interface` pattern merged together.
442    MergedClassLike,
443    TypeAlias,
444    /// Detected when a type alias is a union of string literals.
445    StringEnum,
446    /// A TS enum with numeric (or auto-incrementing) values.
447    NumericEnum,
448    Function,
449    Variable,
450    Namespace,
451}