feat(transformer/jsx-source): get the correct lineNumber and columnNumber from the span. (#3142)

This commit is contained in:
Dunqing 2024-05-01 08:30:47 +08:00 committed by GitHub
parent bdae6b0e4e
commit a52e321b25
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 54 additions and 8 deletions

1
Cargo.lock generated
View file

@ -1683,6 +1683,7 @@ dependencies = [
"oxc_parser", "oxc_parser",
"oxc_span", "oxc_span",
"oxc_syntax", "oxc_syntax",
"ropey",
"rustc-hash", "rustc-hash",
"serde", "serde",
] ]

View file

@ -4,6 +4,7 @@
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use miette::{SourceOffset, SourceSpan}; use miette::{SourceOffset, SourceSpan};
#[cfg(feature = "serialize")] #[cfg(feature = "serialize")]
use serde::Serialize; use serde::Serialize;
#[cfg(feature = "serialize")] #[cfg(feature = "serialize")]

View file

@ -28,6 +28,7 @@ oxc_syntax = { workspace = true, features = ["to_js_string"] }
rustc-hash = { workspace = true } rustc-hash = { workspace = true }
indexmap = { workspace = true } indexmap = { workspace = true }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
ropey = { workspace = true }
[dev-dependencies] [dev-dependencies]
oxc_parser = { workspace = true } oxc_parser = { workspace = true }

View file

@ -4,7 +4,7 @@ use std::rc::Rc;
use oxc_allocator::Vec; use oxc_allocator::Vec;
use oxc_ast::{ast::*, AstBuilder}; use oxc_ast::{ast::*, AstBuilder};
use oxc_span::{CompactStr, GetSpan, SPAN}; use oxc_span::{CompactStr, GetSpan, Span, SPAN};
use oxc_syntax::{ use oxc_syntax::{
identifier::{is_irregular_whitespace, is_line_terminator}, identifier::{is_irregular_whitespace, is_line_terminator},
xml_entities::XML_ENTITIES, xml_entities::XML_ENTITIES,
@ -12,6 +12,7 @@ use oxc_syntax::{
use crate::{context::Ctx, helpers::module_imports::NamedImport}; use crate::{context::Ctx, helpers::module_imports::NamedImport};
use super::utils::get_line_column;
pub use super::{ pub use super::{
jsx_self::ReactJsxSelf, jsx_self::ReactJsxSelf,
jsx_source::ReactJsxSource, jsx_source::ReactJsxSource,
@ -217,6 +218,13 @@ enum JSXElementOrFragment<'a, 'b> {
} }
impl<'a, 'b> JSXElementOrFragment<'a, 'b> { impl<'a, 'b> JSXElementOrFragment<'a, 'b> {
fn span(&self) -> Span {
match self {
Self::Element(e) => e.span(),
Self::Fragment(e) => e.span,
}
}
fn attributes(&self) -> Option<&'b Vec<'a, JSXAttributeItem<'a>>> { fn attributes(&self) -> Option<&'b Vec<'a, JSXAttributeItem<'a>>> {
match self { match self {
Self::Element(e) if !e.opening_element.attributes.is_empty() => { Self::Element(e) if !e.opening_element.attributes.is_empty() => {
@ -367,7 +375,9 @@ impl<'a> ReactJsx<'a> {
if let Some(span) = source_attr_span { if let Some(span) = source_attr_span {
self.jsx_source.report_error(span); self.jsx_source.report_error(span);
} else { } else {
properties.push(self.jsx_source.get_object_property_kind_for_jsx_plugin()); let (line, column) = get_line_column(e.span().start, self.ctx.source_text);
properties
.push(self.jsx_source.get_object_property_kind_for_jsx_plugin(line, column));
} }
} }

View file

@ -10,6 +10,8 @@ use crate::context::Ctx;
use self::diagnostics::DuplicateSourceProp; use self::diagnostics::DuplicateSourceProp;
use super::utils::get_line_column;
const SOURCE: &str = "__source"; const SOURCE: &str = "__source";
const FILE_NAME_VAR: &str = "_jsxFileName"; const FILE_NAME_VAR: &str = "_jsxFileName";
@ -47,12 +49,16 @@ impl<'a> ReactJsxSource<'a> {
self.add_source_attribute(elem); self.add_source_attribute(elem);
} }
pub fn get_object_property_kind_for_jsx_plugin(&mut self) -> ObjectPropertyKind<'a> { pub fn get_object_property_kind_for_jsx_plugin(
&mut self,
line: usize,
column: usize,
) -> ObjectPropertyKind<'a> {
self.should_add_jsx_file_name_variable = true; self.should_add_jsx_file_name_variable = true;
let kind = PropertyKind::Init; let kind = PropertyKind::Init;
let ident = IdentifierName::new(SPAN, SOURCE.into()); let ident = IdentifierName::new(SPAN, SOURCE.into());
let key = self.ctx.ast.property_key_identifier(ident); let key = self.ctx.ast.property_key_identifier(ident);
let value = self.get_source_object(); let value = self.get_source_object(line, column);
let obj = self.ctx.ast.object_property(SPAN, kind, key, value, None, false, false, false); let obj = self.ctx.ast.object_property(SPAN, kind, key, value, None, false, false, false);
ObjectPropertyKind::ObjectProperty(obj) ObjectPropertyKind::ObjectProperty(obj)
} }
@ -83,14 +89,16 @@ impl<'a> ReactJsxSource<'a> {
let key = JSXAttributeName::Identifier( let key = JSXAttributeName::Identifier(
self.ctx.ast.alloc(self.ctx.ast.jsx_identifier(SPAN, SOURCE.into())), self.ctx.ast.alloc(self.ctx.ast.jsx_identifier(SPAN, SOURCE.into())),
); );
let object = self.get_source_object(); let (line, column) = get_line_column(elem.span.start, self.ctx.source_text);
let object = self.get_source_object(line, column);
let expr = self.ctx.ast.jsx_expression_container(SPAN, JSXExpression::from(object)); let expr = self.ctx.ast.jsx_expression_container(SPAN, JSXExpression::from(object));
let value = JSXAttributeValue::ExpressionContainer(expr); let value = JSXAttributeValue::ExpressionContainer(expr);
let attribute_item = self.ctx.ast.jsx_attribute(SPAN, key, Some(value)); let attribute_item = self.ctx.ast.jsx_attribute(SPAN, key, Some(value));
elem.attributes.push(JSXAttributeItem::Attribute(attribute_item)); elem.attributes.push(JSXAttributeItem::Attribute(attribute_item));
} }
fn get_source_object(&self) -> Expression<'a> { #[allow(clippy::cast_precision_loss)]
fn get_source_object(&self, line: usize, column: usize) -> Expression<'a> {
let kind = PropertyKind::Init; let kind = PropertyKind::Init;
let filename = { let filename = {
@ -104,7 +112,12 @@ impl<'a> ReactJsxSource<'a> {
let line_number = { let line_number = {
let ident = IdentifierName::new(SPAN, "lineNumber".into()); let ident = IdentifierName::new(SPAN, "lineNumber".into());
let key = self.ctx.ast.property_key_identifier(ident); let key = self.ctx.ast.property_key_identifier(ident);
let number = self.ctx.ast.number_literal(SPAN, 1.0, "1", NumberBase::Decimal); let number = self.ctx.ast.number_literal(
SPAN,
line as f64,
self.ctx.ast.new_str(&line.to_string()),
NumberBase::Decimal,
);
let value = self.ctx.ast.literal_number_expression(number); let value = self.ctx.ast.literal_number_expression(number);
self.ctx.ast.object_property(SPAN, kind, key, value, None, false, false, false) self.ctx.ast.object_property(SPAN, kind, key, value, None, false, false, false)
}; };
@ -112,7 +125,12 @@ impl<'a> ReactJsxSource<'a> {
let column_number = { let column_number = {
let ident = IdentifierName::new(SPAN, "columnNumber".into()); let ident = IdentifierName::new(SPAN, "columnNumber".into());
let key = self.ctx.ast.property_key_identifier(ident); let key = self.ctx.ast.property_key_identifier(ident);
let number = self.ctx.ast.number_literal(SPAN, 1.0, "1", NumberBase::Decimal); let number = self.ctx.ast.number_literal(
SPAN,
column as f64,
self.ctx.ast.new_str(&column.to_string()),
NumberBase::Decimal,
);
let value = self.ctx.ast.literal_number_expression(number); let value = self.ctx.ast.literal_number_expression(number);
self.ctx.ast.object_property(SPAN, kind, key, value, None, false, false, false) self.ctx.ast.object_property(SPAN, kind, key, value, None, false, false, false)
}; };

View file

@ -3,6 +3,7 @@ mod jsx;
mod jsx_self; mod jsx_self;
mod jsx_source; mod jsx_source;
mod options; mod options;
mod utils;
use std::rc::Rc; use std::rc::Rc;

View file

@ -0,0 +1,14 @@
use ropey::Rope;
/// Get line and column from offset and source text
pub fn get_line_column(offset: u32, source_text: &str) -> (usize, usize) {
let offset = offset as usize;
let rope = Rope::from_str(source_text);
let line = rope.byte_to_line(offset);
let first_char_of_line = rope.line_to_char(line);
// Original offset is byte, but Rope uses char offset
let offset = rope.byte_to_char(offset);
let column = offset - first_char_of_line;
// line and column is zero-indexed, but we want 1-indexed
(line + 1, column + 1)
}