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
454 changes: 232 additions & 222 deletions crates/oxc_ast/src/generated/derive_estree.rs

Large diffs are not rendered by default.

36 changes: 18 additions & 18 deletions crates/oxc_ast/src/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use cow_utils::CowUtils;

use oxc_ast_macros::ast_meta;
use oxc_estree::{
CompactJSSerializer, CompactTSSerializer, ESTree, PrettyJSSerializer, PrettyTSSerializer,
SequenceSerializer, Serializer, StructSerializer,
CompactJSSerializer, CompactTSSerializer, ESTree, JsonSafeString, PrettyJSSerializer,
PrettyTSSerializer, SequenceSerializer, Serializer, StructSerializer,
};

use crate::ast::*;
Expand Down Expand Up @@ -113,7 +113,7 @@ pub struct In<'b, T>(#[expect(dead_code)] pub &'b T);

impl<T> ESTree for In<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) {
"in".serialize(serializer);
JsonSafeString("in").serialize(serializer);
}
}

Expand All @@ -124,7 +124,7 @@ pub struct Init<'b, T>(#[expect(dead_code)] pub &'b T);

impl<T> ESTree for Init<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) {
"init".serialize(serializer);
JsonSafeString("init").serialize(serializer);
}
}

Expand All @@ -143,9 +143,9 @@ impl ESTree for BooleanLiteralRaw<'_> {
let raw = if self.0.span.is_unspanned() {
None
} else if self.0.value {
Some("true")
Some(JsonSafeString("true"))
} else {
Some("false")
Some(JsonSafeString("false"))
};
raw.serialize(serializer);
}
Expand All @@ -159,7 +159,7 @@ pub struct NullLiteralRaw<'b>(pub &'b NullLiteral);
impl ESTree for NullLiteralRaw<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
#[expect(clippy::collection_is_never_read)] // Clippy is wrong!
let raw = if self.0.span.is_unspanned() { None } else { Some("null") };
let raw = if self.0.span.is_unspanned() { None } else { Some(JsonSafeString("null")) };
raw.serialize(serializer);
}
}
Expand All @@ -171,8 +171,8 @@ pub struct BigIntLiteralBigint<'a, 'b>(pub &'b BigIntLiteral<'a>);

impl ESTree for BigIntLiteralBigint<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
let bigint = self.0.raw.strip_suffix('n').unwrap().cow_replace('_', "");
bigint.serialize(serializer);
let bigint = self.0.raw[..self.0.raw.len() - 1].cow_replace('_', "");
JsonSafeString(bigint.as_ref()).serialize(serializer);
}
}

Expand Down Expand Up @@ -206,7 +206,7 @@ impl ESTree for RegExpLiteralRegex<'_, '_> {
if let Some(raw) = &self.0.raw {
let flags_count = flags.bits().count_ones() as usize;
let flags_index = raw.len() - flags_count;
state.serialize_field("flags", &raw[flags_index..]);
state.serialize_field("flags", &JsonSafeString(&raw[flags_index..]));
} else {
state.serialize_field("flags", &flags);
}
Expand Down Expand Up @@ -243,7 +243,7 @@ pub struct RegExpFlagsConverter<'b>(pub &'b RegExpFlags);

impl ESTree for RegExpFlagsConverter<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
self.0.to_string().serialize(serializer);
JsonSafeString(self.0.to_string().as_str()).serialize(serializer);
}
}

Expand Down Expand Up @@ -289,7 +289,7 @@ impl ESTree for FormalParametersRest<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
let rest = self.0;
let mut state = serializer.serialize_struct();
state.serialize_field("type", "RestElement");
state.serialize_field("type", &JsonSafeString("RestElement"));
state.serialize_field("start", &rest.span.start);
state.serialize_field("end", &rest.span.end);
state.serialize_field("argument", &rest.argument.kind);
Expand Down Expand Up @@ -324,7 +324,7 @@ impl ESTree for ObjectPropertyConverter<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
let prop = self.0;
let mut state = serializer.serialize_struct();
state.serialize_field("type", "Property");
state.serialize_field("type", &JsonSafeString("Property"));
state.serialize_field("start", &prop.span.start);
state.serialize_field("end", &prop.span.end);
state.serialize_field("method", &prop.method);
Expand All @@ -351,7 +351,7 @@ impl ESTree for BindingPropertyConverter<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
let prop = self.0;
let mut state = serializer.serialize_struct();
state.serialize_field("type", "Property");
state.serialize_field("type", &JsonSafeString("Property"));
state.serialize_field("start", &prop.span.start);
state.serialize_field("end", &prop.span.end);
state.serialize_field("method", &false);
Expand All @@ -360,11 +360,11 @@ impl ESTree for BindingPropertyConverter<'_, '_> {
state.serialize_field("key", &prop.key);
// Acorn has `kind` field before `value` for shorthand properties
if prop.shorthand {
state.serialize_field("kind", "init");
state.serialize_field("kind", &JsonSafeString("init"));
state.serialize_field("value", &prop.value);
} else {
state.serialize_field("value", &prop.value);
state.serialize_field("kind", "init");
state.serialize_field("kind", &JsonSafeString("init"));
}
state.end();
}
Expand Down Expand Up @@ -400,7 +400,7 @@ impl ESTree for AssignmentTargetPropertyIdentifierValue<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
if let Some(init) = &self.0.init {
let mut state = serializer.serialize_struct();
state.serialize_field("type", "AssignmentPattern");
state.serialize_field("type", &JsonSafeString("AssignmentPattern"));
state.serialize_field("start", &self.0.span.start);
state.serialize_field("end", &self.0.span.end);
state.serialize_field("left", &self.0.binding);
Expand Down Expand Up @@ -441,7 +441,7 @@ impl ESTree for ExportNamedDeclarationConverter<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
let decl = self.0;
let mut state = serializer.serialize_struct();
state.serialize_field("type", "ExportNamedDeclaration");
state.serialize_field("type", &JsonSafeString("ExportNamedDeclaration"));
state.serialize_field("start", &decl.span.start);
state.serialize_field("end", &decl.span.end);
state.serialize_field("declaration", &decl.declaration);
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_estree/src/serialize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use sequences::ESTreeSequenceSerializer;
use structs::ESTreeStructSerializer;

pub use sequences::SequenceSerializer;
pub use strings::JsonSafeString;
pub use structs::{FlatStructSerializer, StructSerializer};

/// Trait for types which can be serialized to ESTree.
Expand Down
18 changes: 18 additions & 0 deletions crates/oxc_estree/src/serialize/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@ use oxc_data_structures::code_buffer::CodeBuffer;

use super::{ESTree, Serializer};

/// A string which does not need any escaping in JSON.
///
/// This provides better performance when you know that the string definitely contains no characters
/// that require escaping, as it avoids the cost of checking that.
///
/// If the string does in fact contain characters that did need escaping, it will result in invalid JSON.
pub struct JsonSafeString<'s>(pub &'s str);

impl ESTree for JsonSafeString<'_> {
#[inline(always)]
fn serialize<S: Serializer>(&self, mut serializer: S) {
let buffer = serializer.buffer_mut();
buffer.print_ascii_byte(b'"');
buffer.print_str(self.0);
buffer.print_ascii_byte(b'"');
}
}

/// [`ESTree`] implementation for string slice.
impl ESTree for str {
fn serialize<S: Serializer>(&self, mut serializer: S) {
Expand Down
Loading
Loading