mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(transformer/react): handle refresh_sig and refresh_reg options correctly (#5638)
In previous implementations, even if pass `import.meta.xxxx` into these options this plugin still treats them as `IdentifierReference`
This commit is contained in:
parent
3bf6aaf06a
commit
7b543dffd9
2 changed files with 84 additions and 149 deletions
|
|
@ -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};
|
||||
use oxc_ast::{ast::*, match_expression, match_member_expression, AstBuilder};
|
||||
use oxc_semantic::{Reference, ReferenceFlags, ScopeId, SymbolFlags, SymbolId};
|
||||
use oxc_span::{Atom, GetSpan, SPAN};
|
||||
use oxc_syntax::operator::AssignmentOperator;
|
||||
|
|
@ -13,6 +13,79 @@ use sha1::{Digest, Sha1};
|
|||
use super::options::ReactRefreshOptions;
|
||||
use crate::context::Ctx;
|
||||
|
||||
/// Parse a string into a `RefreshIdentifierResolver` and convert it into an `Expression`
|
||||
#[derive(Debug)]
|
||||
enum RefreshIdentifierResolver<'a> {
|
||||
/// Simple IdentifierReference (e.g. `$RefreshReg$`)
|
||||
Identifier(IdentifierReference<'a>),
|
||||
/// StaticMemberExpression (object, property) (e.g. `window.$RefreshReg$`)
|
||||
Member((IdentifierReference<'a>, IdentifierName<'a>)),
|
||||
/// Used for `import.meta` expression (e.g. `import.meta.$RefreshReg$`)
|
||||
Expression(Expression<'a>),
|
||||
}
|
||||
|
||||
impl<'a> RefreshIdentifierResolver<'a> {
|
||||
/// Parses a string into a RefreshIdentifierResolver
|
||||
pub fn parse(input: &str, ast: AstBuilder<'a>) -> Self {
|
||||
if !input.contains('.') {
|
||||
// Handle simple identifier reference
|
||||
return Self::Identifier(ast.identifier_reference(SPAN, input));
|
||||
}
|
||||
|
||||
let mut parts = input.split('.');
|
||||
let first_part = parts.next().unwrap();
|
||||
|
||||
if first_part == "import" {
|
||||
// Handle import.meta.$RefreshReg$ expression
|
||||
let mut expr = ast.expression_meta_property(
|
||||
SPAN,
|
||||
ast.identifier_name(SPAN, "import"),
|
||||
ast.identifier_name(SPAN, parts.next().unwrap()),
|
||||
);
|
||||
if let Some(property) = parts.next() {
|
||||
expr = Expression::from(ast.member_expression_static(
|
||||
SPAN,
|
||||
expr,
|
||||
ast.identifier_name(SPAN, property),
|
||||
false,
|
||||
));
|
||||
}
|
||||
return Self::Expression(expr);
|
||||
}
|
||||
|
||||
// Handle `window.$RefreshReg$` member expression
|
||||
let object = ast.identifier_reference(SPAN, first_part);
|
||||
let property = ast.identifier_name(SPAN, parts.next().unwrap());
|
||||
Self::Member((object, property))
|
||||
}
|
||||
|
||||
/// Converts the RefreshIdentifierResolver into an Expression
|
||||
pub fn to_expression(&self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
|
||||
match self {
|
||||
Self::Identifier(ident) => {
|
||||
let ident = ident.clone();
|
||||
let reference_id =
|
||||
ctx.create_unbound_reference(ident.name.to_compact_str(), ReferenceFlags::Read);
|
||||
ident.reference_id.set(Some(reference_id));
|
||||
ctx.ast.expression_from_identifier_reference(ident)
|
||||
}
|
||||
Self::Member((ident, property)) => {
|
||||
let ident = ident.clone();
|
||||
let reference_id =
|
||||
ctx.create_unbound_reference(ident.name.to_compact_str(), ReferenceFlags::Read);
|
||||
ident.reference_id.set(Some(reference_id));
|
||||
Expression::from(ctx.ast.member_expression_static(
|
||||
SPAN,
|
||||
ctx.ast.expression_from_identifier_reference(ident),
|
||||
property.clone(),
|
||||
false,
|
||||
))
|
||||
}
|
||||
Self::Expression(expr) => expr.clone_in(ctx.ast.allocator),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// React Fast Refresh
|
||||
///
|
||||
/// Transform React functional components to integrate Fast Refresh.
|
||||
|
|
@ -22,11 +95,12 @@ use crate::context::Ctx;
|
|||
/// * <https://github.com/facebook/react/issues/16604#issuecomment-528663101>
|
||||
/// * <https://github.com/facebook/react/blob/main/packages/react-refresh/src/ReactFreshBabelPlugin.js>
|
||||
pub struct ReactRefresh<'a> {
|
||||
refresh_reg: Atom<'a>,
|
||||
refresh_sig: Atom<'a>,
|
||||
refresh_reg: RefreshIdentifierResolver<'a>,
|
||||
refresh_sig: RefreshIdentifierResolver<'a>,
|
||||
emit_full_signatures: bool,
|
||||
registrations: Vec<(SymbolId, Atom<'a>)>,
|
||||
ctx: Ctx<'a>,
|
||||
// States
|
||||
registrations: Vec<(SymbolId, Atom<'a>)>,
|
||||
signature_declarator_items: Vec<oxc_allocator::Vec<'a, VariableDeclarator<'a>>>,
|
||||
/// Used to wrap call expression with signature.
|
||||
/// (eg: hoc(() => {}) -> _s1(hoc(_s1(() => {}))))
|
||||
|
|
@ -39,10 +113,9 @@ pub struct ReactRefresh<'a> {
|
|||
|
||||
impl<'a> ReactRefresh<'a> {
|
||||
pub fn new(options: &ReactRefreshOptions, ctx: Ctx<'a>) -> Self {
|
||||
// TODO: refresh_reg and refresh_sig need to support MemberExpression
|
||||
Self {
|
||||
refresh_reg: ctx.ast.atom(&options.refresh_reg),
|
||||
refresh_sig: ctx.ast.atom(&options.refresh_sig),
|
||||
refresh_reg: RefreshIdentifierResolver::parse(&options.refresh_reg, ctx.ast),
|
||||
refresh_sig: RefreshIdentifierResolver::parse(&options.refresh_sig, ctx.ast),
|
||||
emit_full_signatures: options.emit_full_signatures,
|
||||
signature_declarator_items: Vec::new(),
|
||||
registrations: Vec::default(),
|
||||
|
|
@ -99,13 +172,7 @@ impl<'a> Traverse<'a> for ReactRefresh<'a> {
|
|||
),
|
||||
);
|
||||
|
||||
let refresh_reg_ident = ctx.create_reference_id(
|
||||
SPAN,
|
||||
self.refresh_reg.clone(),
|
||||
Some(symbol_id),
|
||||
ReferenceFlags::Read,
|
||||
);
|
||||
let callee = ctx.ast.expression_from_identifier_reference(refresh_reg_ident);
|
||||
let callee = self.refresh_reg.to_expression(ctx);
|
||||
let mut arguments = ctx.ast.vec_with_capacity(2);
|
||||
arguments.push(ctx.ast.argument_expression(
|
||||
Self::create_identifier_reference_from_binding_identifier(&binding_identifier, ctx),
|
||||
|
|
@ -628,13 +695,6 @@ impl<'a> ReactRefresh<'a> {
|
|||
symbol_id: Cell::new(Some(symbol_id)),
|
||||
};
|
||||
|
||||
let sig_identifier_reference = ctx.create_reference_id(
|
||||
SPAN,
|
||||
self.refresh_sig.clone(),
|
||||
Some(symbol_id),
|
||||
ReferenceFlags::Read,
|
||||
);
|
||||
|
||||
// _s();
|
||||
let call_expression = ctx.ast.statement_expression(
|
||||
SPAN,
|
||||
|
|
@ -660,7 +720,7 @@ impl<'a> ReactRefresh<'a> {
|
|||
),
|
||||
Some(ctx.ast.expression_call(
|
||||
SPAN,
|
||||
ctx.ast.expression_from_identifier_reference(sig_identifier_reference.clone()),
|
||||
self.refresh_sig.to_expression(ctx),
|
||||
Option::<TSTypeParameterInstantiation>::None,
|
||||
ctx.ast.vec(),
|
||||
false,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
commit: 3bcfee23
|
||||
|
||||
Passed: 17/51
|
||||
Passed: 18/51
|
||||
|
||||
# All Passed:
|
||||
* babel-plugin-transform-nullish-coalescing-operator
|
||||
|
|
@ -167,80 +167,11 @@ rebuilt : SymbolId(2): []
|
|||
x Output mismatch
|
||||
|
||||
|
||||
# babel-plugin-transform-react-jsx (3/29)
|
||||
# babel-plugin-transform-react-jsx (4/29)
|
||||
* refresh/can-handle-implicit-arrow-returns/input.jsx
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(9): [ReferenceId(23), ReferenceId(24), ReferenceId(25)]
|
||||
rebuilt : SymbolId(0): [ReferenceId(6), ReferenceId(7)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(10): [ReferenceId(26), ReferenceId(27), ReferenceId(29)]
|
||||
rebuilt : SymbolId(1): [ReferenceId(10), ReferenceId(13)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(11): [ReferenceId(30), ReferenceId(31), ReferenceId(32)]
|
||||
rebuilt : SymbolId(2): [ReferenceId(18), ReferenceId(19)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(12): [ReferenceId(33), ReferenceId(34), ReferenceId(36)]
|
||||
rebuilt : SymbolId(3): [ReferenceId(22), ReferenceId(25)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(13): [ReferenceId(37), ReferenceId(38), ReferenceId(39), ReferenceId(40)]
|
||||
rebuilt : SymbolId(4): [ReferenceId(29), ReferenceId(32), ReferenceId(33)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(14): [ReferenceId(41), ReferenceId(42), ReferenceId(44)]
|
||||
rebuilt : SymbolId(5): [ReferenceId(38), ReferenceId(41)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(4): [ReferenceId(14), ReferenceId(45), ReferenceId(46)]
|
||||
rebuilt : SymbolId(10): [ReferenceId(15), ReferenceId(46)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(5): [ReferenceId(16), ReferenceId(47), ReferenceId(48)]
|
||||
rebuilt : SymbolId(11): [ReferenceId(27), ReferenceId(48)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(6): [ReferenceId(18), ReferenceId(49), ReferenceId(50)]
|
||||
rebuilt : SymbolId(12): [ReferenceId(31), ReferenceId(50)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(7): [ReferenceId(19), ReferenceId(51), ReferenceId(52)]
|
||||
rebuilt : SymbolId(13): [ReferenceId(36), ReferenceId(52)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(8): [ReferenceId(21), ReferenceId(53), ReferenceId(54)]
|
||||
rebuilt : SymbolId(14): [ReferenceId(43), ReferenceId(54)]
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(23): Some("_s")
|
||||
rebuilt : ReferenceId(0): None
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(26): Some("_s2")
|
||||
rebuilt : ReferenceId(1): None
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(30): Some("_s3")
|
||||
rebuilt : ReferenceId(2): None
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(33): Some("_s4")
|
||||
rebuilt : ReferenceId(3): None
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(37): Some("_s5")
|
||||
rebuilt : ReferenceId(4): None
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(41): Some("_s6")
|
||||
rebuilt : ReferenceId(5): None
|
||||
Reference flags mismatch:
|
||||
after transform: ReferenceId(18): ReferenceFlags(Write)
|
||||
rebuilt : ReferenceId(31): ReferenceFlags(Read | Write)
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(45): Some("_c")
|
||||
rebuilt : ReferenceId(45): None
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(47): Some("_c2")
|
||||
rebuilt : ReferenceId(47): None
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(49): Some("_c3")
|
||||
rebuilt : ReferenceId(49): None
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(51): Some("_c4")
|
||||
rebuilt : ReferenceId(51): None
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(53): Some("_c5")
|
||||
rebuilt : ReferenceId(53): None
|
||||
Unresolved references mismatch:
|
||||
after transform: ["X", "memo", "module", "useContext"]
|
||||
rebuilt : ["$RefreshReg$", "$RefreshSig$", "X", "memo", "module", "useContext"]
|
||||
|
||||
* refresh/does-not-consider-require-like-methods-to-be-hocs/input.jsx
|
||||
x Output mismatch
|
||||
|
|
@ -255,15 +186,6 @@ rebuilt : ScopeId(1): []
|
|||
Symbol scope ID mismatch:
|
||||
after transform: SymbolId(1): ScopeId(1)
|
||||
rebuilt : SymbolId(0): ScopeId(0)
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(1): [ReferenceId(3), ReferenceId(4), ReferenceId(5)]
|
||||
rebuilt : SymbolId(0): [ReferenceId(2), ReferenceId(3)]
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(3): Some("_s")
|
||||
rebuilt : ReferenceId(1): None
|
||||
Unresolved references mismatch:
|
||||
after transform: ["item", "useFoo"]
|
||||
rebuilt : ["$RefreshSig$", "item", "useFoo"]
|
||||
|
||||
* refresh/does-not-transform-it-because-it-is-not-used-in-the-AST/input.jsx
|
||||
x Output mismatch
|
||||
|
|
@ -295,53 +217,6 @@ x Output mismatch
|
|||
* refresh/registers-identifiers-used-in-jsx-at-definition-site/input.jsx
|
||||
x Output mismatch
|
||||
|
||||
* refresh/registers-identifiers-used-in-react-create-element-at-definition-site/input.jsx
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(13): [ReferenceId(33), ReferenceId(47), ReferenceId(48)]
|
||||
rebuilt : SymbolId(13): [ReferenceId(2), ReferenceId(48)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(14): [ReferenceId(35), ReferenceId(49), ReferenceId(50)]
|
||||
rebuilt : SymbolId(14): [ReferenceId(5), ReferenceId(50)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(15): [ReferenceId(37), ReferenceId(51), ReferenceId(52)]
|
||||
rebuilt : SymbolId(15): [ReferenceId(8), ReferenceId(52)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(16): [ReferenceId(39), ReferenceId(53), ReferenceId(54)]
|
||||
rebuilt : SymbolId(16): [ReferenceId(12), ReferenceId(54)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(17): [ReferenceId(41), ReferenceId(55), ReferenceId(56)]
|
||||
rebuilt : SymbolId(17): [ReferenceId(35), ReferenceId(56)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(18): [ReferenceId(43), ReferenceId(57), ReferenceId(58)]
|
||||
rebuilt : SymbolId(18): [ReferenceId(41), ReferenceId(58)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(19): [ReferenceId(45), ReferenceId(59), ReferenceId(60)]
|
||||
rebuilt : SymbolId(19): [ReferenceId(45), ReferenceId(60)]
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(47): Some("_c")
|
||||
rebuilt : ReferenceId(47): None
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(49): Some("_c2")
|
||||
rebuilt : ReferenceId(49): None
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(51): Some("_c3")
|
||||
rebuilt : ReferenceId(51): None
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(53): Some("_c4")
|
||||
rebuilt : ReferenceId(53): None
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(55): Some("_c5")
|
||||
rebuilt : ReferenceId(55): None
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(57): Some("_c6")
|
||||
rebuilt : ReferenceId(57): None
|
||||
Reference symbol mismatch:
|
||||
after transform: ReferenceId(59): Some("_c7")
|
||||
rebuilt : ReferenceId(59): None
|
||||
Unresolved references mismatch:
|
||||
after transform: ["React", "funny", "hoc", "jsx", "styled", "wow"]
|
||||
rebuilt : ["$RefreshReg$", "React", "funny", "hoc", "jsx", "styled", "wow"]
|
||||
|
||||
* refresh/registers-likely-hocs-with-inline-functions-1/input.jsx
|
||||
x Output mismatch
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue