From d0bb9be51e78dafa62a29f24076372faad4608da Mon Sep 17 00:00:00 2001 From: Colby Mchenry Date: Wed, 14 Oct 2026 20:39:08 +0000 Subject: [PATCH] fix(react): a component a file declares itself as a value is what its JSX renders (#2420) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A PascalCase name that a .tsx/.jsx file declares as a module-level value is that declaration wherever the file uses it: a JSX tag, a route's element, a layout or a TSX type. Its initializer decides what it renders, in order: - the component of the module its lazy loader loads, or the export the loader picks, in either branch of a conditional; - the function it wraps (`observer(function X() {…})`, `forwardRef(Inner) as {…}`); - else the declaration itself. Before, the lookup by kind skipped the value and took a lone same-named component anywhere, often in another app of the repository. - Pattern 1 asks declaredComponent about every PascalCase reference, not only route references. layoutComponent and the jsx-render synthesizer's jsxChild ask it too. - valueLoader reads a value's loader with loaderValue (#2480). - defaultExportedName reads `export default observer(Login)`. - lazyValueModules records the modules a file's lazy values load as synthesis inputs, so a sync that changes one of them redraws the tag. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + __tests__/jsx-child-disambiguation.test.ts | 295 +++++++++++++++++++ __tests__/react-router.test.ts | 51 ++++ __tests__/sync-lazy-route-value.test.ts | 3 +- docs/design/framework-coverage.md | 25 +- src/resolution/callback-synthesizer.ts | 38 ++- src/resolution/frameworks/react.ts | 321 ++++++++++++++++++--- 7 files changed, 681 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43922549..9d00b7b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -150,6 +150,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - In JavaScript and TypeScript, a default import now links to what the module's `export default` statement exports, wherever the statement sits in the file. Before, a module that declares its default, like `export default function Vans() {…}` or `export default class Store {…}`, had its default import linked to the first function it exports instead: in a React Router data-router app, where a page exports its `loader` or `action` above the page component, `import Vans, { loader as vansLoader } from './pages/Vans'` made the route `/vans` and every use of `Vans` lead to the loader. A component file that also exports a styled or memoized component no longer hands its default import to that component, and an anonymous default, like `export default function () {…}` or `export default () => …`, is no longer linked to a function exported beside it or nested inside it. A module loaded with `require('./x').default` is read the same way. Re-index JavaScript and TypeScript projects after upgrading. - In React and Vue, a tag for a component the file imports from a package, like `