diff --git a/crates/oxc_ast/src/ast/jsx.rs b/crates/oxc_ast/src/ast/jsx.rs index 89e5bb96dab75..0798571c4703a 100644 --- a/crates/oxc_ast/src/ast/jsx.rs +++ b/crates/oxc_ast/src/ast/jsx.rs @@ -482,4 +482,10 @@ pub struct JSXText<'a> { pub span: Span, /// The text content. pub value: Atom<'a>, + + /// The raw string as it appears in source code. + /// + /// `None` when this ast node is not constructed from the parser. + #[content_eq(skip)] + pub raw: Option>, } diff --git a/crates/oxc_ast/src/generated/assert_layouts.rs b/crates/oxc_ast/src/generated/assert_layouts.rs index 386141b295396..746e0e8b16075 100644 --- a/crates/oxc_ast/src/generated/assert_layouts.rs +++ b/crates/oxc_ast/src/generated/assert_layouts.rs @@ -898,10 +898,11 @@ const _: () = { assert!(offset_of!(JSXSpreadChild, span) == 0); assert!(offset_of!(JSXSpreadChild, expression) == 8); - assert!(size_of::() == 24); + assert!(size_of::() == 40); assert!(align_of::() == 8); assert!(offset_of!(JSXText, span) == 0); assert!(offset_of!(JSXText, value) == 8); + assert!(offset_of!(JSXText, raw) == 24); assert!(size_of::() == 24); assert!(align_of::() == 8); @@ -2302,10 +2303,11 @@ const _: () = { assert!(offset_of!(JSXSpreadChild, span) == 0); assert!(offset_of!(JSXSpreadChild, expression) == 8); - assert!(size_of::() == 16); + assert!(size_of::() == 24); assert!(align_of::() == 4); assert!(offset_of!(JSXText, span) == 0); assert!(offset_of!(JSXText, value) == 8); + assert!(offset_of!(JSXText, raw) == 16); assert!(size_of::() == 20); assert!(align_of::() == 4); diff --git a/crates/oxc_ast/src/generated/ast_builder.rs b/crates/oxc_ast/src/generated/ast_builder.rs index 39c819107539c..2b9728399e9fb 100644 --- a/crates/oxc_ast/src/generated/ast_builder.rs +++ b/crates/oxc_ast/src/generated/ast_builder.rs @@ -9449,12 +9449,13 @@ impl<'a> AstBuilder<'a> { /// ## Parameters /// * `span`: Node location in source code /// * `value`: The text content. + /// * `raw`: The raw string as it appears in source code. #[inline] - pub fn jsx_child_text(self, span: Span, value: A) -> JSXChild<'a> + pub fn jsx_child_text(self, span: Span, value: A, raw: Option>) -> JSXChild<'a> where A: IntoIn<'a, Atom<'a>>, { - JSXChild::Text(self.alloc_jsx_text(span, value)) + JSXChild::Text(self.alloc_jsx_text(span, value, raw)) } /// Build a [`JSXChild::Element`]. @@ -9569,12 +9570,13 @@ impl<'a> AstBuilder<'a> { /// ## Parameters /// * `span`: Node location in source code /// * `value`: The text content. + /// * `raw`: The raw string as it appears in source code. #[inline] - pub fn jsx_text(self, span: Span, value: A) -> JSXText<'a> + pub fn jsx_text(self, span: Span, value: A, raw: Option>) -> JSXText<'a> where A: IntoIn<'a, Atom<'a>>, { - JSXText { span, value: value.into_in(self.allocator) } + JSXText { span, value: value.into_in(self.allocator), raw } } /// Build a [`JSXText`], and store it in the memory arena. @@ -9584,12 +9586,18 @@ impl<'a> AstBuilder<'a> { /// ## Parameters /// * `span`: Node location in source code /// * `value`: The text content. + /// * `raw`: The raw string as it appears in source code. #[inline] - pub fn alloc_jsx_text(self, span: Span, value: A) -> Box<'a, JSXText<'a>> + pub fn alloc_jsx_text( + self, + span: Span, + value: A, + raw: Option>, + ) -> Box<'a, JSXText<'a>> where A: IntoIn<'a, Atom<'a>>, { - Box::new_in(self.jsx_text(span, value), self.allocator) + Box::new_in(self.jsx_text(span, value, raw), self.allocator) } /// Build a [`TSThisParameter`]. diff --git a/crates/oxc_ast/src/generated/derive_clone_in.rs b/crates/oxc_ast/src/generated/derive_clone_in.rs index c87dfc0903c4e..b0cd851cb99c0 100644 --- a/crates/oxc_ast/src/generated/derive_clone_in.rs +++ b/crates/oxc_ast/src/generated/derive_clone_in.rs @@ -2950,6 +2950,7 @@ impl<'new_alloc> CloneIn<'new_alloc> for JSXText<'_> { JSXText { span: CloneIn::clone_in(&self.span, allocator), value: CloneIn::clone_in(&self.value, allocator), + raw: CloneIn::clone_in(&self.raw, allocator), } } } diff --git a/crates/oxc_ast/src/generated/derive_estree.rs b/crates/oxc_ast/src/generated/derive_estree.rs index 3393dc7f31dec..dec6119e4ac7b 100644 --- a/crates/oxc_ast/src/generated/derive_estree.rs +++ b/crates/oxc_ast/src/generated/derive_estree.rs @@ -2257,6 +2257,7 @@ impl ESTree for JSXText<'_> { state.serialize_field("start", &self.span.start); state.serialize_field("end", &self.span.end); state.serialize_field("value", &self.value); + state.serialize_field("raw", &self.raw); state.end(); } } diff --git a/crates/oxc_parser/src/jsx/mod.rs b/crates/oxc_parser/src/jsx/mod.rs index 56b1b2d14fd30..a2d7afd3949ab 100644 --- a/crates/oxc_parser/src/jsx/mod.rs +++ b/crates/oxc_parser/src/jsx/mod.rs @@ -419,7 +419,13 @@ impl<'a> ParserImpl<'a> { let span = self.start_span(); let value = Atom::from(self.cur_string()); self.bump_any(); - self.ast.alloc_jsx_text(self.end_span(span), value) + let span = self.end_span(span); + // SAFETY: + // range comes from the lexer, which are ensured to meeting the criteria of `get_unchecked`. + let raw = Atom::from(unsafe { + self.source_text.get_unchecked(span.start as usize..span.end as usize) + }); + self.ast.alloc_jsx_text(span, value, Some(raw)) } fn jsx_element_name_eq(lhs: &JSXElementName<'a>, rhs: &JSXElementName<'a>) -> bool { diff --git a/napi/parser/deserialize-js.js b/napi/parser/deserialize-js.js index 069db022e954b..adefa9b05884e 100644 --- a/napi/parser/deserialize-js.js +++ b/napi/parser/deserialize-js.js @@ -1254,6 +1254,7 @@ function deserializeJSXText(pos) { start: deserializeU32(pos), end: deserializeU32(pos + 4), value: deserializeStr(pos + 8), + raw: deserializeOptionStr(pos + 24), }; } diff --git a/napi/parser/deserialize-ts.js b/napi/parser/deserialize-ts.js index 18fbff6891e57..09a8e12379d4d 100644 --- a/napi/parser/deserialize-ts.js +++ b/napi/parser/deserialize-ts.js @@ -1307,6 +1307,7 @@ function deserializeJSXText(pos) { start: deserializeU32(pos), end: deserializeU32(pos + 4), value: deserializeStr(pos + 8), + raw: deserializeOptionStr(pos + 24), }; } diff --git a/npm/oxc-types/types.d.ts b/npm/oxc-types/types.d.ts index ca2db41313217..f5718a8dff6b0 100644 --- a/npm/oxc-types/types.d.ts +++ b/npm/oxc-types/types.d.ts @@ -902,6 +902,7 @@ export interface JSXSpreadChild extends Span { export interface JSXText extends Span { type: 'JSXText'; value: string; + raw: string | null; } export interface TSThisParameter extends Span {