refactor(transformer): remove update_fragment from JSX transform (#3541)

Fix #3464.

Import of fragment has to be done late in order to replicate the order of imports in Babel's output. But do that in a simpler way.
This commit is contained in:
overlookmotel 2024-06-06 03:19:44 +00:00
parent 73b78643f2
commit e4d74ac3ee

View file

@ -504,13 +504,6 @@ impl<'a> ReactJsx<'a> {
let is_development = self.options.development;
let mut arguments = self.ast().new_vec();
let (argument_expr, fragment_needs_update) = match e {
JSXElementOrFragment::Element(e) => {
(self.transform_element_name(&e.opening_element.name, ctx), false)
}
JSXElementOrFragment::Fragment(_) => self.get_fragment(ctx),
};
arguments.push(Argument::from(argument_expr));
// The key prop in `<div key={true} />`
let mut key_prop = None;
@ -623,9 +616,17 @@ impl<'a> ReactJsx<'a> {
}
}
if fragment_needs_update {
self.update_fragment(arguments.first_mut().unwrap(), ctx);
}
// It would be better to push to `arguments` earlier, rather than use `insert`.
// But we have to do it here to replicate the same import order as Babel, in order to pass
// Babel's conformance tests.
// TODO(improve-on-babel): Change this if we can handle differing output in tests.
let argument_expr = match e {
JSXElementOrFragment::Element(e) => {
self.transform_element_name(&e.opening_element.name, ctx)
}
JSXElementOrFragment::Fragment(_) => self.get_fragment(ctx),
};
arguments.insert(0, Argument::from(argument_expr));
// If runtime is automatic that means we always to add `{ .. }` as the second argument even if it's empty
if is_automatic || !properties.is_empty() {
@ -726,56 +727,21 @@ impl<'a> ReactJsx<'a> {
}
}
/// Create fragment expression.
/// `bool` returned is flag for whether identifier is temporary and `update_fragment`
/// needs to be called later.
fn get_fragment(&mut self, ctx: &mut TraverseCtx<'a>) -> (Expression<'a>, bool) {
match &self.bindings {
Bindings::Classic(bindings) => {
let expr = bindings.pragma_frag.create_expression(ctx);
let needs_update = false;
(expr, needs_update)
}
fn get_fragment(&mut self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
match &mut self.bindings {
Bindings::Classic(bindings) => bindings.pragma_frag.create_expression(ctx),
Bindings::AutomaticScript(bindings) => {
let (object_ident, needs_update) = match bindings.require_jsx.as_ref() {
Some(id) => (id.create_read_reference(ctx), false),
None => (create_read_identifier_reference(SPAN, Atom::empty(), None), true),
};
let object_ident = bindings.require_jsx(ctx);
let property_name = Atom::from("Fragment");
let expr = create_static_member_expression(object_ident, property_name, ctx);
(expr, needs_update)
create_static_member_expression(object_ident, property_name, ctx)
}
Bindings::AutomaticModule(bindings) => {
let (ident, needs_update) = match bindings.import_fragment.as_ref() {
Some(id) => (id.create_read_reference(ctx), false),
None => (create_read_identifier_reference(SPAN, Atom::empty(), None), true),
};
let expr = ctx.ast.identifier_reference_expression(ident);
(expr, needs_update)
let ident = bindings.import_fragment(ctx);
ctx.ast.identifier_reference_expression(ident)
}
}
}
fn update_fragment(&mut self, arg: &mut Argument<'a>, ctx: &mut TraverseCtx<'a>) {
let (id, new_id) = match &mut self.bindings {
Bindings::AutomaticScript(bindings) => {
let Argument::StaticMemberExpression(member_expr) = arg else { unreachable!() };
let Expression::Identifier(id) = &mut member_expr.object else {
unreachable!();
};
(id, bindings.require_jsx(ctx))
}
Bindings::AutomaticModule(bindings) => {
let Argument::Identifier(id) = arg else { unreachable!() };
(id, bindings.import_fragment(ctx))
}
Bindings::Classic(_) => unreachable!(),
};
id.name = new_id.name;
id.reference_id = new_id.reference_id;
}
fn get_create_element(
&mut self,
has_key_after_props_spread: bool,