Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions crates/oxc_ast/src/ast/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,6 @@ pub use match_assignment_target_pattern;
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ContentHash, ESTree)]
pub struct ArrayAssignmentTarget<'a> {
pub span: Span,
#[estree(type = "Array<AssignmentTargetMaybeDefault | AssignmentTargetRest | null>")]
pub elements: Vec<'a, Option<AssignmentTargetMaybeDefault<'a>>>,
#[estree(append_to = "elements")]
pub rest: Option<AssignmentTargetRest<'a>>,
Expand All @@ -823,7 +822,6 @@ pub struct ArrayAssignmentTarget<'a> {
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ContentHash, ESTree)]
pub struct ObjectAssignmentTarget<'a> {
pub span: Span,
#[estree(type = "Array<AssignmentTargetProperty | AssignmentTargetRest>")]
pub properties: Vec<'a, AssignmentTargetProperty<'a>>,
#[estree(append_to = "properties")]
pub rest: Option<AssignmentTargetRest<'a>>,
Expand Down Expand Up @@ -1482,7 +1480,6 @@ pub struct AssignmentPattern<'a> {
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ContentHash, ESTree)]
pub struct ObjectPattern<'a> {
pub span: Span,
#[estree(type = "Array<BindingProperty | BindingRestElement>")]
pub properties: Vec<'a, BindingProperty<'a>>,
#[estree(append_to = "properties")]
pub rest: Option<Box<'a, BindingRestElement<'a>>>,
Expand All @@ -1505,7 +1502,6 @@ pub struct BindingProperty<'a> {
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ContentHash, ESTree)]
pub struct ArrayPattern<'a> {
pub span: Span,
#[estree(type = "Array<BindingPattern | BindingRestElement | null>")]
pub elements: Vec<'a, Option<BindingPattern<'a>>>,
#[estree(append_to = "elements")]
pub rest: Option<Box<'a, BindingRestElement<'a>>>,
Expand Down
20 changes: 16 additions & 4 deletions crates/oxc_ast/src/generated/derive_estree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,10 @@ impl<'a> Serialize for ArrayAssignmentTarget<'a> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "ArrayAssignmentTarget")?;
self.span.serialize(serde::__private::ser::FlatMapSerializer(&mut map))?;
map.serialize_entry("elements", &oxc_estree::AppendTo(&self.elements, &self.rest))?;
map.serialize_entry(
"elements",
&oxc_estree::ser::AppendTo { array: &self.elements, after: &self.rest },
)?;
map.end()
}
}
Expand All @@ -694,7 +697,10 @@ impl<'a> Serialize for ObjectAssignmentTarget<'a> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "ObjectAssignmentTarget")?;
self.span.serialize(serde::__private::ser::FlatMapSerializer(&mut map))?;
map.serialize_entry("properties", &oxc_estree::AppendTo(&self.properties, &self.rest))?;
map.serialize_entry(
"properties",
&oxc_estree::ser::AppendTo { array: &self.properties, after: &self.rest },
)?;
map.end()
}
}
Expand Down Expand Up @@ -1308,7 +1314,10 @@ impl<'a> Serialize for ObjectPattern<'a> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "ObjectPattern")?;
self.span.serialize(serde::__private::ser::FlatMapSerializer(&mut map))?;
map.serialize_entry("properties", &oxc_estree::AppendTo(&self.properties, &self.rest))?;
map.serialize_entry(
"properties",
&oxc_estree::ser::AppendTo { array: &self.properties, after: &self.rest },
)?;
map.end()
}
}
Expand All @@ -1331,7 +1340,10 @@ impl<'a> Serialize for ArrayPattern<'a> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "ArrayPattern")?;
self.span.serialize(serde::__private::ser::FlatMapSerializer(&mut map))?;
map.serialize_entry("elements", &oxc_estree::AppendTo(&self.elements, &self.rest))?;
map.serialize_entry(
"elements",
&oxc_estree::ser::AppendTo { array: &self.elements, after: &self.rest },
)?;
map.end()
}
}
Expand Down
21 changes: 1 addition & 20 deletions crates/oxc_estree/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,6 @@
#[cfg(feature = "serialize")]
use serde::ser::{Serialize, SerializeSeq, Serializer};
pub mod ser;

/// Empty trait that will be used later for custom serialization and TypeScript
/// generation for AST nodes.
pub trait ESTree {}

#[cfg(feature = "serialize")]
pub struct AppendTo<'a, TVec, TChild>(pub &'a [TVec], pub &'a Option<TChild>);

#[cfg(feature = "serialize")]
impl<'b, TVec: Serialize, TChild: Serialize> Serialize for AppendTo<'b, TVec, TChild> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
if let Some(child) = self.1 {
let mut seq = serializer.serialize_seq(Some(self.0.len() + 1))?;
for element in self.0 {
seq.serialize_element(element)?;
}
seq.serialize_element(child)?;
seq.end()
} else {
self.0.serialize(serializer)
}
}
}
23 changes: 23 additions & 0 deletions crates/oxc_estree/src/ser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use serde::ser::{Serialize, SerializeSeq, Serializer};

/// A helper struct for serializing a sequence followed by an optional element.
/// This is only used by generated ESTree serialization code.
pub struct AppendTo<'a, TVec, TAfter> {
pub array: &'a [TVec],
pub after: &'a Option<TAfter>,
}

impl<'b, TVec: Serialize, TAfter: Serialize> Serialize for AppendTo<'b, TVec, TAfter> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
if let Some(after) = self.after {
let mut seq = serializer.serialize_seq(Some(self.array.len() + 1))?;
for element in self.array {
seq.serialize_element(element)?;
}
seq.serialize_element(after)?;
seq.end()
} else {
self.array.serialize(serializer)
}
}
}
12 changes: 4 additions & 8 deletions npm/oxc-types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,14 +386,12 @@ export type AssignmentTargetPattern = ArrayAssignmentTarget | ObjectAssignmentTa

export interface ArrayAssignmentTarget extends Span {
type: 'ArrayAssignmentTarget';
elements: Array<AssignmentTargetMaybeDefault | AssignmentTargetRest | null>;
rest: AssignmentTargetRest | null;
elements: Array<AssignmentTargetRest | AssignmentTargetMaybeDefault | null>;
}

export interface ObjectAssignmentTarget extends Span {
type: 'ObjectAssignmentTarget';
properties: Array<AssignmentTargetProperty | AssignmentTargetRest>;
rest: AssignmentTargetRest | null;
properties: Array<AssignmentTargetRest | AssignmentTargetProperty>;
}

export interface AssignmentTargetRest extends Span {
Expand Down Expand Up @@ -730,8 +728,7 @@ export interface AssignmentPattern extends Span {

export interface ObjectPattern extends Span {
type: 'ObjectPattern';
properties: Array<BindingProperty | BindingRestElement>;
rest: BindingRestElement | null;
properties: Array<BindingRestElement | BindingProperty>;
}

export interface BindingProperty extends Span {
Expand All @@ -744,8 +741,7 @@ export interface BindingProperty extends Span {

export interface ArrayPattern extends Span {
type: 'ArrayPattern';
elements: Array<BindingPattern | BindingRestElement | null>;
rest: BindingRestElement | null;
elements: Array<BindingRestElement | BindingPattern | null>;
}

export interface BindingRestElement extends Span {
Expand Down
13 changes: 8 additions & 5 deletions tasks/ast_tools/src/derives/estree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,24 +118,27 @@ fn serialize_struct(def: &StructDef, schema: &Schema) -> TokenStream {
None => false,
};

let append_child = append_to.get(&ident.to_string());
let append_after = append_to.get(&ident.to_string());

if always_flatten || field.markers.derive_attributes.estree.flatten {
assert!(
append_child.is_none(),
append_after.is_none(),
"Cannot flatten and append to the same field (on {ident})"
);
fields.push(quote! {
self.#ident.serialize(
serde::__private::ser::FlatMapSerializer(&mut map)
)?;
});
} else if let Some(append_child) = append_child {
let child_ident = append_child.ident().unwrap();
} else if let Some(append_after) = append_after {
let after_ident = append_after.ident().unwrap();
fields.push(quote! {
map.serialize_entry(
#name,
&oxc_estree::AppendTo(&self.#ident, &self.#child_ident)
&oxc_estree::ser::AppendTo {
array: &self.#ident,
after: &self.#after_ident
}
)?;
});
} else {
Expand Down
43 changes: 39 additions & 4 deletions tasks/ast_tools/src/generators/typescript.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use convert_case::{Case, Casing};
use itertools::Itertools;
use rustc_hash::FxHashSet;
use rustc_hash::{FxHashMap, FxHashSet};

use crate::{
output::Output,
schema::{
serialize::{enum_variant_name, get_always_flatten_structs, get_type_tag},
EnumDef, GetIdent, Schema, StructDef, TypeDef, TypeName,
EnumDef, FieldDef, GetIdent, Schema, StructDef, TypeDef, TypeName,
},
Generator, TypeId,
};
Expand Down Expand Up @@ -72,11 +72,26 @@ fn typescript_struct(def: &StructDef, always_flatten_structs: &FxHashSet<TypeId>
fields.push_str(&format!("\n\ttype: '{type_tag}';"));
}

let mut append_to: FxHashMap<String, &FieldDef> = FxHashMap::default();

// Scan through to find all append_to fields
for field in &def.fields {
let Some(parent) = field.markers.derive_attributes.estree.append_to.as_ref() else {
continue;
};
assert!(
append_to.insert(parent.clone(), field).is_none(),
"Duplicate append_to target (on {ident})"
);
}

for field in &def.fields {
if field.markers.derive_attributes.estree.skip {
if field.markers.derive_attributes.estree.skip
|| field.markers.derive_attributes.estree.append_to.is_some()
{
continue;
}
let ty = match &field.markers.derive_attributes.estree.typescript_type {
let mut ty = match &field.markers.derive_attributes.estree.typescript_type {
Some(ty) => ty.clone(),
None => type_to_string(field.typ.name()),
};
Expand All @@ -91,6 +106,26 @@ fn typescript_struct(def: &StructDef, always_flatten_structs: &FxHashSet<TypeId>
continue;
}

let ident = field.ident().unwrap();
if let Some(append_after) = append_to.get(&ident.to_string()) {
let after_type = match &append_after.markers.derive_attributes.estree.typescript_type {
Some(ty) => ty.clone(),
None => {
let typ = append_after.typ.name();
if let TypeName::Opt(inner) = typ {
type_to_string(inner)
} else {
panic!("expected field labeled with append_to to be Option<...>, but found {typ}");
}
}
};
if let Some(inner) = ty.strip_prefix("Array<") {
ty = format!("Array<{after_type} | {inner}");
} else {
panic!("expected append_to target to be a Vec, but found {ty}");
}
}

let name = match &field.markers.derive_attributes.estree.rename {
Some(rename) => rename.to_string(),
None => field.name.clone().unwrap().to_case(Case::Camel),
Expand Down