From 9d93037eb2fccfe3426d4aedcd0e77958f7258f1 Mon Sep 17 00:00:00 2001 From: Carlos Nogueira Date: Fri, 20 Mar 2026 16:19:57 +0000 Subject: [PATCH] Fix hyphenated attributes names in babel plugin getContent step --- .../src/actions/rum/index.ts | 9 +- .../test/plugin.test.ts | 83 +++++++++++++++++++ 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/packages/react-native-babel-plugin/src/actions/rum/index.ts b/packages/react-native-babel-plugin/src/actions/rum/index.ts index aeb4c0932..c347fdd30 100644 --- a/packages/react-native-babel-plugin/src/actions/rum/index.ts +++ b/packages/react-native-babel-plugin/src/actions/rum/index.ts @@ -251,9 +251,12 @@ function jsxChildToRuntimeCall( t.isJSXAttribute(attr) ) .map(attr => { - const key = t.isJSXIdentifier(attr.name) - ? t.identifier(attr.name.name) - : t.stringLiteral(getNodeName(t, attr.name) || ''); + const key = + t.isJSXIdentifier(attr.name) && + t.isValidIdentifier(attr.name.name) + ? t.identifier(attr.name.name) + : t.stringLiteral(getNodeName(t, attr.name) || ''); + let value: Babel.types.Expression; if (!attr.value) { value = t.booleanLiteral(true); diff --git a/packages/react-native-babel-plugin/test/plugin.test.ts b/packages/react-native-babel-plugin/test/plugin.test.ts index 6860a361e..2a1ca9575 100644 --- a/packages/react-native-babel-plugin/test/plugin.test.ts +++ b/packages/react-native-babel-plugin/test/plugin.test.ts @@ -1679,3 +1679,86 @@ describe('Babel plugin: wrap interaction handlers for RUM ( with memoization )', expect(output).toContain('Card.Title'); }); }); + +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*\(\)\s*=>\s*\{[\s\S]*?return\s+([\s\S]*?);\s*\}/ + ); + return match ? match[1] : ''; + } + + it('should quote hyphenated attribute names in getContent child elements to produce valid JS', () => { + const input = ` + import { TouchableOpacity, View, Text } from 'react-native'; + + function MyComponent() { + return ( + {}}> + + Hello + + + ); + } + `; + + const output = transformCode(input); + const getContent = extractGetContent(output); + // Inside getContent, the generated _jsx call must use "aria-hidden" (quoted string) + // not aria-hidden (bare identifier) because aria-hidden is not a valid JS identifier. + // Bare identifiers with hyphens cause Hermes parse errors: ':' expected in property initialization + expect(getContent).toContain('"aria-hidden"'); + expect(getContent).not.toMatch(/\baria-hidden\b(?!":)/); + }); + + it('should quote multiple hyphenated attribute names in getContent child elements', () => { + const input = ` + import { TouchableOpacity, View, Text } from 'react-native'; + + function MyComponent() { + return ( + {}}> + + Hello + + + ); + } + `; + + const output = transformCode(input); + const getContent = extractGetContent(output); + expect(getContent).toContain('"aria-hidden"'); + expect(getContent).toContain('"aria-label"'); + expect(getContent).toContain('"data-testid"'); + }); + + it('should not quote valid JS identifier attribute names in getContent child elements', () => { + const input = ` + import { TouchableOpacity, View, Text } from 'react-native'; + + function MyComponent() { + return ( + {}}> + + Hello + + + ); + } + `; + + const output = transformCode(input); + const getContent = extractGetContent(output); + // Valid JS identifiers like "style" and "accessible" should remain bare (unquoted) + expect(getContent).toMatch(/\bstyle:/); + expect(getContent).toMatch(/\baccessible:/); + // They should NOT be quoted as string literals + expect(getContent).not.toContain('"style"'); + expect(getContent).not.toContain('"accessible"'); + }); +});