diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index 7bea509ae..215c95a21 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -21,6 +21,14 @@ export interface BindingIdentifier extends Span { type: "Identifier", name: Atom export interface IdentifierReference extends Span { type: "Identifier", name: Atom } export interface IdentifierName extends Span { type: "Identifier", name: Atom } export interface LabelIdentifier extends Span { type: "Identifier", name: Atom } +export interface AssignmentTargetRest extends Span { type: "RestElement", target: AssignmentTarget } +export interface BindingRestElement extends Span { type: "RestElement", argument: BindingPattern } +export interface FormalParameterRest extends Span { + type: "RestElement", + argument: BindingPatternKind, + typeAnnotation?: TSTypeAnnotation, + optional: boolean, +} "#; #[derive(Debug, Hash)] @@ -970,13 +978,18 @@ pub enum AssignmentTargetPattern<'a> { ObjectAssignmentTarget(Box<'a, ObjectAssignmentTarget<'a>>), } +// See serializer in serialize.rs #[derive(Debug, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type", rename_all = "camelCase"))] #[cfg_attr(feature = "wasm", derive(tsify::Tsify))] pub struct ArrayAssignmentTarget<'a> { - #[cfg_attr(feature = "serde", serde(flatten))] + #[cfg_attr(feature = "wasm", serde(flatten))] pub span: Span, + #[cfg_attr( + feature = "wasm", + tsify(type = "Array") + )] pub elements: Vec<'a, Option>>, + #[cfg_attr(feature = "wasm", serde(skip))] pub rest: Option>, pub trailing_comma: Option, } @@ -990,13 +1003,18 @@ impl<'a> ArrayAssignmentTarget<'a> { } } +// See serializer in serialize.rs #[derive(Debug, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))] #[cfg_attr(feature = "wasm", derive(tsify::Tsify))] pub struct ObjectAssignmentTarget<'a> { - #[cfg_attr(feature = "serde", serde(flatten))] + #[cfg_attr(feature = "wasm", serde(flatten))] pub span: Span, + #[cfg_attr( + feature = "wasm", + tsify(type = "Array") + )] pub properties: Vec<'a, AssignmentTargetProperty<'a>>, + #[cfg_attr(feature = "wasm", serde(skip))] pub rest: Option>, } @@ -1018,11 +1036,11 @@ impl<'a> ObjectAssignmentTarget<'a> { } #[derive(Debug, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))] -#[cfg_attr(feature = "wasm", derive(tsify::Tsify))] +#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type", rename = "RestElement"))] pub struct AssignmentTargetRest<'a> { #[cfg_attr(feature = "serde", serde(flatten))] pub span: Span, + #[cfg_attr(feature = "serde", serde(rename = "argument"))] pub target: AssignmentTarget<'a>, } @@ -1623,6 +1641,7 @@ impl<'a> BindingPattern<'a> { #[derive(Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize), serde(untagged))] +#[cfg_attr(feature = "wasm", derive(tsify::Tsify))] pub enum BindingPatternKind<'a> { /// `const a = 1` BindingIdentifier(Box<'a, BindingIdentifier<'a>>), @@ -1661,13 +1680,15 @@ pub struct AssignmentPattern<'a> { pub right: Expression<'a>, } +// See serializer in serialize.rs #[derive(Debug, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))] #[cfg_attr(feature = "wasm", derive(tsify::Tsify))] pub struct ObjectPattern<'a> { - #[cfg_attr(feature = "serde", serde(flatten))] + #[cfg_attr(feature = "wasm", serde(flatten))] pub span: Span, + #[cfg_attr(feature = "wasm", tsify(type = "Array"))] pub properties: Vec<'a, BindingProperty<'a>>, + #[cfg_attr(feature = "wasm", serde(skip))] pub rest: Option>>, } @@ -1693,13 +1714,18 @@ pub struct BindingProperty<'a> { pub computed: bool, } +// See serializer in serialize.rs #[derive(Debug, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))] #[cfg_attr(feature = "wasm", derive(tsify::Tsify))] pub struct ArrayPattern<'a> { - #[cfg_attr(feature = "serde", serde(flatten))] + #[cfg_attr(feature = "wasm", serde(flatten))] pub span: Span, + #[cfg_attr( + feature = "wasm", + tsify(type = "Array") + )] pub elements: Vec<'a, Option>>, + #[cfg_attr(feature = "wasm", serde(skip))] pub rest: Option>>, } @@ -1714,8 +1740,7 @@ impl<'a> ArrayPattern<'a> { } #[derive(Debug, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))] -#[cfg_attr(feature = "wasm", derive(tsify::Tsify))] +#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type", rename = "RestElement"))] pub struct BindingRestElement<'a> { #[cfg_attr(feature = "serde", serde(flatten))] pub span: Span, @@ -1799,14 +1824,16 @@ pub enum FunctionType { } /// +// See serializer in serialize.rs #[derive(Debug, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))] #[cfg_attr(feature = "wasm", derive(tsify::Tsify))] pub struct FormalParameters<'a> { - #[cfg_attr(feature = "serde", serde(flatten))] + #[cfg_attr(feature = "wasm", serde(flatten))] pub span: Span, pub kind: FormalParameterKind, + #[cfg_attr(feature = "wasm", tsify(type = "Array"))] pub items: Vec<'a, FormalParameter<'a>>, + #[cfg_attr(feature = "wasm", serde(skip))] pub rest: Option>>, } diff --git a/crates/oxc_ast/src/serialize.rs b/crates/oxc_ast/src/serialize.rs index 3af0a3e31..3c5dfd672 100644 --- a/crates/oxc_ast/src/serialize.rs +++ b/crates/oxc_ast/src/serialize.rs @@ -1,11 +1,16 @@ use serde::{ - ser::{SerializeStruct, Serializer}, + ser::{SerializeSeq, SerializeStruct, Serializer}, Serialize, }; use crate::ast::{ - BindingIdentifier, IdentifierName, IdentifierReference, LabelIdentifier, Program, RegExpFlags, + ArrayAssignmentTarget, ArrayPattern, AssignmentTargetMaybeDefault, AssignmentTargetProperty, + AssignmentTargetRest, BindingIdentifier, BindingPattern, BindingPatternKind, BindingProperty, + BindingRestElement, FormalParameter, FormalParameterKind, FormalParameters, IdentifierName, + IdentifierReference, LabelIdentifier, ObjectAssignmentTarget, ObjectPattern, Program, + RegExpFlags, TSTypeAnnotation, }; +use oxc_allocator::{Box, Vec}; use oxc_span::{Atom, Span}; pub struct EcmaFormatter; @@ -92,3 +97,143 @@ impl<'a> Serialize for LabelIdentifier<'a> { serialize_identifier(serializer, "LabelIdentifier", self.span, &self.name) } } + +/// Serialize `ArrayAssignmentTarget`, `ObjectAssignmentTarget`, `ObjectPattern`, `ArrayPattern` +/// to be estree compatible, with `elements`/`properties` and `rest` fields combined. + +impl<'a> Serialize for ArrayAssignmentTarget<'a> { + fn serialize(&self, serializer: S) -> Result { + let converted = SerArrayAssignmentTarget { + span: self.span, + elements: ElementsAndRest::new(&self.elements, &self.rest), + trailing_comma: self.trailing_comma, + }; + converted.serialize(serializer) + } +} + +#[derive(Serialize)] +#[serde(tag = "type", rename = "ArrayAssignmentTarget", rename_all = "camelCase")] +struct SerArrayAssignmentTarget<'a, 'b> { + #[serde(flatten)] + span: Span, + elements: + ElementsAndRest<'a, 'b, Option>, AssignmentTargetRest<'a>>, + trailing_comma: Option, +} + +impl<'a> Serialize for ObjectAssignmentTarget<'a> { + fn serialize(&self, serializer: S) -> Result { + let converted = SerObjectAssignmentTarget { + span: self.span, + properties: ElementsAndRest::new(&self.properties, &self.rest), + }; + converted.serialize(serializer) + } +} + +#[derive(Serialize)] +#[serde(tag = "type", rename = "ObjectAssignmentTarget")] +struct SerObjectAssignmentTarget<'a, 'b> { + #[serde(flatten)] + span: Span, + properties: ElementsAndRest<'a, 'b, AssignmentTargetProperty<'a>, AssignmentTargetRest<'a>>, +} + +impl<'a> Serialize for ObjectPattern<'a> { + fn serialize(&self, serializer: S) -> Result { + let converted = SerObjectPattern { + span: self.span, + properties: ElementsAndRest::new(&self.properties, &self.rest), + }; + converted.serialize(serializer) + } +} + +#[derive(Serialize)] +#[serde(tag = "type", rename = "ObjectPattern")] +struct SerObjectPattern<'a, 'b> { + #[serde(flatten)] + span: Span, + properties: ElementsAndRest<'a, 'b, BindingProperty<'a>, Box<'a, BindingRestElement<'a>>>, +} + +impl<'a> Serialize for ArrayPattern<'a> { + fn serialize(&self, serializer: S) -> Result { + let converted = SerArrayPattern { + span: self.span, + elements: ElementsAndRest::new(&self.elements, &self.rest), + }; + converted.serialize(serializer) + } +} + +#[derive(Serialize)] +#[serde(tag = "type", rename = "ArrayPattern")] +struct SerArrayPattern<'a, 'b> { + #[serde(flatten)] + span: Span, + elements: ElementsAndRest<'a, 'b, Option>, Box<'a, BindingRestElement<'a>>>, +} + +/// Serialize `FormalParameters`, to be estree compatible, with `items` and `rest` fields combined +/// and `argument` field flattened. +impl<'a> Serialize for FormalParameters<'a> { + fn serialize(&self, serializer: S) -> Result { + let converted_rest = self.rest.as_ref().map(|rest| SerFormalParameterRest { + span: rest.span, + argument: &rest.argument.kind, + type_annotation: &rest.argument.type_annotation, + optional: rest.argument.optional, + }); + let converted = SerFormalParameters { + span: self.span, + kind: self.kind, + items: ElementsAndRest::new(&self.items, &converted_rest), + }; + converted.serialize(serializer) + } +} + +#[derive(Serialize)] +#[serde(tag = "type", rename = "FormalParameters")] +struct SerFormalParameters<'a, 'b> { + #[serde(flatten)] + span: Span, + kind: FormalParameterKind, + items: ElementsAndRest<'a, 'b, FormalParameter<'a>, SerFormalParameterRest<'a, 'b>>, +} + +#[derive(Serialize)] +#[serde(tag = "type", rename = "RestElement", rename_all = "camelCase")] +struct SerFormalParameterRest<'a, 'b> { + #[serde(flatten)] + span: Span, + argument: &'b BindingPatternKind<'a>, + type_annotation: &'b Option>>, + optional: bool, +} + +pub struct ElementsAndRest<'a, 'b, E, R> { + elements: &'b Vec<'a, E>, + rest: &'b Option, +} + +impl<'a, 'b, E, R> ElementsAndRest<'a, 'b, E, R> { + pub fn new(elements: &'b Vec<'a, E>, rest: &'b Option) -> Self { + Self { elements, rest } + } +} + +impl<'a, 'b, E: Serialize, R: Serialize> Serialize for ElementsAndRest<'a, 'b, E, R> { + fn serialize(&self, serializer: S) -> Result { + let mut seq = serializer.serialize_seq(Some(self.elements.len() + 1))?; + for element in self.elements { + seq.serialize_element(element)?; + } + if let Some(rest) = self.rest { + seq.serialize_element(rest)?; + } + seq.end() + } +}