From 427cd5fef32cd77b8f472233814e607823b3e0b9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 5 Mar 2026 13:52:39 +0000
Subject: [PATCH 1/2] Initial plan
From c0021dafb852f62019230f09a0ebe7b07eea0141 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 5 Mar 2026 14:06:06 +0000
Subject: [PATCH 2/2] fix(formatjs): Fix overly-aggressive JSX evaluation
breaking valid JSX outside formatjs calls
Co-authored-by: arantespp <16626980+arantespp@users.noreply.github.com>
---
packages/formatjs/__tests__/wasm.test.ts | 44 ++++++++++++++++++++++++
packages/formatjs/transform/src/lib.rs | 27 +++++++++++++--
2 files changed, 68 insertions(+), 3 deletions(-)
diff --git a/packages/formatjs/__tests__/wasm.test.ts b/packages/formatjs/__tests__/wasm.test.ts
index 29780795a..985eb9fc1 100644
--- a/packages/formatjs/__tests__/wasm.test.ts
+++ b/packages/formatjs/__tests__/wasm.test.ts
@@ -553,6 +553,50 @@ describe("formatjs swc plugin", () => {
expect(code).toMatchSnapshot();
});
+ it("should not break on JSX outside formatjs calls", async () => {
+ const input = `
+ import React from 'react';
+
+ const Loading = () =>
Loading...
;
+
+ function App() {
+ return (
+ }>
+ Content
+
+ );
+ }
+ `;
+
+ const output = await transformCode(input);
+
+ // Build should succeed; no formatjs ids should be generated
+ expect(output).toBeTruthy();
+ expect(output).not.toMatch(/id:/);
+ // The original JSX structure should be preserved
+ expect(output).toMatch(/React\.Suspense/);
+ expect(output).toMatch(/Loading/);
+ });
+
+ it("should not break on conditional JSX rendering outside formatjs calls", async () => {
+ const input = `
+ import React from 'react';
+
+ function App({ show }) {
+ return show ? Hello
: World;
+ }
+ `;
+
+ const output = await transformCode(input);
+
+ // Build should succeed; no formatjs ids should be generated
+ expect(output).toBeTruthy();
+ expect(output).not.toMatch(/id:/);
+ // The original JSX structure should be preserved
+ expect(output).toMatch(/Hello/);
+ expect(output).toMatch(/World/);
+ });
+
it("should generate same id even if description is an template literal string", async () => {
const input1 = `
import { FormattedMessage } from 'react-intl';
diff --git a/packages/formatjs/transform/src/lib.rs b/packages/formatjs/transform/src/lib.rs
index 42424290c..66678792a 100644
--- a/packages/formatjs/transform/src/lib.rs
+++ b/packages/formatjs/transform/src/lib.rs
@@ -94,6 +94,14 @@ impl MessageDescriptorExtractor for JSXAttrOrSpread {
Some(name.sym.to_string())
}
};
+ // Only evaluate expressions for known formatjs attribute names to avoid
+ // spurious "must be statically evaluate-able" errors on unrelated attributes.
+ if !matches!(
+ key.as_deref(),
+ Some("id") | Some("defaultMessage") | Some("description")
+ ) {
+ return None;
+ }
let value = match value {
JSXAttrValue::Str(s) => Some(MessageDescriptionValue::Str(
s.value.as_str().expect("non-utf8 string").to_string(),
@@ -161,6 +169,14 @@ impl MessageDescriptorExtractor for PropOrSpread {
None
}
};
+ // Only evaluate expressions for known formatjs prop names to avoid
+ // spurious "must be statically evaluate-able" errors on unrelated props.
+ if !matches!(
+ key.as_deref(),
+ Some("id") | Some("defaultMessage") | Some("description")
+ ) {
+ return None;
+ }
let value = match &*key_value.value {
Expr::Object(obj) => Some(MessageDescriptionValue::Obj(obj.clone())),
expr => {
@@ -978,10 +994,15 @@ impl<'a, C: Clone + Comments, S: SourceMapper> VisitMut for FormatJSVisitor<'a,
let name = &jsx_opening_elem.name;
- if let JSXElementName::Ident(ident) = name {
- if !self.component_names.contains(&*ident.sym) {
- return;
+ match name {
+ JSXElementName::Ident(ident) => {
+ if !self.component_names.contains(&*ident.sym) {
+ return;
+ }
}
+ // Member expressions (e.g. React.Suspense) and namespaced names are never
+ // formatjs components, so skip processing their attributes entirely.
+ _ => return,
}
let mut descriptor = self.create_message_descriptor_from_extractor(&jsx_opening_elem.attrs);