Skip to content

Commit 9403ce2

Browse files
authored
fix(react-compiler): Fix fast check (#10538)
1 parent 1b5b4f8 commit 9403ce2

File tree

4 files changed

+207
-27
lines changed

4 files changed

+207
-27
lines changed

.changeset/nice-grapes-mix.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
swc_core: patch
3+
swc_ecma_react_compiler: patch
4+
---
5+
6+
fix(react-compiler): Fix fast check

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/swc_ecma_react_compiler/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,7 @@ swc_atoms = { version = "5.0.0", path = "../swc_atoms" }
1717
swc_common = { version = "11.0.0", path = "../swc_common" }
1818
swc_ecma_ast = { version = "11.0.0", path = "../swc_ecma_ast" }
1919
swc_ecma_visit = { version = "11.0.0", path = "../swc_ecma_visit" }
20+
21+
[dev-dependencies]
22+
swc_ecma_parser = { version = "14.0.0", path = "../swc_ecma_parser" }
23+
testing = { version = "12.0.0", path = "../testing" }

crates/swc_ecma_react_compiler/src/fast_check.rs

Lines changed: 195 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use swc_ecma_ast::{Callee, Expr, FnDecl, FnExpr, Pat, Program, ReturnStmt, Stmt, VarDeclarator};
1+
use swc_ecma_ast::{
2+
Callee, ExportDefaultDecl, ExportDefaultExpr, Expr, FnDecl, FnExpr, Pat, Program, Stmt,
3+
VarDeclarator,
4+
};
25
use swc_ecma_visit::{Visit, VisitWith};
36
pub fn is_required(program: &Program) -> bool {
47
let mut finder = Finder::default();
@@ -31,18 +34,40 @@ impl Visit for Finder {
3134
node.visit_children_with(self);
3235
}
3336

37+
fn visit_export_default_decl(&mut self, node: &ExportDefaultDecl) {
38+
let old = self.is_interested;
39+
40+
self.is_interested = true;
41+
42+
node.visit_children_with(self);
43+
44+
self.is_interested = old;
45+
}
46+
47+
fn visit_export_default_expr(&mut self, node: &ExportDefaultExpr) {
48+
let old = self.is_interested;
49+
50+
self.is_interested = true;
51+
52+
node.visit_children_with(self);
53+
54+
self.is_interested = old;
55+
}
56+
3457
fn visit_expr(&mut self, node: &Expr) {
3558
if self.found {
3659
return;
3760
}
38-
if matches!(
39-
node,
40-
Expr::JSXMember(..)
41-
| Expr::JSXNamespacedName(..)
42-
| Expr::JSXEmpty(..)
43-
| Expr::JSXElement(..)
44-
| Expr::JSXFragment(..)
45-
) {
61+
if self.is_interested
62+
&& matches!(
63+
node,
64+
Expr::JSXMember(..)
65+
| Expr::JSXNamespacedName(..)
66+
| Expr::JSXEmpty(..)
67+
| Expr::JSXElement(..)
68+
| Expr::JSXFragment(..)
69+
)
70+
{
4671
self.found = true;
4772
return;
4873
}
@@ -52,6 +77,7 @@ impl Visit for Finder {
5277

5378
fn visit_fn_decl(&mut self, node: &FnDecl) {
5479
let old = self.is_interested;
80+
5581
self.is_interested = node.ident.sym.starts_with("use")
5682
|| node.ident.sym.starts_with(|c: char| c.is_ascii_uppercase());
5783

@@ -72,19 +98,6 @@ impl Visit for Finder {
7298
self.is_interested = old;
7399
}
74100

75-
fn visit_return_stmt(&mut self, node: &ReturnStmt) {
76-
if self.is_interested {
77-
if let Some(Expr::JSXElement(..) | Expr::JSXEmpty(..) | Expr::JSXFragment(..)) =
78-
node.arg.as_deref()
79-
{
80-
self.found = true;
81-
return;
82-
}
83-
}
84-
85-
node.visit_children_with(self);
86-
}
87-
88101
fn visit_stmt(&mut self, node: &Stmt) {
89102
if self.found {
90103
return;
@@ -95,15 +108,170 @@ impl Visit for Finder {
95108
fn visit_var_declarator(&mut self, node: &VarDeclarator) {
96109
let old = self.is_interested;
97110

98-
if let Pat::Ident(ident) = &node.name {
99-
self.is_interested = ident.sym.starts_with("use")
100-
|| ident.sym.starts_with(|c: char| c.is_ascii_uppercase());
101-
} else {
102-
self.is_interested = false;
111+
if matches!(node.init.as_deref(), Some(Expr::Fn(..) | Expr::Arrow(..))) {
112+
if let Pat::Ident(ident) = &node.name {
113+
self.is_interested = ident.sym.starts_with("use")
114+
|| ident.sym.starts_with(|c: char| c.is_ascii_uppercase());
115+
} else {
116+
self.is_interested = false;
117+
}
103118
}
104119

105120
node.visit_children_with(self);
106121

107122
self.is_interested = old;
108123
}
109124
}
125+
126+
#[cfg(test)]
127+
mod tests {
128+
use swc_common::FileName;
129+
use swc_ecma_parser::{parse_file_as_program, EsSyntax, Syntax};
130+
use testing::run_test2;
131+
132+
use super::*;
133+
134+
fn assert_required(code: &str, required: bool) {
135+
run_test2(false, |cm, _| {
136+
let fm = cm.new_source_file(FileName::Custom("test.tsx".into()).into(), code.into());
137+
138+
let program = parse_file_as_program(
139+
&fm,
140+
Syntax::Es(EsSyntax {
141+
jsx: true,
142+
..Default::default()
143+
}),
144+
Default::default(),
145+
Default::default(),
146+
&mut vec![],
147+
)
148+
.unwrap();
149+
150+
assert_eq!(is_required(&program), required);
151+
152+
Ok(())
153+
})
154+
.unwrap();
155+
}
156+
157+
#[test]
158+
fn lazy_return() {
159+
assert_required(
160+
"
161+
function Foo() {
162+
const a = <div>Hello</div>;
163+
164+
return a
165+
}
166+
",
167+
true,
168+
);
169+
170+
assert_required(
171+
"
172+
function Foo() {
173+
",
174+
false,
175+
);
176+
}
177+
178+
#[test]
179+
fn return_jsx() {
180+
assert_required(
181+
"
182+
function Foo() {
183+
return <div>Hello</div>;
184+
}
185+
",
186+
true,
187+
);
188+
}
189+
190+
#[test]
191+
fn use_hooks() {
192+
assert_required(
193+
"
194+
function Foo(props) {
195+
const [a, b] = useState(0);
196+
197+
return props.children;
198+
}
199+
",
200+
true,
201+
);
202+
}
203+
204+
#[test]
205+
fn arrow_function() {
206+
assert_required(
207+
"
208+
const Foo = () => <div>Hello</div>;
209+
",
210+
true,
211+
);
212+
213+
assert_required(
214+
"
215+
const Foo = () => {
216+
return <div>Hello</div>;
217+
};
218+
",
219+
true,
220+
);
221+
}
222+
223+
#[test]
224+
fn export_const_arrow_function() {
225+
assert_required(
226+
"
227+
export const Foo = () => <div>Hello</div>;
228+
",
229+
true,
230+
);
231+
232+
assert_required(
233+
"
234+
export const Foo = () => {
235+
return <div>Hello</div>;
236+
};
237+
",
238+
true,
239+
);
240+
}
241+
242+
#[test]
243+
fn normal_arrow_function() {
244+
assert_required(
245+
"
246+
const Foo = () => {
247+
const a = 1;
248+
console.log(a);
249+
};
250+
",
251+
false,
252+
);
253+
}
254+
255+
#[test]
256+
fn export_default_arrow_function() {
257+
assert_required(
258+
"
259+
export default () => <div>Hello</div>;
260+
",
261+
true,
262+
);
263+
}
264+
265+
#[test]
266+
fn not_required_arrow_function() {
267+
assert_required(
268+
"
269+
export default () => {
270+
const a = 1;
271+
console.log(a);
272+
};
273+
",
274+
false,
275+
);
276+
}
277+
}

0 commit comments

Comments
 (0)