1
1
use oxc_ast:: ast:: * ;
2
2
use oxc_diagnostics:: OxcDiagnostic ;
3
3
use oxc_span:: { Span , SPAN } ;
4
- use oxc_syntax:: number:: NumberBase ;
4
+ use oxc_syntax:: { number:: NumberBase , symbol:: SymbolFlags } ;
5
+ use oxc_traverse:: TraverseCtx ;
5
6
6
- use crate :: context:: Ctx ;
7
+ use crate :: { context:: Ctx , helpers :: bindings :: BoundIdentifier } ;
7
8
8
9
use super :: utils:: get_line_column;
9
10
10
11
const SOURCE : & str = "__source" ;
11
- const FILE_NAME_VAR : & str = "_jsxFileName " ;
12
+ const FILE_NAME_VAR : & str = "jsxFileName " ;
12
13
13
14
/// [plugin-transform-react-jsx-source](https://babeljs.io/docs/babel-plugin-transform-react-jsx-source)
14
15
///
@@ -20,26 +21,32 @@ const FILE_NAME_VAR: &str = "_jsxFileName";
20
21
/// Out: `<sometag __source={ { fileName: 'this/file.js', lineNumber: 10, columnNumber: 1 } } />`
21
22
pub struct ReactJsxSource < ' a > {
22
23
ctx : Ctx < ' a > ,
24
+ filename_var : Option < BoundIdentifier < ' a > > ,
23
25
}
24
26
25
27
impl < ' a > ReactJsxSource < ' a > {
26
28
pub fn new ( ctx : Ctx < ' a > ) -> Self {
27
- Self { ctx }
29
+ Self { ctx, filename_var : None }
28
30
}
29
31
30
- pub fn transform_jsx_opening_element ( & mut self , elem : & mut JSXOpeningElement < ' a > ) {
31
- self . add_source_attribute ( elem) ;
32
+ pub fn transform_jsx_opening_element (
33
+ & mut self ,
34
+ elem : & mut JSXOpeningElement < ' a > ,
35
+ ctx : & mut TraverseCtx < ' a > ,
36
+ ) {
37
+ self . add_source_attribute ( elem, ctx) ;
32
38
}
33
39
34
40
pub fn get_object_property_kind_for_jsx_plugin (
35
41
& mut self ,
36
42
line : usize ,
37
43
column : usize ,
44
+ ctx : & mut TraverseCtx < ' a > ,
38
45
) -> ObjectPropertyKind < ' a > {
39
46
let kind = PropertyKind :: Init ;
40
47
let ident = IdentifierName :: new ( SPAN , SOURCE . into ( ) ) ;
41
48
let key = self . ctx . ast . property_key_identifier ( ident) ;
42
- let value = self . get_source_object ( line, column) ;
49
+ let value = self . get_source_object ( line, column, ctx ) ;
43
50
let obj = self . ctx . ast . object_property ( SPAN , kind, key, value, None , false , false , false ) ;
44
51
ObjectPropertyKind :: ObjectProperty ( obj)
45
52
}
@@ -53,7 +60,11 @@ impl<'a> ReactJsxSource<'a> {
53
60
impl < ' a > ReactJsxSource < ' a > {
54
61
/// `<sometag __source={ { fileName: 'this/file.js', lineNumber: 10, columnNumber: 1 } } />`
55
62
/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
56
- fn add_source_attribute ( & mut self , elem : & mut JSXOpeningElement < ' a > ) {
63
+ fn add_source_attribute (
64
+ & mut self ,
65
+ elem : & mut JSXOpeningElement < ' a > ,
66
+ ctx : & mut TraverseCtx < ' a > ,
67
+ ) {
57
68
// Check if `__source` attribute already exists
58
69
for item in & elem. attributes {
59
70
if let JSXAttributeItem :: Attribute ( attribute) = item {
@@ -70,21 +81,26 @@ impl<'a> ReactJsxSource<'a> {
70
81
self . ctx . ast . alloc ( self . ctx . ast . jsx_identifier ( SPAN , SOURCE . into ( ) ) ) ,
71
82
) ;
72
83
let ( line, column) = get_line_column ( elem. span . start , self . ctx . source_text ) ;
73
- let object = self . get_source_object ( line, column) ;
84
+ let object = self . get_source_object ( line, column, ctx ) ;
74
85
let expr = self . ctx . ast . jsx_expression_container ( SPAN , JSXExpression :: from ( object) ) ;
75
86
let value = JSXAttributeValue :: ExpressionContainer ( expr) ;
76
87
let attribute_item = self . ctx . ast . jsx_attribute ( SPAN , key, Some ( value) ) ;
77
88
elem. attributes . push ( JSXAttributeItem :: Attribute ( attribute_item) ) ;
78
89
}
79
90
80
91
#[ allow( clippy:: cast_precision_loss) ]
81
- pub fn get_source_object ( & mut self , line : usize , column : usize ) -> Expression < ' a > {
92
+ pub fn get_source_object (
93
+ & mut self ,
94
+ line : usize ,
95
+ column : usize ,
96
+ ctx : & mut TraverseCtx < ' a > ,
97
+ ) -> Expression < ' a > {
82
98
let kind = PropertyKind :: Init ;
83
99
84
100
let filename = {
85
101
let name = IdentifierName :: new ( SPAN , "fileName" . into ( ) ) ;
86
102
let key = self . ctx . ast . property_key_identifier ( name) ;
87
- let ident = self . ctx . ast . identifier_reference ( SPAN , FILE_NAME_VAR ) ;
103
+ let ident = self . get_filename_var ( ctx) . create_read_reference ( ctx ) ;
88
104
let value = self . ctx . ast . identifier_reference_expression ( ident) ;
89
105
self . ctx . ast . object_property ( SPAN , kind, key, value, None , false , false , false )
90
106
} ;
@@ -122,10 +138,12 @@ impl<'a> ReactJsxSource<'a> {
122
138
self . ctx . ast . object_expression ( SPAN , properties, None )
123
139
}
124
140
125
- pub fn get_var_file_name_statement ( & self ) -> Statement < ' a > {
141
+ pub fn get_var_file_name_statement ( & mut self ) -> Option < Statement < ' a > > {
142
+ let filename_var = self . filename_var . as_ref ( ) ?;
143
+
126
144
let var_kind = VariableDeclarationKind :: Var ;
127
145
let id = {
128
- let ident = BindingIdentifier :: new ( SPAN , FILE_NAME_VAR . into ( ) ) ;
146
+ let ident = filename_var . create_binding_identifier ( ) ;
129
147
let ident = self . ctx . ast . binding_pattern_identifier ( ident) ;
130
148
self . ctx . ast . binding_pattern ( ident, None , false )
131
149
} ;
@@ -136,6 +154,17 @@ impl<'a> ReactJsxSource<'a> {
136
154
self . ctx . ast . new_vec_single ( decl)
137
155
} ;
138
156
let var_decl = self . ctx . ast . variable_declaration ( SPAN , var_kind, decl, Modifiers :: empty ( ) ) ;
139
- Statement :: VariableDeclaration ( var_decl)
157
+ Some ( Statement :: VariableDeclaration ( var_decl) )
158
+ }
159
+
160
+ fn get_filename_var ( & mut self , ctx : & mut TraverseCtx < ' a > ) -> BoundIdentifier < ' a > {
161
+ if self . filename_var . is_none ( ) {
162
+ self . filename_var = Some ( BoundIdentifier :: new_root_uid (
163
+ FILE_NAME_VAR ,
164
+ SymbolFlags :: FunctionScopedVariable ,
165
+ ctx,
166
+ ) ) ;
167
+ }
168
+ self . filename_var . as_ref ( ) . unwrap ( ) . clone ( )
140
169
}
141
170
}
0 commit comments