fix(compiler): treat underscore-prefixed JSX tags as components#36703
Open
ousamabenyounes wants to merge 2 commits into
Open
fix(compiler): treat underscore-prefixed JSX tags as components#36703ousamabenyounes wants to merge 2 commits into
ousamabenyounes wants to merge 2 commits into
Conversation
The React Compiler was using a regex check `/^[A-Z]/` to determine whether a JSX tag is a component or a builtin (host) element. This meant that tags prefixed with underscore (e.g., `<_Bar>`) were incorrectly treated as builtin elements, even though the JSX specification states that only tags starting with a lowercase letter should be treated as host elements. This change flips the condition to check for lowercase first letters (`/^[a-z]/`) for builtin tags, treating everything else (uppercase, underscore-prefixed, dollar-prefixed, etc.) as component references, which matches the React JSX transform behavior. Fixes facebook#36601
The fix shipped without a fixture. Adds a snapshot test exercising the facebook#36601 repro: a `<_Bar>` JSX tag must be lowered as a component reference, not a BuiltinTag. RED (fix reverted to `/^[A-Z]/`): the fixture fails — Forget output throws "_Bar is not defined" vs the non-Forget `<div>{"children":"ok"}</div>`, because `_Bar` is emitted as a host string tag. GREEN (fix `/^[a-z]/`): `_Bar` is lowered as a reactive component dependency; snap passes (1 Tests, 1 Passed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #36601
The React Compiler was using a regex check
/^[A-Z]/to determine whether a JSX tag is a component or a builtin (host) element. This meant that tags prefixed with underscore (e.g.,<_Bar>) were incorrectly treated as builtin elements.Per the JSX specification, only tags starting with a lowercase letter (
a-z) should be treated as host/builtin elements. Tags starting with anything else (uppercase letters, underscore, dollar sign, etc.) should be treated as component references.The Bug
Given this input:
The compiler was incorrectly treating
<_Bar>as a builtin tag, which caused the children{() => <div />}to be incorrectly extracted into a separate_tempfunction — silently changing the runtime behavior from what worked fine before React Compiler.The Fix
Changed the condition in
lowerJsxElementName()inBuildHIR.tsfrom:To:
This matches the React JSX transform convention where a JSX tag is a host element only if its name starts with a lowercase ASCII letter.
Testing
Existing test fixtures (
builtin-jsx-tag-lowered-between-mutations,global-jsx-tag-lowered-between-mutations) continue to work correctly since:<div>(lowercase) → still treated as builtin ✓<View>(uppercase) → still treated as component ✓Test verification (RED → GREEN)
Added fixture
compiler/.../fixtures/compiler/jsx-underscore-prefixed-tag-is-component.tsx(+ generated.expect.md).Command:
yarn snap -p '*underscore*'GREEN — with the fix (
/^[a-z]/):_Baris lowered as a component (reactive dependency), sprout eval matches.RED — fix reverted to
/^[A-Z]/:Without the patch,
<_Bar>is lowered as a BuiltinTag (host string), so the compiled output references an undefined_Barand throws at runtime — diverging from the non-compiled reference. The fixture catches exactly this regression.