Skip to content

Commit 62f4179

Browse files
committed
refactor(ast/estree): define TS types for extra fields on converters
1 parent 79fa584 commit 62f4179

File tree

11 files changed

+90
-48
lines changed

11 files changed

+90
-48
lines changed

.github/.generated_ast_watch_list.yml

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ src:
2020
- 'crates/oxc_ast/src/generated/get_id.rs'
2121
- 'crates/oxc_ast/src/generated/visit.rs'
2222
- 'crates/oxc_ast/src/generated/visit_mut.rs'
23+
- 'crates/oxc_ast/src/serialize.rs'
2324
- 'crates/oxc_ast_macros/src/generated/mod.rs'
2425
- 'crates/oxc_ast_macros/src/lib.rs'
2526
- 'crates/oxc_regular_expression/src/ast.rs'

crates/oxc_ast/src/ast/js.rs

+8-18
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ pub use match_member_expression;
493493
#[ast(visit)]
494494
#[derive(Debug)]
495495
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
496-
#[estree(rename = "MemberExpression", add_fields(computed = True), add_ts = "computed: true")]
496+
#[estree(rename = "MemberExpression", add_fields(computed = True))]
497497
pub struct ComputedMemberExpression<'a> {
498498
pub span: Span,
499499
pub object: Expression<'a>,
@@ -508,7 +508,7 @@ pub struct ComputedMemberExpression<'a> {
508508
#[ast(visit)]
509509
#[derive(Debug)]
510510
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
511-
#[estree(rename = "MemberExpression", add_fields(computed = False), add_ts = "computed: false")]
511+
#[estree(rename = "MemberExpression", add_fields(computed = False))]
512512
pub struct StaticMemberExpression<'a> {
513513
pub span: Span,
514514
pub object: Expression<'a>,
@@ -522,7 +522,7 @@ pub struct StaticMemberExpression<'a> {
522522
#[ast(visit)]
523523
#[derive(Debug)]
524524
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
525-
#[estree(rename = "MemberExpression", add_fields(computed = False), add_ts = "computed: false")]
525+
#[estree(rename = "MemberExpression", add_fields(computed = False))]
526526
pub struct PrivateFieldExpression<'a> {
527527
pub span: Span,
528528
pub object: Expression<'a>,
@@ -644,7 +644,7 @@ pub struct UpdateExpression<'a> {
644644
#[ast(visit)]
645645
#[derive(Debug)]
646646
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
647-
#[estree(add_fields(prefix = True), add_ts = "prefix: true")]
647+
#[estree(add_fields(prefix = True))]
648648
pub struct UnaryExpression<'a> {
649649
pub span: Span,
650650
pub operator: UnaryOperator,
@@ -668,7 +668,7 @@ pub struct BinaryExpression<'a> {
668668
#[ast(visit)]
669669
#[derive(Debug)]
670670
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
671-
#[estree(rename = "BinaryExpression", add_fields(operator = In), add_ts = "operator: \"in\"")]
671+
#[estree(rename = "BinaryExpression", add_fields(operator = In))]
672672
pub struct PrivateInExpression<'a> {
673673
pub span: Span,
674674
pub left: PrivateIdentifier<'a>,
@@ -898,7 +898,6 @@ pub enum AssignmentTargetProperty<'a> {
898898
#[estree(
899899
rename = "Property",
900900
add_fields(kind = Init, method = False, shorthand = True, computed = False),
901-
add_ts = "kind: \"init\"; method: false; shorthand: true; computed: false"
902901
)]
903902
pub struct AssignmentTargetPropertyIdentifier<'a> {
904903
pub span: Span,
@@ -918,11 +917,7 @@ pub struct AssignmentTargetPropertyIdentifier<'a> {
918917
#[ast(visit)]
919918
#[derive(Debug)]
920919
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
921-
#[estree(
922-
rename = "Property",
923-
add_fields(kind = Init, method = False, shorthand = False),
924-
add_ts = "kind: \"init\"; method: false; shorthand: false"
925-
)]
920+
#[estree(rename = "Property", add_fields(kind = Init, method = False, shorthand = False))]
926921
pub struct AssignmentTargetPropertyProperty<'a> {
927922
pub span: Span,
928923
/// The property key
@@ -1525,11 +1520,7 @@ pub struct ObjectPattern<'a> {
15251520
#[ast(visit)]
15261521
#[derive(Debug)]
15271522
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
1528-
#[estree(
1529-
rename = "Property",
1530-
add_fields(kind = Init, method = False),
1531-
add_ts = "kind: \"init\"; method: false"
1532-
)]
1523+
#[estree(rename = "Property", add_fields(kind = Init, method = False))]
15331524
pub struct BindingProperty<'a> {
15341525
pub span: Span,
15351526
pub key: PropertyKey<'a>,
@@ -1615,7 +1606,6 @@ pub struct BindingRestElement<'a> {
16151606
#[estree(
16161607
add_ts_def = "type ParamPattern = FormalParameter | FormalParameterRest",
16171608
add_fields(expression = False),
1618-
add_ts = "expression: false"
16191609
)]
16201610
pub struct Function<'a> {
16211611
pub span: Span,
@@ -1768,7 +1758,7 @@ pub struct FunctionBody<'a> {
17681758
)]
17691759
#[derive(Debug)]
17701760
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
1771-
#[estree(add_fields(generator = False, id = Null), add_ts = "generator: false; id: null")]
1761+
#[estree(add_fields(generator = False, id = Null))]
17721762
pub struct ArrowFunctionExpression<'a> {
17731763
pub span: Span,
17741764
/// Is the function body an arrow expression? i.e. `() => expr` instead of `() => {}`

crates/oxc_ast/src/ast/literal.rs

+4-12
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use oxc_syntax::number::{BigintBase, NumberBase};
2020
#[ast(visit)]
2121
#[derive(Debug, Clone)]
2222
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
23-
#[estree(rename = "Literal", add_fields(raw = BooleanLiteralRaw), add_ts = "raw: string | null")]
23+
#[estree(rename = "Literal", add_fields(raw = BooleanLiteralRaw))]
2424
pub struct BooleanLiteral {
2525
/// Node location in source code
2626
pub span: Span,
@@ -34,11 +34,7 @@ pub struct BooleanLiteral {
3434
#[ast(visit)]
3535
#[derive(Debug, Clone)]
3636
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
37-
#[estree(
38-
rename = "Literal",
39-
add_fields(value = Null, raw = NullLiteralRaw),
40-
add_ts = "value: null, raw: \"null\" | null",
41-
)]
37+
#[estree(rename = "Literal", add_fields(value = Null, raw = NullLiteralRaw))]
4238
pub struct NullLiteral {
4339
/// Node location in source code
4440
pub span: Span,
@@ -93,11 +89,7 @@ pub struct StringLiteral<'a> {
9389
#[ast(visit)]
9490
#[derive(Debug, Clone)]
9591
#[generate_derive(CloneIn, ContentEq, GetSpan, GetSpanMut, ESTree)]
96-
#[estree(
97-
rename = "Literal",
98-
add_fields(value = Null, bigint = BigIntLiteralBigint),
99-
add_ts = "value: BigInt, bigint: string",
100-
)]
92+
#[estree(rename = "Literal", add_fields(value = BigIntLiteralValue, bigint = BigIntLiteralBigint))]
10193
pub struct BigIntLiteral<'a> {
10294
/// Node location in source code
10395
pub span: Span,
@@ -116,7 +108,7 @@ pub struct BigIntLiteral<'a> {
116108
#[ast(visit)]
117109
#[derive(Debug)]
118110
#[generate_derive(CloneIn, ContentEq, GetSpan, GetSpanMut, ESTree)]
119-
#[estree(rename = "Literal", add_fields(value = Null), add_ts = "value: RegExp | null")]
111+
#[estree(rename = "Literal", add_fields(value = RegExpLiteralValue))]
120112
pub struct RegExpLiteral<'a> {
121113
/// Node location in source code
122114
pub span: Span,

crates/oxc_ast/src/generated/derive_estree.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1969,7 +1969,7 @@ impl Serialize for BigIntLiteral<'_> {
19691969
map.serialize_entry("start", &self.span.start)?;
19701970
map.serialize_entry("end", &self.span.end)?;
19711971
map.serialize_entry("raw", &self.raw)?;
1972-
map.serialize_entry("value", &crate::serialize::Null(self))?;
1972+
map.serialize_entry("value", &crate::serialize::BigIntLiteralValue(self))?;
19731973
map.serialize_entry("bigint", &crate::serialize::BigIntLiteralBigint(self))?;
19741974
map.end()
19751975
}
@@ -1983,7 +1983,7 @@ impl Serialize for RegExpLiteral<'_> {
19831983
map.serialize_entry("end", &self.span.end)?;
19841984
map.serialize_entry("regex", &crate::serialize::RegExpLiteralRegex(self))?;
19851985
map.serialize_entry("raw", &self.raw)?;
1986-
map.serialize_entry("value", &crate::serialize::Null(self))?;
1986+
map.serialize_entry("value", &crate::serialize::RegExpLiteralValue(self))?;
19871987
map.end()
19881988
}
19891989
}

crates/oxc_ast/src/serialize.rs

+43
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use serde::{
77
};
88

99
use oxc_allocator::{Box as ArenaBox, Vec as ArenaVec};
10+
use oxc_ast_macros::ast_meta;
1011
use oxc_span::Span;
1112

1213
use crate::ast::*;
@@ -83,6 +84,8 @@ impl serde_json::ser::Formatter for EcmaFormatter {
8384
// --------------------
8485

8586
/// Serialized as `null`.
87+
#[ast_meta]
88+
#[estree(ts_type = "null")]
8689
pub struct Null<'b, T>(#[expect(dead_code)] pub &'b T);
8790

8891
impl<T> Serialize for Null<'_, T> {
@@ -92,6 +95,8 @@ impl<T> Serialize for Null<'_, T> {
9295
}
9396

9497
/// Serialized as `true`.
98+
#[ast_meta]
99+
#[estree(ts_type = "true")]
95100
pub struct True<'b, T>(#[expect(dead_code)] pub &'b T);
96101

97102
impl<T> Serialize for True<'_, T> {
@@ -101,6 +106,8 @@ impl<T> Serialize for True<'_, T> {
101106
}
102107

103108
/// Serialized as `false`.
109+
#[ast_meta]
110+
#[estree(ts_type = "false")]
104111
pub struct False<'b, T>(#[expect(dead_code)] pub &'b T);
105112

106113
impl<T> Serialize for False<'_, T> {
@@ -110,6 +117,8 @@ impl<T> Serialize for False<'_, T> {
110117
}
111118

112119
/// Serialized as `"in"`.
120+
#[ast_meta]
121+
#[estree(ts_type = "'in'")]
113122
pub struct In<'b, T>(#[expect(dead_code)] pub &'b T);
114123

115124
impl<T> Serialize for In<'_, T> {
@@ -119,6 +128,8 @@ impl<T> Serialize for In<'_, T> {
119128
}
120129

121130
/// Serialized as `"init"`.
131+
#[ast_meta]
132+
#[estree(ts_type = "'init'")]
122133
pub struct Init<'b, T>(#[expect(dead_code)] pub &'b T);
123134

124135
impl<T> Serialize for Init<'_, T> {
@@ -132,6 +143,8 @@ impl<T> Serialize for Init<'_, T> {
132143
// --------------------
133144

134145
/// Serializer for `raw` field of `BooleanLiteral`.
146+
#[ast_meta]
147+
#[estree(ts_type = "string | null")]
135148
pub struct BooleanLiteralRaw<'b>(pub &'b BooleanLiteral);
136149

137150
impl Serialize for BooleanLiteralRaw<'_> {
@@ -148,6 +161,8 @@ impl Serialize for BooleanLiteralRaw<'_> {
148161
}
149162

150163
/// Serializer for `raw` field of `NullLiteral`.
164+
#[ast_meta]
165+
#[estree(ts_type = "'null' | null")]
151166
pub struct NullLiteralRaw<'b>(pub &'b NullLiteral);
152167

153168
impl Serialize for NullLiteralRaw<'_> {
@@ -158,6 +173,8 @@ impl Serialize for NullLiteralRaw<'_> {
158173
}
159174

160175
/// Serializer for `bigint` field of `BigIntLiteral`.
176+
#[ast_meta]
177+
#[estree(ts_type = "string")]
161178
pub struct BigIntLiteralBigint<'a, 'b>(pub &'b BigIntLiteral<'a>);
162179

163180
impl Serialize for BigIntLiteralBigint<'_, '_> {
@@ -167,6 +184,19 @@ impl Serialize for BigIntLiteralBigint<'_, '_> {
167184
}
168185
}
169186

187+
/// Serializer for `value` field of `BigIntLiteral`.
188+
///
189+
/// Serialized as `null` in JSON, but updated on JS side to contain a `BigInt`.
190+
#[ast_meta]
191+
#[estree(ts_type = "BigInt")]
192+
pub struct BigIntLiteralValue<'a, 'b>(#[expect(dead_code)] pub &'b BigIntLiteral<'a>);
193+
194+
impl Serialize for BigIntLiteralValue<'_, '_> {
195+
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
196+
serializer.serialize_none()
197+
}
198+
}
199+
170200
/// Serializer for `regex` field of `RegExpLiteral`.
171201
pub struct RegExpLiteralRegex<'a, 'b>(pub &'b RegExpLiteral<'a>);
172202

@@ -190,6 +220,19 @@ impl Serialize for RegExpLiteralRegex<'_, '_> {
190220
}
191221
}
192222

223+
/// Serializer for `value` field of `RegExpLiteral`.
224+
///
225+
/// Serialized as `null` in JSON, but updated on JS side to contain a `RegExp` if the regexp is valid.
226+
#[ast_meta]
227+
#[estree(ts_type = "RegExp | null")]
228+
pub struct RegExpLiteralValue<'a, 'b>(#[expect(dead_code)] pub &'b RegExpLiteral<'a>);
229+
230+
impl Serialize for RegExpLiteralValue<'_, '_> {
231+
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
232+
serializer.serialize_none()
233+
}
234+
}
235+
193236
impl Serialize for RegExpFlags {
194237
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
195238
serializer.serialize_str(&self.to_string())

tasks/ast_tools/src/derives/estree.rs

+15-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use syn::{parse_str, Expr};
88

99
use crate::{
1010
schema::{Def, EnumDef, FieldDef, Schema, StructDef, TypeDef, VariantDef, Visibility},
11-
utils::{create_ident, number_lit},
11+
utils::number_lit,
1212
Result,
1313
};
1414

@@ -34,14 +34,17 @@ impl Derive for DeriveESTree {
3434
"estree".to_string()
3535
}
3636

37-
/// Register that accept `#[estree]` attr on structs, enums, struct fields, or enum variants.
37+
/// Register that accept `#[estree]` attr on structs, enums, struct fields, enum variants,
38+
/// or meta types.
3839
/// Allow attr on structs and enums which don't derive this trait.
3940
/// Also accept `#[ts]` attr on struct fields and enum variants.
4041
fn attrs(&self) -> &[(&'static str, AttrPositions)] {
4142
&[
4243
(
4344
"estree",
44-
attr_positions!(StructMaybeDerived | EnumMaybeDerived | StructField | EnumVariant),
45+
attr_positions!(
46+
StructMaybeDerived | EnumMaybeDerived | StructField | EnumVariant | Meta
47+
),
4548
),
4649
("ts", attr_positions!(StructField | EnumVariant)),
4750
]
@@ -118,7 +121,6 @@ fn parse_estree_attr(location: AttrLocation, part: AttrPart) -> Result<()> {
118121
struct_def.estree.add_fields.push((name, value));
119122
}
120123
}
121-
AttrPart::String("add_ts", value) => struct_def.estree.add_ts = Some(value),
122124
AttrPart::String("custom_ts_def", value) => {
123125
struct_def.estree.custom_ts_def = Some(value);
124126
}
@@ -184,6 +186,11 @@ fn parse_estree_attr(location: AttrLocation, part: AttrPart) -> Result<()> {
184186
}
185187
_ => return Err(()),
186188
},
189+
// `#[estree]` attr on meta type
190+
AttrLocation::Meta(meta) => match part {
191+
AttrPart::String("ts_type", ts_type) => meta.estree.ts_type = Some(ts_type),
192+
_ => return Err(()),
193+
},
187194
_ => unreachable!(),
188195
}
189196

@@ -233,9 +240,10 @@ fn generate_body_for_struct(struct_def: &StructDef, schema: &Schema) -> TokenStr
233240
};
234241

235242
// Add any additional manually-defined fields
236-
let add_fields = struct_def.estree.add_fields.iter().map(|(name, converter)| {
237-
let converter = create_ident(converter);
238-
quote!( map.serialize_entry(#name, &crate::serialize::#converter(self))?; )
243+
let add_fields = struct_def.estree.add_fields.iter().map(|(name, converter_name)| {
244+
let converter = schema.meta_by_name(converter_name);
245+
let converter_path = converter.import_path_from_crate(krate, schema);
246+
quote!( map.serialize_entry(#name, &#converter_path(self))?; )
239247
});
240248

241249
let stmts = gen.stmts;

tasks/ast_tools/src/generators/typescript.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,12 @@ fn generate_ts_type_def_for_struct(struct_def: &StructDef, schema: &Schema) -> O
113113
}
114114
}
115115

116-
if let Some(add_ts) = struct_def.estree.add_ts.as_deref() {
117-
fields_str.push_str(&format!("\n\t{add_ts};"));
116+
for (add_field_name, converter_name) in &struct_def.estree.add_fields {
117+
let converter = schema.meta_by_name(converter_name);
118+
let Some(add_field_ts_type) = &converter.estree.ts_type else {
119+
panic!("No `ts_type` provided for ESTree converter `{}`", converter.name());
120+
};
121+
fields_str.push_str(&format!("\n\t{add_field_name}: {add_field_ts_type};"));
118122
}
119123

120124
let ts_def = if extends.is_empty() {

tasks/ast_tools/src/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ static SOURCE_PATHS: &[&str] = &[
212212
"crates/oxc_ast/src/ast/jsx.rs",
213213
"crates/oxc_ast/src/ast/ts.rs",
214214
"crates/oxc_ast/src/ast/comment.rs",
215+
"crates/oxc_ast/src/serialize.rs",
215216
"crates/oxc_syntax/src/lib.rs",
216217
"crates/oxc_syntax/src/number.rs",
217218
"crates/oxc_syntax/src/operator.rs",

tasks/ast_tools/src/schema/extensions/estree.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ pub struct ESTreeStruct {
1010
pub custom_serialize: bool,
1111
/// Additional fields to add to struct in ESTree AST.
1212
/// `(name, converter)` where `name` is the name of the field, and `converter` is name of
13-
/// a converter type in the same crate, accessible as `crate::serialize::<converter>`.
13+
/// a converter meta type.
1414
pub add_fields: Vec<(String, String)>,
15-
/// Additional fields to add to TS type definition
16-
pub add_ts: Option<String>,
1715
/// TS alias.
1816
/// e.g. `#[estree(ts_alias = "null")]` means this type won't have a type def generated,
1917
/// and any struct / enum referencing it will substitute `null` as the type.
@@ -64,3 +62,9 @@ pub struct ESTreeEnumVariant {
6462
pub rename: Option<String>,
6563
pub is_ts: bool,
6664
}
65+
66+
/// Configuration for ESTree generator on a meta type.
67+
#[derive(Default, Debug)]
68+
pub struct ESTreeMeta {
69+
pub ts_type: Option<String>,
70+
}

0 commit comments

Comments
 (0)