diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts index 452aa0ce329d..75e2db0266f2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -3409,19 +3409,19 @@ function lowerJsxElementName( const exprLoc = exprNode.loc ?? GeneratedSource; if (exprPath.isJSXIdentifier()) { const tag: string = exprPath.node.name; - if (tag.match(/^[A-Z]/)) { + if (tag.match(/^[a-z]/)) { + return { + kind: 'BuiltinTag', + name: tag, + loc: exprLoc, + }; + } else { const kind = getLoadKind(builder, exprPath); return lowerValueToTemporary(builder, { kind: kind, place: lowerIdentifier(builder, exprPath), loc: exprLoc, }); - } else { - return { - kind: 'BuiltinTag', - name: tag, - loc: exprLoc, - }; } } else if (exprPath.isJSXMemberExpression()) { return lowerJsxMemberExpression(builder, exprPath); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-underscore-prefixed-tag-is-component.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-underscore-prefixed-tag-is-component.expect.md new file mode 100644 index 000000000000..b4e7b84bacdf --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-underscore-prefixed-tag-is-component.expect.md @@ -0,0 +1,58 @@ + +## Input + +```javascript +import {Stringify} from 'shared-runtime'; + +// Repro for #36601: an underscore-prefixed JSX tag (`<_Bar>`) is a component +// reference, not a host/builtin element. Before the fix, `lowerJsxElementName` +// used `/^[A-Z]/` to detect components, so `_Bar` (which is not A-Z) was lowered +// as a BuiltinTag — emitting the literal string tag "_Bar" instead of loading +// the `_Bar` binding. The compiled output below must reference the `_Bar` +// identifier (component), not a string tag. +function Foo({_Bar}: {_Bar: React.ComponentType<{children: React.ReactNode}>}) { + return <_Bar>ok; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{_Bar: Stringify}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { Stringify } from "shared-runtime"; + +// Repro for #36601: an underscore-prefixed JSX tag (`<_Bar>`) is a component +// reference, not a host/builtin element. Before the fix, `lowerJsxElementName` +// used `/^[A-Z]/` to detect components, so `_Bar` (which is not A-Z) was lowered +// as a BuiltinTag — emitting the literal string tag "_Bar" instead of loading +// the `_Bar` binding. The compiled output below must reference the `_Bar` +// identifier (component), not a string tag. +function Foo(t0) { + const $ = _c(2); + const { _Bar } = t0; + let t1; + if ($[0] !== _Bar) { + t1 = <_Bar>ok; + $[0] = _Bar; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{ _Bar: Stringify }], +}; + +``` + +### Eval output +(kind: ok)