Skip to content

Commit

Permalink
refactor(ast): support #[estree(ts_alias)] attr on enums and use it…
Browse files Browse the repository at this point in the history
… on `RegExpPattern` and `RegExpFlags` (#8953)

It's preferable to specify how a type is serialized and its TS type on the type itself, rather than on fields containing that type. Support the `#[estree(ts_alias = "...")]` attr on enums (previously was only supported on structs). Use that attr on `RegExpPattern` and `RegExpFlags`.
  • Loading branch information
overlookmotel committed Feb 9, 2025
1 parent a2883b1 commit a1ca2eb
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 21 deletions.
6 changes: 2 additions & 4 deletions crates/oxc_ast/src/ast/literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,8 @@ pub struct RegExpLiteral<'a> {
#[estree(no_type)]
pub struct RegExp<'a> {
/// The regex pattern between the slashes
#[estree(ts_type = "string")]
pub pattern: RegExpPattern<'a>,
/// Regex flags after the closing slash
#[estree(ts_type = "string")]
pub flags: RegExpFlags,
}

Expand All @@ -166,7 +164,7 @@ pub struct RegExp<'a> {
#[ast]
#[derive(Debug)]
#[generate_derive(CloneIn, ESTree)]
#[estree(custom_serialize, no_ts_def)]
#[estree(custom_serialize, ts_alias = "string")]
pub enum RegExpPattern<'a> {
/// Unparsed pattern. Contains string slice of the pattern.
/// Pattern was not parsed, so may be valid or invalid.
Expand Down Expand Up @@ -224,6 +222,6 @@ bitflags! {
/// Dummy type to communicate the content of `RegExpFlags` to `oxc_ast_tools`.
#[ast(foreign = RegExpFlags)]
#[generate_derive(ESTree)]
#[estree(custom_serialize, no_ts_def)]
#[estree(custom_serialize, ts_alias = "string")]
#[expect(dead_code)]
struct RegExpFlagsAlias(u8);
1 change: 1 addition & 0 deletions tasks/ast_tools/src/derives/estree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ fn parse_estree_attr(location: AttrLocation, part: AttrPart) -> Result<()> {
AttrPart::Tag("custom_serialize") => enum_def.estree.custom_serialize = true,
AttrPart::Tag("no_ts_def") => enum_def.estree.custom_ts_def = Some(String::new()),
AttrPart::String("custom_ts_def", value) => enum_def.estree.custom_ts_def = Some(value),
AttrPart::String("ts_alias", value) => enum_def.estree.ts_alias = Some(value),
AttrPart::String("add_ts_def", value) => {
enum_def.estree.add_ts_def = Some(value);
}
Expand Down
35 changes: 21 additions & 14 deletions tasks/ast_tools/src/generators/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,15 @@ fn generate_ts_type_def(type_def: &TypeDef, code: &mut String, schema: &Schema)
}
} else {
// No custom definition. Generate one.
match type_def {
TypeDef::Struct(struct_def) => {
let ts_def = generate_ts_type_def_for_struct(struct_def, schema);
if let Some(ts_def) = ts_def {
write!(code, "{ts_def};\n\n").unwrap();
}
}
TypeDef::Enum(enum_def) => {
let ts_def = generate_ts_type_def_for_enum(enum_def, schema);
write!(code, "{ts_def};\n\n").unwrap();
}
let ts_def = match type_def {
TypeDef::Struct(struct_def) => generate_ts_type_def_for_struct(struct_def, schema),
TypeDef::Enum(enum_def) => generate_ts_type_def_for_enum(enum_def, schema),
_ => unreachable!(),
};

if let Some(ts_def) = ts_def {
write!(code, "{ts_def};\n\n").unwrap();
}
};

// Add additional custom TS def if provided via `#[estree(add_ts_def = "...")]` attribute
Expand Down Expand Up @@ -236,7 +232,12 @@ fn generate_ts_type_def_for_struct_field<'s>(
}

/// Generate Typescript type definition for an enum.
fn generate_ts_type_def_for_enum(enum_def: &EnumDef, schema: &Schema) -> String {
fn generate_ts_type_def_for_enum(enum_def: &EnumDef, schema: &Schema) -> Option<String> {
// If enum marked with `#[estree(ts_alias = "...")]`, then it needs no type def
if enum_def.estree.ts_alias.is_some() {
return None;
}

let own_variants_type_names = enum_def.variants.iter().map(|variant| {
if let Some(variant_type) = variant.field_type(schema) {
ts_type_name(variant_type, schema)
Expand All @@ -251,7 +252,7 @@ fn generate_ts_type_def_for_enum(enum_def: &EnumDef, schema: &Schema) -> String
let union = own_variants_type_names.chain(inherits_type_names).join(" | ");

let enum_name = enum_def.name();
format!("export type {enum_name} = {union};")
Some(format!("export type {enum_name} = {union};"))
}

/// Get TS type name for a type.
Expand All @@ -264,7 +265,13 @@ fn ts_type_name<'s>(type_def: &'s TypeDef, schema: &'s Schema) -> Cow<'s, str> {
Cow::Borrowed(struct_def.name())
}
}
TypeDef::Enum(enum_def) => Cow::Borrowed(enum_def.name()),
TypeDef::Enum(enum_def) => {
if let Some(ts_alias) = &enum_def.estree.ts_alias {
Cow::Borrowed(ts_alias)
} else {
Cow::Borrowed(enum_def.name())
}
}
TypeDef::Primitive(primitive_def) => Cow::Borrowed(match primitive_def.name() {
#[rustfmt::skip]
"u8" | "u16" | "u32" | "u64" | "u128" | "usize"
Expand Down
10 changes: 7 additions & 3 deletions tasks/ast_tools/src/schema/extensions/estree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ pub struct ESTreeStruct {
pub add_fields: Vec<(String, String)>,
/// Additional fields to add to TS type definition
pub add_ts: Option<String>,
/// Custom TS type definition. Does not include `export`.
/// Empty string if type should not have a TS type definition.
pub custom_ts_def: Option<String>,
/// TS alias.
/// e.g. `#[estree(ts_alias = "null")]` means this type won't have a type def generated,
/// and any struct / enum referencing it will substitute `null` as the type.
pub ts_alias: Option<String>,
/// Custom TS type definition. Does not include `export`.
/// Empty string if type should not have a TS type definition.
pub custom_ts_def: Option<String>,
/// Additional custom TS type definition to add along with the generated one.
/// Does not include `export`.
pub add_ts_def: Option<String>,
Expand All @@ -32,6 +32,10 @@ pub struct ESTreeEnum {
pub no_rename_variants: bool,
/// `true` if serializer is implemented manually and should not be generated
pub custom_serialize: bool,
/// TS alias.
/// e.g. `#[estree(ts_alias = "null")]` means this type won't have a type def generated,
/// and any struct / enum referencing it will substitute `null` as the type.
pub ts_alias: Option<String>,
/// Custom TS type definition. Does not include `export`.
/// Empty string if type should not have a TS type definition.
pub custom_ts_def: Option<String>,
Expand Down

0 comments on commit a1ca2eb

Please sign in to comment.