mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 12:51:57 +00:00
feat(transformer/jsx-source): get the correct lineNumber and columnNumber from the span. (#3142)
This commit is contained in:
parent
bdae6b0e4e
commit
a52e321b25
7 changed files with 54 additions and 8 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1683,6 +1683,7 @@ dependencies = [
|
||||||
"oxc_parser",
|
"oxc_parser",
|
||||||
"oxc_span",
|
"oxc_span",
|
||||||
"oxc_syntax",
|
"oxc_syntax",
|
||||||
|
"ropey",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -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")]
|
||||||
|
|
|
||||||
|
|
@ -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 }
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
14
crates/oxc_transformer/src/react/utils.rs
Normal file
14
crates/oxc_transformer/src/react/utils.rs
Normal 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)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue