1
1
use swc_core:: ecma:: {
2
- ast:: { Callee , Expr , FnDecl , FnExpr , Pat , Program , ReturnStmt , Stmt , VarDeclarator } ,
2
+ ast:: {
3
+ Callee , ExportDefaultDecl , ExportDefaultExpr , Expr , FnDecl , FnExpr , Pat , Program , Stmt ,
4
+ VarDeclarator ,
5
+ } ,
3
6
visit:: { Visit , VisitWith } ,
4
7
} ;
5
8
@@ -34,18 +37,40 @@ impl Visit for Finder {
34
37
node. visit_children_with ( self ) ;
35
38
}
36
39
40
+ fn visit_export_default_decl ( & mut self , node : & ExportDefaultDecl ) {
41
+ let old = self . is_interested ;
42
+
43
+ self . is_interested = true ;
44
+
45
+ node. visit_children_with ( self ) ;
46
+
47
+ self . is_interested = old;
48
+ }
49
+
50
+ fn visit_export_default_expr ( & mut self , node : & ExportDefaultExpr ) {
51
+ let old = self . is_interested ;
52
+
53
+ self . is_interested = true ;
54
+
55
+ node. visit_children_with ( self ) ;
56
+
57
+ self . is_interested = old;
58
+ }
59
+
37
60
fn visit_expr ( & mut self , node : & Expr ) {
38
61
if self . found {
39
62
return ;
40
63
}
41
- if matches ! (
42
- node,
43
- Expr :: JSXMember ( ..)
44
- | Expr :: JSXNamespacedName ( ..)
45
- | Expr :: JSXEmpty ( ..)
46
- | Expr :: JSXElement ( ..)
47
- | Expr :: JSXFragment ( ..)
48
- ) {
64
+ if self . is_interested
65
+ && matches ! (
66
+ node,
67
+ Expr :: JSXMember ( ..)
68
+ | Expr :: JSXNamespacedName ( ..)
69
+ | Expr :: JSXEmpty ( ..)
70
+ | Expr :: JSXElement ( ..)
71
+ | Expr :: JSXFragment ( ..)
72
+ )
73
+ {
49
74
self . found = true ;
50
75
return ;
51
76
}
@@ -55,6 +80,7 @@ impl Visit for Finder {
55
80
56
81
fn visit_fn_decl ( & mut self , node : & FnDecl ) {
57
82
let old = self . is_interested ;
83
+
58
84
self . is_interested = node. ident . sym . starts_with ( "use" )
59
85
|| node. ident . sym . starts_with ( |c : char | c. is_ascii_uppercase ( ) ) ;
60
86
@@ -75,19 +101,6 @@ impl Visit for Finder {
75
101
self . is_interested = old;
76
102
}
77
103
78
- fn visit_return_stmt ( & mut self , node : & ReturnStmt ) {
79
- if self . is_interested {
80
- if let Some ( Expr :: JSXElement ( ..) | Expr :: JSXEmpty ( ..) | Expr :: JSXFragment ( ..) ) =
81
- node. arg . as_deref ( )
82
- {
83
- self . found = true ;
84
- return ;
85
- }
86
- }
87
-
88
- node. visit_children_with ( self ) ;
89
- }
90
-
91
104
fn visit_stmt ( & mut self , node : & Stmt ) {
92
105
if self . found {
93
106
return ;
@@ -98,15 +111,172 @@ impl Visit for Finder {
98
111
fn visit_var_declarator ( & mut self , node : & VarDeclarator ) {
99
112
let old = self . is_interested ;
100
113
101
- if let Pat :: Ident ( ident) = & node. name {
102
- self . is_interested = ident. sym . starts_with ( "use" )
103
- || ident. sym . starts_with ( |c : char | c. is_ascii_uppercase ( ) ) ;
104
- } else {
105
- self . is_interested = false ;
114
+ if matches ! ( node. init. as_deref( ) , Some ( Expr :: Fn ( ..) | Expr :: Arrow ( ..) ) ) {
115
+ if let Pat :: Ident ( ident) = & node. name {
116
+ self . is_interested = ident. sym . starts_with ( "use" )
117
+ || ident. sym . starts_with ( |c : char | c. is_ascii_uppercase ( ) ) ;
118
+ } else {
119
+ self . is_interested = false ;
120
+ }
106
121
}
107
122
108
123
node. visit_children_with ( self ) ;
109
124
110
125
self . is_interested = old;
111
126
}
112
127
}
128
+
129
+ #[ cfg( test) ]
130
+ mod tests {
131
+ use swc_core:: {
132
+ common:: FileName ,
133
+ ecma:: parser:: { parse_file_as_program, EsSyntax } ,
134
+ } ;
135
+ use testing:: run_test2;
136
+
137
+ use super :: * ;
138
+
139
+ fn assert_required ( code : & str , required : bool ) {
140
+ run_test2 ( false , |cm, _| {
141
+ let fm = cm. new_source_file ( FileName :: Custom ( "test.tsx" . into ( ) ) . into ( ) , code. into ( ) ) ;
142
+
143
+ let program = parse_file_as_program (
144
+ & fm,
145
+ swc_core:: ecma:: parser:: Syntax :: Es ( EsSyntax {
146
+ jsx : true ,
147
+ ..Default :: default ( )
148
+ } ) ,
149
+ Default :: default ( ) ,
150
+ Default :: default ( ) ,
151
+ & mut vec ! [ ] ,
152
+ )
153
+ . unwrap ( ) ;
154
+
155
+ assert_eq ! ( is_required( & program) , required) ;
156
+
157
+ Ok ( ( ) )
158
+ } )
159
+ . unwrap ( ) ;
160
+ }
161
+
162
+ #[ test]
163
+ fn lazy_return ( ) {
164
+ assert_required (
165
+ "
166
+ function Foo() {
167
+ const a = <div>Hello</div>;
168
+
169
+ return a
170
+ }
171
+ " ,
172
+ true ,
173
+ ) ;
174
+
175
+ assert_required (
176
+ "
177
+ function Foo() {
178
+ " ,
179
+ false ,
180
+ ) ;
181
+ }
182
+
183
+ #[ test]
184
+ fn return_jsx ( ) {
185
+ assert_required (
186
+ "
187
+ function Foo() {
188
+ return <div>Hello</div>;
189
+ }
190
+ " ,
191
+ true ,
192
+ ) ;
193
+ }
194
+
195
+ #[ test]
196
+ fn use_hooks ( ) {
197
+ assert_required (
198
+ "
199
+ function Foo(props) {
200
+ const [a, b] = useState(0);
201
+
202
+ return props.children;
203
+ }
204
+ " ,
205
+ true ,
206
+ ) ;
207
+ }
208
+
209
+ #[ test]
210
+ fn arrow_function ( ) {
211
+ assert_required (
212
+ "
213
+ const Foo = () => <div>Hello</div>;
214
+ " ,
215
+ true ,
216
+ ) ;
217
+
218
+ assert_required (
219
+ "
220
+ const Foo = () => {
221
+ return <div>Hello</div>;
222
+ };
223
+ " ,
224
+ true ,
225
+ ) ;
226
+ }
227
+
228
+ #[ test]
229
+ fn export_const_arrow_function ( ) {
230
+ assert_required (
231
+ "
232
+ export const Foo = () => <div>Hello</div>;
233
+ " ,
234
+ true ,
235
+ ) ;
236
+
237
+ assert_required (
238
+ "
239
+ export const Foo = () => {
240
+ return <div>Hello</div>;
241
+ };
242
+ " ,
243
+ true ,
244
+ ) ;
245
+ }
246
+
247
+ #[ test]
248
+ fn normal_arrow_function ( ) {
249
+ assert_required (
250
+ "
251
+ const Foo = () => {
252
+ const a = 1;
253
+ console.log(a);
254
+ };
255
+ " ,
256
+ false ,
257
+ ) ;
258
+ }
259
+
260
+ #[ test]
261
+ fn export_default_arrow_function ( ) {
262
+ assert_required (
263
+ "
264
+ export default () => <div>Hello</div>;
265
+ " ,
266
+ true ,
267
+ ) ;
268
+ }
269
+
270
+ #[ test]
271
+ fn not_required_arrow_function ( ) {
272
+ assert_required (
273
+ "
274
+ export default () => {
275
+ const a = 1;
276
+ console.log(a);
277
+ };
278
+ " ,
279
+ false ,
280
+ ) ;
281
+ }
282
+ }
0 commit comments