Fix #1200: Quote hyphenated JSX attribute names in babel plugin getContent#1203
Fix #1200: Quote hyphenated JSX attribute names in babel plugin getContent#1203
Conversation
Root cause: jsxChildToRuntimeCall used t.identifier() for JSX attribute
names, producing unquoted object keys. Hyphenated names like aria-hidden
or bg-$backgroundDefault became invalid JS identifiers, causing Hermes
parse errors ("':' expected in property initialization").
Changes:
- Use t.stringLiteral() for all attribute keys in jsxChildToRuntimeCall
- This matches the existing pattern in tap.ts and is semantically
identical ({foo: v} === {"foo": v} in JavaScript)
- Add tests for hyphenated attribute names in getContent child elements
Tests: 3 unit
Performance: SUCCESS
There was a problem hiding this comment.
Pull request overview
This PR fixes a Hermes compilation error caused by the React Native Babel plugin generating invalid JS object keys inside the getContent closure when JSX attributes are hyphenated (e.g., aria-hidden, data-testid, bg-$backgroundDefault).
Changes:
- Update
jsxChildToRuntimeCallto always emit JSX attribute keys as string literals (quoted) when building the props object forgetContent. - Add unit tests covering hyphenated attributes, multiple hyphenated attributes, and consistency for non-hyphenated attributes.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/react-native-babel-plugin/src/actions/rum/index.ts | Always uses t.stringLiteral(...) for JSX attribute keys when generating runtime calls for getContent, ensuring valid JS for hyphenated names. |
| packages/react-native-babel-plugin/test/plugin.test.ts | Adds tests asserting hyphenated attribute keys are quoted in the generated getContent code path. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| describe('Babel plugin: hyphenated JSX attribute names in getContent', () => { | ||
| function extractGetContent(output: string | null | undefined): string { | ||
| if (!output) return ''; | ||
| const match = output.match(/"getContent": \(\) => \{[\s\S]*?return ([\s\S]*?);\s*\}/); |
There was a problem hiding this comment.
Good catch — loosened the regex to tolerate whitespace variations around structural tokens (:, =>, (), {, return) in fb40e48. The helper still scopes assertions to the getContent body (important to avoid matching the outer JSX), but is now resilient to formatting changes.
Loosen the regex in the test helper to tolerate whitespace variations
around structural tokens (`:`, `=>`, `()`, `{`, `return`), addressing
PR review feedback about brittleness to codegen formatting changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add braces around single-line if block (curly rule) - Wrap long regex across multiple lines (prettier rule) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a Babel plugin codegen bug where hyphenated JSX attribute names inside the generated getContent closure were emitted as bare object keys (invalid JS identifiers), causing Hermes parse failures. It aligns the RUM plugin’s JSX-to-runtime conversion with the already-safe quoting approach used elsewhere.
Changes:
- Always emit JSX attribute keys as string literals in
jsxChildToRuntimeCallto support hyphenated names (e.g.,aria-hidden,data-testid,bg-$...). - Add unit tests covering hyphenated attributes in
getContentgeneration (single, multiple, and consistency for non-hyphenated attrs).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/react-native-babel-plugin/src/actions/rum/index.ts | Changes JSX attribute object key emission to always use quoted string keys. |
| packages/react-native-babel-plugin/test/plugin.test.ts | Adds tests asserting quoted attribute keys in the getContent closure output. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| return ''; | ||
| } | ||
| const match = output.match( | ||
| /"getContent"\s*:\s*\(\)\s*=>\s*\{[\s\S]*?return\s+([\s\S]*?);\s*\}/ |
| const key = t.isJSXIdentifier(attr.name) | ||
| ? t.identifier(attr.name.name) | ||
| : t.stringLiteral(getNodeName(t, attr.name) || ''); | ||
| const key = t.stringLiteral(getNodeName(t, attr.name) || ''); |
There was a problem hiding this comment.
While the suggested fix works, we should not be quoting every key if not needed, I would do something like this instead:
const key =
t.isJSXIdentifier(attr.name) &&
t.isValidIdentifier(attr.name.name)
? t.identifier(attr.name.name)
: t.stringLiteral(getNodeName(t, attr.name) || '');
Issue
Fixes #1200: Babel plugin: error with unescaped content when using useContent: true
GitHub Issue: #1200
Root Cause
jsxChildToRuntimeCallin the babel plugin usedt.identifier()for JSX attribute names when generating thegetContentclosure. This produces unquoted object keys in the generated JavaScript. While valid for simple names likestyleoraccessible, hyphenated attribute names likearia-hidden,aria-label,data-testid, orbg-$backgroundDefaultbecome invalid JavaScript identifiers — causing Hermes to fail with':' expected in property initialization.Changes
jsxChildToRuntimeCallfrom conditionalt.identifier()/t.stringLiteral()to always uset.stringLiteral()for all attribute keystap.tsand is semantically identical in JavaScript ({foo: v}==={"foo": v})Files Changed
packages/react-native-babel-plugin/src/actions/rum/index.tst.stringLiteral()for all attribute keys (line 250)packages/react-native-babel-plugin/test/plugin.test.tsTests
Regression & Impact Analysis
Runtime chain
The
getContentclosure flows through:wrapRumAction()→getTargetName()→__ddExtractText(jsx(...), [])→ returnsstring[]used as RUM action name →ddRum.addAction()→ native SDK bridge → Datadog intake.The change only affects how attribute keys are quoted in the transpiled source code. At runtime,
{style: v}and{"style": v}produce the exact same JavaScript object —__ddExtractTextwalks the React element tree by accessingprops.children,props.label,props.title, andprops.textvia standard property access, which is identical for both forms.Stakeholder impact
__ddExtractTextruntimegetContentgetContentImpact on existing RUM events and queries
None. Two cases:
__ddExtractTextreturns the same text. RUM action names are unchanged. No dashboards, monitors, or queries are affected.Consistency
tap.tsin the same package already usest.stringLiteral()for all object keys. This change alignsjsxChildToRuntimeCallwith that established pattern.