fix(transformer/react): the refresh plugin cannot handle member expressions with React hooks (#5655)

The previous implementation doesn't handle nested StaticMemberExpression. For example: `A.B.C.useHook`.
This commit is contained in:
Dunqing 2024-09-11 07:57:16 +00:00
parent 0739b5f1e3
commit 3e8b96f165
4 changed files with 54 additions and 49 deletions

View file

@ -2,7 +2,7 @@ use std::{cell::Cell, iter::once};
use base64::prelude::{Engine, BASE64_STANDARD};
use oxc_allocator::CloneIn;
use oxc_ast::{ast::*, match_expression, match_member_expression, AstBuilder};
use oxc_ast::{ast::*, match_expression, AstBuilder};
use oxc_semantic::{Reference, ReferenceFlags, ScopeId, SymbolFlags, SymbolId};
use oxc_span::{Atom, GetSpan, SPAN};
use oxc_syntax::operator::AssignmentOperator;
@ -384,14 +384,10 @@ impl<'a> Traverse<'a> for ReactRefresh<'a> {
return;
}
let name = match &call_expr.callee {
Expression::Identifier(ident) => Some(ident.name.clone()),
Expression::StaticMemberExpression(ref member) => Some(member.property.name.clone()),
_ => None,
};
let Some(hook_name) = name else {
return;
let hook_name = match &call_expr.callee {
Expression::Identifier(ident) => ident.name.clone(),
Expression::StaticMemberExpression(ref member) => member.property.name.clone(),
_ => return,
};
if !is_use_hook_name(&hook_name) {
@ -399,50 +395,47 @@ impl<'a> Traverse<'a> for ReactRefresh<'a> {
}
if !is_builtin_hook(&hook_name) {
let (binding_name, hook_name) = match &call_expr.callee {
Expression::Identifier(ident) => (ident.name.clone(), None),
callee @ match_member_expression!(Expression) => {
let member_expr = callee.to_member_expression();
match member_expr.object() {
Expression::Identifier(ident) => {
(ident.name.clone(), Some(hook_name.clone()))
}
_ => unreachable!(),
let (binding_name, is_member_expression) = match &call_expr.callee {
Expression::Identifier(ident) => (Some(ident.name.clone()), false),
Expression::StaticMemberExpression(member) => {
if let Expression::Identifier(object) = member.get_first_object() {
(Some(object.name.clone()), true)
} else {
(None, false)
}
}
_ => unreachable!(),
};
let callees = self.non_builtin_hooks_callee.entry(current_scope_id).or_default();
callees.push(
ctx.scopes()
.find_binding(
ctx.scopes().get_parent_id(ctx.current_scope_id()).unwrap(),
binding_name.as_str(),
)
.map(|symbol_id| {
let ident = ctx.create_reference_id(
SPAN,
binding_name.clone(),
Some(symbol_id),
ReferenceFlags::Read,
);
let mut expr = self.ctx.ast.expression_from_identifier_reference(ident);
if let Some(hook_name) = hook_name {
// binding_name.hook_name
expr = Expression::from(self.ctx.ast.member_expression_static(
if let Some(binding_name) = binding_name {
self.non_builtin_hooks_callee.entry(current_scope_id).or_default().push(
ctx.scopes()
.find_binding(
ctx.scopes().get_parent_id(ctx.current_scope_id()).unwrap(),
binding_name.as_str(),
)
.map(|symbol_id| {
let ident = ctx.create_reference_id(
SPAN,
expr,
self.ctx.ast.identifier_name(SPAN, hook_name),
false,
));
}
expr
}),
);
binding_name,
Some(symbol_id),
ReferenceFlags::Read,
);
let mut expr = self.ctx.ast.expression_from_identifier_reference(ident);
if is_member_expression {
// binding_name.hook_name
expr = Expression::from(self.ctx.ast.member_expression_static(
SPAN,
expr,
self.ctx.ast.identifier_name(SPAN, hook_name.clone()),
false,
));
}
expr
}),
);
}
}
let key = if let Ancestor::VariableDeclaratorInit(declarator) = ctx.parent() {

View file

@ -1,6 +1,6 @@
commit: 3bcfee23
Passed: 18/51
Passed: 19/52
# All Passed:
* babel-plugin-transform-nullish-coalescing-operator
@ -167,7 +167,7 @@ rebuilt : SymbolId(2): []
x Output mismatch
# babel-plugin-transform-react-jsx (4/29)
# babel-plugin-transform-react-jsx (5/30)
* refresh/can-handle-implicit-arrow-returns/input.jsx
Reference flags mismatch:
after transform: ReferenceId(18): ReferenceFlags(Write)

View file

@ -0,0 +1,3 @@
export function Bar () {
A.B.C.useHook()
}

View file

@ -0,0 +1,9 @@
var _s = $RefreshSig$();
export function Bar() {
_s();
A.B.C.useHook();
}
_s(Bar, "useHook{}", true);
_c = Bar;
var _c;
$RefreshReg$(_c, "Bar");