Skip to content

Commit

Permalink
refactor(ast): override TS type defs with #[estree(custom_ts_def)]
Browse files Browse the repository at this point in the history
…attribute on type
  • Loading branch information
overlookmotel committed Feb 4, 2025
1 parent 7c8743e commit 5046b09
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 99 deletions.
32 changes: 0 additions & 32 deletions crates/oxc_ast/custom_types.d.ts

This file was deleted.

14 changes: 12 additions & 2 deletions crates/oxc_ast/src/ast/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ inherit_variants! {
#[ast(visit)]
#[derive(Debug)]
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
#[estree(custom_ts_def)]
#[estree(no_ts_def)]
pub enum ArrayExpressionElement<'a> {
/// `...[3, 4]` in `const array = [1, 2, ...[3, 4], null];`
SpreadElement(Box<'a, SpreadElement<'a>>) = 64,
Expand Down Expand Up @@ -1626,7 +1626,17 @@ pub enum FunctionType {
#[ast(visit)]
#[derive(Debug)]
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
#[estree(custom_serialize)]
#[estree(
custom_serialize,
add_ts_def = "
interface FormalParameterRest extends Span {
type: 'RestElement';
argument: BindingPatternKind;
typeAnnotation: TSTypeAnnotation | null;
optional: boolean;
}
"
)]
pub struct FormalParameters<'a> {
pub span: Span,
pub kind: FormalParameterKind,
Expand Down
12 changes: 10 additions & 2 deletions crates/oxc_ast/src/ast/jsx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,11 @@ pub struct JSXClosingFragment {
/// JSX Element Name
#[ast(visit)]
#[derive(Debug)]
#[generate_derive(CloneIn, GetSpan, GetSpanMut, GetAddress, ContentEq)]
#[generate_derive(CloneIn, GetSpan, GetSpanMut, GetAddress, ContentEq, ESTree)]
#[estree(
custom_serialize,
custom_ts_def = "type JSXElementName = JSXIdentifier | JSXNamespacedName | JSXMemberExpression"
)]
pub enum JSXElementName<'a> {
/// `<div />`
Identifier(Box<'a, JSXIdentifier<'a>>) = 0,
Expand Down Expand Up @@ -224,7 +228,11 @@ pub struct JSXMemberExpression<'a> {
/// ```
#[ast(visit)]
#[derive(Debug)]
#[generate_derive(CloneIn, GetSpan, GetSpanMut, GetAddress, ContentEq)]
#[generate_derive(CloneIn, GetSpan, GetSpanMut, GetAddress, ContentEq, ESTree)]
#[estree(
custom_serialize,
custom_ts_def = "type JSXMemberExpressionObject = JSXIdentifier | JSXMemberExpression"
)]
pub enum JSXMemberExpressionObject<'a> {
/// `<Apple.Orange />`
IdentifierReference(Box<'a, IdentifierReference<'a>>) = 0,
Expand Down
24 changes: 24 additions & 0 deletions crates/oxc_ast/src/ast/literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,5 +203,29 @@ bitflags! {

/// Dummy type to communicate the content of `RegExpFlags` to `oxc_ast_tools`.
#[ast(foreign = RegExpFlags)]
#[generate_derive(ESTree)]
#[estree(
custom_serialize,
custom_ts_def = "
type RegExpFlags = {
/** Global flag */
G: 1;
/** Ignore case flag */
I: 2;
/** Multiline flag */
M: 4;
/** DotAll flag */
S: 8;
/** Unicode flag */
U: 16;
/** Sticky flag */
Y: 32;
/** Indices flag */
D: 64;
/** Unicode sets flag */
V: 128;
}
"
)]
#[expect(dead_code)]
struct RegExpFlagsAlias(u8);
63 changes: 30 additions & 33 deletions npm/oxc-types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,13 @@ export interface FormalParameters extends Span {
items: Array<FormalParameter | FormalParameterRest>;
}

export interface FormalParameterRest extends Span {
type: 'RestElement';
argument: BindingPatternKind;
typeAnnotation: TSTypeAnnotation | null;
optional: boolean;
}

export interface FormalParameter extends Span {
type: 'FormalParameter';
decorators: Array<Decorator>;
Expand Down Expand Up @@ -1032,6 +1039,25 @@ export interface RegExp {

export type RegExpPattern = string | string | Pattern;

export type RegExpFlags = {
/** Global flag */
G: 1;
/** Ignore case flag */
I: 2;
/** Multiline flag */
M: 4;
/** DotAll flag */
S: 8;
/** Unicode flag */
U: 16;
/** Sticky flag */
Y: 32;
/** Indices flag */
D: 64;
/** Unicode sets flag */
V: 128;
};

export interface JSXElement extends Span {
type: 'JSXElement';
openingElement: JSXOpeningElement;
Expand Down Expand Up @@ -1067,6 +1093,8 @@ export interface JSXClosingFragment extends Span {
type: 'JSXClosingFragment';
}

export type JSXElementName = JSXIdentifier | JSXNamespacedName | JSXMemberExpression;

export interface JSXNamespacedName extends Span {
type: 'JSXNamespacedName';
namespace: JSXIdentifier;
Expand All @@ -1079,6 +1107,8 @@ export interface JSXMemberExpression extends Span {
property: JSXIdentifier;
}

export type JSXMemberExpressionObject = JSXIdentifier | JSXMemberExpression;

export interface JSXExpressionContainer extends Span {
type: 'JSXExpressionContainer';
expression: JSXExpression;
Expand Down Expand Up @@ -1941,36 +1971,3 @@ export interface NamedReference extends Span {
type: 'NamedReference';
name: string;
}

export interface FormalParameterRest extends Span {
type: 'RestElement';
argument: BindingPatternKind;
typeAnnotation: TSTypeAnnotation | null;
optional: boolean;
}

export type RegExpFlags = {
/** Global flag */
G: 1;
/** Ignore case flag */
I: 2;
/** Multiline flag */
M: 4;
/** DotAll flag */
S: 8;
/** Unicode flag */
U: 16;
/** Sticky flag */
Y: 32;
/** Indices flag */
D: 64;
/** Unicode sets flag */
V: 128;
};

export type JSXElementName =
| JSXIdentifier
| JSXNamespacedName
| JSXMemberExpression;

export type JSXMemberExpressionObject = JSXIdentifier | JSXMemberExpression;
23 changes: 20 additions & 3 deletions tasks/ast_tools/src/derives/estree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,12 @@ impl Derive for DeriveESTree {
}
generate_body_for_struct(struct_def, schema)
}
StructOrEnum::Enum(enum_def) => generate_body_for_enum(enum_def, schema),
StructOrEnum::Enum(enum_def) => {
if enum_def.estree.custom_serialize {
return quote!();
}
generate_body_for_enum(enum_def, schema)
}
};

let ty = type_def.ty_anon(schema);
Expand All @@ -92,16 +97,28 @@ fn parse_estree_attr(location: AttrLocation, part: AttrPart) -> Result<()> {
AttrPart::Tag("flatten") => struct_def.estree.flatten = true,
AttrPart::Tag("no_type") => struct_def.estree.no_type = true,
AttrPart::Tag("custom_serialize") => struct_def.estree.custom_serialize = true,
AttrPart::Tag("no_ts_def") => struct_def.estree.custom_ts_def = Some(String::new()),
AttrPart::String("add_ts", value) => struct_def.estree.add_ts = Some(value),
AttrPart::String("custom_ts_def", value) => {
struct_def.estree.custom_ts_def = Some(value);
}
AttrPart::String("add_ts_def", value) => {
struct_def.estree.add_ts_def = Some(value);
}
AttrPart::String("rename", value) => struct_def.estree.rename = Some(value),
AttrPart::String("via", value) => struct_def.estree.via = Some(value),
AttrPart::String("add_ts", value) => struct_def.estree.add_ts = Some(value),
_ => return Err(()),
},
// `#[estree]` attr on enum
AttrLocation::Enum(enum_def) => match part {
AttrPart::Tag("skip") => enum_def.estree.skip = true,
AttrPart::Tag("no_rename_variants") => enum_def.estree.no_rename_variants = true,
AttrPart::Tag("custom_ts_def") => enum_def.estree.custom_ts_def = true,
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("add_ts_def", value) => {
enum_def.estree.add_ts_def = Some(value);
}
_ => return Err(()),
},
// `#[estree]` attr on struct field
Expand Down
66 changes: 41 additions & 25 deletions tasks/ast_tools/src/generators/typescript.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Generator for TypeScript type definitions for all AST types.
use std::borrow::Cow;
use std::{borrow::Cow, fmt::Write};

use itertools::Itertools;

Expand All @@ -16,8 +16,6 @@ use crate::{

use super::{attr_positions, define_generator, AttrLocation, AttrPart, AttrPositions};

const CUSTOM_TYPESCRIPT: &str = include_str!("../../../../crates/oxc_ast/custom_types.d.ts");

/// Generator for TypeScript type definitions.
pub struct TypescriptGenerator;

Expand Down Expand Up @@ -56,27 +54,49 @@ impl Generator for TypescriptGenerator {

let mut code = String::new();
for type_def in &schema.types {
if !type_def.generates_derive(estree_derive_id) {
continue;
if type_def.generates_derive(estree_derive_id) {
generate_ts_type_def(type_def, &mut code, schema);
}
}

let ts_type_def = match type_def {
TypeDef::Struct(struct_def) => generate_ts_type_def_for_struct(struct_def, schema),
TypeDef::Enum(enum_def) => {
let ts_type_def = generate_ts_type_def_for_enum(enum_def, schema);
let Some(ts_type_def) = ts_type_def else { continue };
ts_type_def
}
_ => unreachable!(),
};
Output::Javascript { path: TYPESCRIPT_DEFINITIONS_PATH.to_string(), code }
}
}

code.push_str(&ts_type_def);
code.push_str("\n\n");
}
/// Generate Typescript type definition for a struct or enum.
///
/// Push type defs to `code`.
fn generate_ts_type_def(type_def: &TypeDef, code: &mut String, schema: &Schema) {
// Use custom TS def if provided via `#[estree(custom_ts_def = "...")]` attribute
let custom_ts_def = match type_def {
TypeDef::Struct(struct_def) => &struct_def.estree.custom_ts_def,
TypeDef::Enum(enum_def) => &enum_def.estree.custom_ts_def,
_ => unreachable!(),
};

code.push_str(CUSTOM_TYPESCRIPT);
if let Some(custom_ts_def) = custom_ts_def {
// Empty string means don't output any TS def at all for this type
if !custom_ts_def.is_empty() {
write!(code, "export {custom_ts_def};\n\n").unwrap();
}
} else {
// No custom definition. Generate one.
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!(),
};
write!(code, "{ts_def};\n\n").unwrap();
};

Output::Javascript { path: TYPESCRIPT_DEFINITIONS_PATH.to_string(), code }
// Add additional custom TS def if provided via `#[estree(add_ts_def = "...")]` attribute
let add_ts_def = match type_def {
TypeDef::Struct(struct_def) => &struct_def.estree.add_ts_def,
TypeDef::Enum(enum_def) => &enum_def.estree.add_ts_def,
_ => unreachable!(),
};
if let Some(add_ts_def) = add_ts_def {
write!(code, "export {add_ts_def};\n\n").unwrap();
}
}

Expand Down Expand Up @@ -163,11 +183,7 @@ fn generate_ts_type_def_for_struct(struct_def: &StructDef, schema: &Schema) -> S
}

/// Generate Typescript type definition for an enum.
fn generate_ts_type_def_for_enum(enum_def: &EnumDef, schema: &Schema) -> Option<String> {
if enum_def.estree.custom_ts_def {
return None;
}

fn generate_ts_type_def_for_enum(enum_def: &EnumDef, schema: &Schema) -> String {
let union = if enum_def.is_fieldless() {
enum_def
.all_variants(schema)
Expand All @@ -181,7 +197,7 @@ fn generate_ts_type_def_for_enum(enum_def: &EnumDef, schema: &Schema) -> Option<
};

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

/// Get TS type name for a type.
Expand Down
19 changes: 17 additions & 2 deletions tasks/ast_tools/src/schema/extensions/estree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,34 @@
pub struct ESTreeStruct {
pub rename: Option<String>,
pub via: Option<String>,
pub add_ts: Option<String>,
pub skip: bool,
pub flatten: bool,
pub no_type: bool,
/// `true` if serializer is implemented manually and should not be generated
pub custom_serialize: bool,
/// 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>,
/// Additional custom TS type definition to add along with the generated one.
/// Does not include `export`.
pub add_ts_def: Option<String>,
}

/// Configuration for ESTree generator on an enum.
#[derive(Default, Debug)]
pub struct ESTreeEnum {
pub skip: bool,
pub no_rename_variants: bool,
pub custom_ts_def: bool,
/// `true` if serializer is implemented manually and should not be generated
pub custom_serialize: bool,
/// 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>,
}

/// Configuration for ESTree generator on a struct field.
Expand Down

0 comments on commit 5046b09

Please sign in to comment.