diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index 73fd1b610..8a12cbb11 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -832,13 +832,15 @@ pub enum TSModuleDeclarationBody<'a> { TSModuleBlock(Box<'a, TSModuleBlock<'a>>), } +// See serializer in serialize.rs #[visited_node] #[derive(Debug, Hash)] -#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] +#[cfg_attr(feature = "serialize", derive(Tsify))] #[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))] pub struct TSModuleBlock<'a> { #[cfg_attr(feature = "serialize", serde(flatten))] pub span: Span, + #[cfg_attr(feature = "serialize", serde(skip))] pub directives: Vec<'a, Directive<'a>>, pub body: Vec<'a, Statement<'a>>, } diff --git a/crates/oxc_ast/src/serialize.rs b/crates/oxc_ast/src/serialize.rs index bf1cf0c2b..a578a1f9e 100644 --- a/crates/oxc_ast/src/serialize.rs +++ b/crates/oxc_ast/src/serialize.rs @@ -8,8 +8,9 @@ use serde::{ use crate::ast::{ ArrayAssignmentTarget, ArrayPattern, AssignmentTargetMaybeDefault, AssignmentTargetProperty, AssignmentTargetRest, BindingPattern, BindingPatternKind, BindingProperty, BindingRestElement, - Elision, FormalParameter, FormalParameterKind, FormalParameters, ObjectAssignmentTarget, - ObjectPattern, Program, RegExpFlags, TSTypeAnnotation, + Directive, Elision, FormalParameter, FormalParameterKind, FormalParameters, + ObjectAssignmentTarget, ObjectPattern, Program, RegExpFlags, Statement, StringLiteral, + TSModuleBlock, TSTypeAnnotation, }; pub struct EcmaFormatter; @@ -199,3 +200,52 @@ impl<'a, 'b, E: Serialize, R: Serialize> Serialize for ElementsAndRest<'a, 'b, E } } } + +/// Serialize `TSModuleBlock` to be ESTree compatible, with `body` and `directives` fields combined, +/// and directives output as `StringLiteral` expression statements +impl<'a> Serialize for TSModuleBlock<'a> { + fn serialize(&self, serializer: S) -> Result { + let converted = SerTSModuleBlock { + span: self.span, + body: DirectivesAndStatements { directives: &self.directives, body: &self.body }, + }; + converted.serialize(serializer) + } +} + +#[derive(Serialize)] +#[serde(tag = "type", rename = "TSModuleBlock")] +struct SerTSModuleBlock<'a, 'b> { + #[serde(flatten)] + span: Span, + body: DirectivesAndStatements<'a, 'b>, +} + +struct DirectivesAndStatements<'a, 'b> { + directives: &'b [Directive<'a>], + body: &'b [Statement<'a>], +} + +impl<'a, 'b> Serialize for DirectivesAndStatements<'a, 'b> { + fn serialize(&self, serializer: S) -> Result { + let mut seq = serializer.serialize_seq(Some(self.directives.len() + self.body.len()))?; + for directive in self.directives { + seq.serialize_element(&DirectiveAsStatement { + span: directive.span, + expression: &directive.expression, + })?; + } + for stmt in self.body { + seq.serialize_element(stmt)?; + } + seq.end() + } +} + +#[derive(Serialize)] +#[serde(tag = "type", rename = "ExpressionStatement")] +struct DirectiveAsStatement<'a, 'b> { + #[serde(flatten)] + span: Span, + expression: &'b StringLiteral<'a>, +}