diff --git a/crates/oxc_transformer/src/react/jsx.rs b/crates/oxc_transformer/src/react/jsx.rs index 286cd9903d236..c68aa22d44ede 100644 --- a/crates/oxc_transformer/src/react/jsx.rs +++ b/crates/oxc_transformer/src/react/jsx.rs @@ -11,7 +11,7 @@ use oxc_syntax::{ }; use oxc_traverse::{Traverse, TraverseCtx}; -use super::{diagnostics, utils::get_line_column}; +use super::diagnostics; pub use super::{ jsx_self::ReactJsxSelf, jsx_source::ReactJsxSource, @@ -560,7 +560,7 @@ impl<'a> ReactJsx<'a> { if let Some(span) = source_attr_span { self.jsx_source.report_error(span); } else { - let (line, column) = get_line_column(e.span().start, self.ctx.source_text); + let (line, column) = self.jsx_source.get_line_column(e.span().start); properties.push( self.jsx_source.get_object_property_kind_for_jsx_plugin(line, column, ctx), ); @@ -614,7 +614,7 @@ impl<'a> ReactJsx<'a> { if let Some(span) = source_attr_span { self.jsx_source.report_error(span); } else { - let (line, column) = get_line_column(e.span().start, self.ctx.source_text); + let (line, column) = self.jsx_source.get_line_column(e.span().start); let expr = self.jsx_source.get_source_object(line, column, ctx); arguments.push(Argument::from(expr)); } diff --git a/crates/oxc_transformer/src/react/jsx_source.rs b/crates/oxc_transformer/src/react/jsx_source.rs index d5b0924a0bec8..6d6892f1b1948 100644 --- a/crates/oxc_transformer/src/react/jsx_source.rs +++ b/crates/oxc_transformer/src/react/jsx_source.rs @@ -3,6 +3,7 @@ use oxc_diagnostics::OxcDiagnostic; use oxc_span::{Span, SPAN}; use oxc_syntax::{number::NumberBase, symbol::SymbolFlags}; use oxc_traverse::{Traverse, TraverseCtx}; +use ropey::Rope; use super::utils::get_line_column; use crate::{context::Ctx, helpers::bindings::BoundIdentifier}; @@ -19,13 +20,14 @@ const FILE_NAME_VAR: &str = "jsxFileName"; /// In: `` /// Out: `` pub struct ReactJsxSource<'a> { - ctx: Ctx<'a>, filename_var: Option>, + source_rope: Option, + ctx: Ctx<'a>, } impl<'a> ReactJsxSource<'a> { pub fn new(ctx: Ctx<'a>) -> Self { - Self { ctx, filename_var: None } + Self { filename_var: None, source_rope: None, ctx } } } @@ -40,6 +42,13 @@ impl<'a> Traverse<'a> for ReactJsxSource<'a> { } impl<'a> ReactJsxSource<'a> { + pub fn get_line_column(&mut self, offset: u32) -> (usize, usize) { + if self.source_rope.is_none() { + self.source_rope = Some(Rope::from_str(self.ctx.source_text)); + } + get_line_column(self.source_rope.as_ref().unwrap(), offset, self.ctx.source_text) + } + pub fn get_object_property_kind_for_jsx_plugin( &mut self, line: usize, @@ -87,7 +96,7 @@ impl<'a> ReactJsxSource<'a> { // TODO: We shouldn't calculate line + column from scratch each time as it's expensive. // Build a table of byte indexes of each line's start on first usage, and save it. // Then calculate line and column from that. - let (line, column) = get_line_column(elem.span.start, self.ctx.source_text); + let (line, column) = self.get_line_column(elem.span.start); let object = self.get_source_object(line, column, ctx); let value = self .ctx diff --git a/crates/oxc_transformer/src/react/utils.rs b/crates/oxc_transformer/src/react/utils.rs index f1e120da06c86..12a1ebc2eeee3 100644 --- a/crates/oxc_transformer/src/react/utils.rs +++ b/crates/oxc_transformer/src/react/utils.rs @@ -6,9 +6,8 @@ use ropey::Rope; /// Column number is in UTF-16 characters, and starts at 1. /// /// This matches Babel's output. -pub fn get_line_column(offset: u32, source_text: &str) -> (usize, usize) { +pub fn get_line_column(rope: &Rope, offset: u32, source_text: &str) -> (usize, usize) { let offset = offset as usize; - let rope = Rope::from_str(source_text); // Get line number and byte offset of start of line let line_index = rope.byte_to_line(offset); let line_offset = rope.line_to_byte(line_index); @@ -18,74 +17,84 @@ pub fn get_line_column(offset: u32, source_text: &str) -> (usize, usize) { (line_index + 1, column_index + 1) } -#[test] -fn empty_file() { - assert_eq!(get_line_column(0, ""), (1, 1)); -} +#[cfg(test)] +mod test { + use ropey::Rope; -#[test] -fn first_line_start() { - assert_eq!(get_line_column(0, "foo\nbar\n"), (1, 1)); -} + fn test_line_column(offset: u32, source_text: &str) -> (usize, usize) { + let rope = Rope::from_str(source_text); + super::get_line_column(&rope, offset, source_text) + } -#[test] -fn first_line_middle() { - assert_eq!(get_line_column(5, "blahblahblah\noops\n"), (1, 6)); -} + #[test] + fn empty_file() { + assert_eq!(test_line_column(0, ""), (1, 1)); + } -#[test] -fn later_line_start() { - assert_eq!(get_line_column(8, "foo\nbar\nblahblahblah"), (3, 1)); -} + #[test] + fn first_line_start() { + assert_eq!(test_line_column(0, "foo\nbar\n"), (1, 1)); + } -#[test] -fn later_line_middle() { - assert_eq!(get_line_column(12, "foo\nbar\nblahblahblah"), (3, 5)); -} + #[test] + fn first_line_middle() { + assert_eq!(test_line_column(5, "blahblahblah\noops\n"), (1, 6)); + } -#[test] -fn after_2_byte_unicode() { - assert_eq!("£".len(), 2); - assert_eq!(utf16_len("£"), 1); - assert_eq!(get_line_column(4, "£abc"), (1, 4)); -} + #[test] + fn later_line_start() { + assert_eq!(test_line_column(8, "foo\nbar\nblahblahblah"), (3, 1)); + } -#[test] -fn after_3_byte_unicode() { - assert_eq!("अ".len(), 3); - assert_eq!(utf16_len("अ"), 1); - assert_eq!(get_line_column(5, "अabc"), (1, 4)); -} + #[test] + fn later_line_middle() { + assert_eq!(test_line_column(12, "foo\nbar\nblahblahblah"), (3, 5)); + } -#[test] -fn after_4_byte_unicode() { - assert_eq!("🍄".len(), 4); - assert_eq!(utf16_len("🍄"), 2); - assert_eq!(get_line_column(6, "🍄abc"), (1, 5)); -} + #[test] + fn after_2_byte_unicode() { + assert_eq!("£".len(), 2); + assert_eq!(utf16_len("£"), 1); + assert_eq!(test_line_column(4, "£abc"), (1, 4)); + } -#[test] -fn after_2_byte_unicode_on_previous_line() { - assert_eq!("£".len(), 2); - assert_eq!(utf16_len("£"), 1); - assert_eq!(get_line_column(4, "£\nabc"), (2, 2)); -} + #[test] + fn after_3_byte_unicode() { + assert_eq!("अ".len(), 3); + assert_eq!(utf16_len("अ"), 1); + assert_eq!(test_line_column(5, "अabc"), (1, 4)); + } -#[test] -fn after_3_byte_unicode_on_previous_line() { - assert_eq!("अ".len(), 3); - assert_eq!(utf16_len("अ"), 1); - assert_eq!(get_line_column(5, "अ\nabc"), (2, 2)); -} + #[test] + fn after_4_byte_unicode() { + assert_eq!("🍄".len(), 4); + assert_eq!(utf16_len("🍄"), 2); + assert_eq!(test_line_column(6, "🍄abc"), (1, 5)); + } -#[test] -fn after_4_byte_unicode_on_previous_line() { - assert_eq!("🍄".len(), 4); - assert_eq!(utf16_len("🍄"), 2); - assert_eq!(get_line_column(6, "🍄\nabc"), (2, 2)); -} + #[test] + fn after_2_byte_unicode_on_previous_line() { + assert_eq!("£".len(), 2); + assert_eq!(utf16_len("£"), 1); + assert_eq!(test_line_column(4, "£\nabc"), (2, 2)); + } -#[cfg(test)] -fn utf16_len(s: &str) -> usize { - s.encode_utf16().count() + #[test] + fn after_3_byte_unicode_on_previous_line() { + assert_eq!("अ".len(), 3); + assert_eq!(utf16_len("अ"), 1); + assert_eq!(test_line_column(5, "अ\nabc"), (2, 2)); + } + + #[test] + fn after_4_byte_unicode_on_previous_line() { + assert_eq!("🍄".len(), 4); + assert_eq!(utf16_len("🍄"), 2); + assert_eq!(test_line_column(6, "🍄\nabc"), (2, 2)); + } + + #[cfg(test)] + fn utf16_len(s: &str) -> usize { + s.encode_utf16().count() + } }