Skip to content

Commit 39d3d04

Browse files
committed
Fix hyphenated attributes names in babel plugin getContent step
1 parent 3433cc8 commit 39d3d04

2 files changed

Lines changed: 89 additions & 3 deletions

File tree

packages/react-native-babel-plugin/src/actions/rum/index.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,12 @@ function jsxChildToRuntimeCall(
251251
t.isJSXAttribute(attr)
252252
)
253253
.map(attr => {
254-
const key = t.isJSXIdentifier(attr.name)
255-
? t.identifier(attr.name.name)
256-
: t.stringLiteral(getNodeName(t, attr.name) || '');
254+
const key =
255+
t.isJSXIdentifier(attr.name) &&
256+
t.isValidIdentifier(attr.name.name)
257+
? t.identifier(attr.name.name)
258+
: t.stringLiteral(getNodeName(t, attr.name) || '');
259+
257260
let value: Babel.types.Expression;
258261
if (!attr.value) {
259262
value = t.booleanLiteral(true);

packages/react-native-babel-plugin/test/plugin.test.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,3 +1679,86 @@ describe('Babel plugin: wrap interaction handlers for RUM ( with memoization )',
16791679
expect(output).toContain('Card.Title');
16801680
});
16811681
});
1682+
1683+
describe('Babel plugin: hyphenated JSX attribute names in getContent', () => {
1684+
function extractGetContent(output: string | null | undefined): string {
1685+
if (!output) {
1686+
return '';
1687+
}
1688+
const match = output.match(
1689+
/"getContent"\s*:\s*\(\)\s*=>\s*\{[\s\S]*?return\s+([\s\S]*?);\s*\}/
1690+
);
1691+
return match ? match[1] : '';
1692+
}
1693+
1694+
it('should quote hyphenated attribute names in getContent child elements to produce valid JS', () => {
1695+
const input = `
1696+
import { TouchableOpacity, View, Text } from 'react-native';
1697+
1698+
function MyComponent() {
1699+
return (
1700+
<TouchableOpacity onPress={() => {}}>
1701+
<View aria-hidden>
1702+
<Text>Hello</Text>
1703+
</View>
1704+
</TouchableOpacity>
1705+
);
1706+
}
1707+
`;
1708+
1709+
const output = transformCode(input);
1710+
const getContent = extractGetContent(output);
1711+
// Inside getContent, the generated _jsx call must use "aria-hidden" (quoted string)
1712+
// not aria-hidden (bare identifier) because aria-hidden is not a valid JS identifier.
1713+
// Bare identifiers with hyphens cause Hermes parse errors: ':' expected in property initialization
1714+
expect(getContent).toContain('"aria-hidden"');
1715+
expect(getContent).not.toMatch(/\baria-hidden\b(?!":)/);
1716+
});
1717+
1718+
it('should quote multiple hyphenated attribute names in getContent child elements', () => {
1719+
const input = `
1720+
import { TouchableOpacity, View, Text } from 'react-native';
1721+
1722+
function MyComponent() {
1723+
return (
1724+
<TouchableOpacity onPress={() => {}}>
1725+
<View aria-hidden aria-label="test" data-testid="my-view">
1726+
<Text>Hello</Text>
1727+
</View>
1728+
</TouchableOpacity>
1729+
);
1730+
}
1731+
`;
1732+
1733+
const output = transformCode(input);
1734+
const getContent = extractGetContent(output);
1735+
expect(getContent).toContain('"aria-hidden"');
1736+
expect(getContent).toContain('"aria-label"');
1737+
expect(getContent).toContain('"data-testid"');
1738+
});
1739+
1740+
it('should not quote valid JS identifier attribute names in getContent child elements', () => {
1741+
const input = `
1742+
import { TouchableOpacity, View, Text } from 'react-native';
1743+
1744+
function MyComponent() {
1745+
return (
1746+
<TouchableOpacity onPress={() => {}}>
1747+
<View style={{flex: 1}} accessible>
1748+
<Text>Hello</Text>
1749+
</View>
1750+
</TouchableOpacity>
1751+
);
1752+
}
1753+
`;
1754+
1755+
const output = transformCode(input);
1756+
const getContent = extractGetContent(output);
1757+
// Valid JS identifiers like "style" and "accessible" should remain bare (unquoted)
1758+
expect(getContent).toMatch(/\bstyle:/);
1759+
expect(getContent).toMatch(/\baccessible:/);
1760+
// They should NOT be quoted as string literals
1761+
expect(getContent).not.toContain('"style"');
1762+
expect(getContent).not.toContain('"accessible"');
1763+
});
1764+
});

0 commit comments

Comments
 (0)