Repository navigation
fix(react,vue): a tag imported from a package renders no project namesake - #2475
Merged
Merged
Conversation
…sake A JSX tag the file imports from a package (antd's `<Button>`, react-router's `<Link>`, `@mui/material/Typography`'s default) renders that package's component, which the index does not hold. `jsxChild` took a project node of the same name anyway: a unique name without looking at the file's imports at all, and a repeated one by language and order, because a package import never resolves to a file. On SigNoz, antd and @signozhq/ui `<Button>`s rendered `export const Button = styled(Link)` from a 404 page's styles. - `jsxChild`: a component the file declares itself still comes first. Then a tag the file imports from a package binds nothing: the import's specifier passes `ctx.isOutOfRepoImport`, the test the name matcher applies to calls and values (no project file answers it, and a package.json on the file's way to the root declares it). An alias that reads like a package (`components/Modal`), an undeclared bare specifier and a workspace package keep the old rules. - Vue templates (`vueTemplateEdges`'s `resolve`): the same rule for a PascalCase or kebab-case tag, a handler, and the composable a destructured handler comes from. A package-imported tag no longer falls back to a Nuxt auto-imported component either. Fresh before/after indexes of 35 repos on main b635dd4: 6,550 jsx-render edges removed, and one added: a same-file component (refine's win95 `AddButton`) that the 30-children cap had crowded out. A script that reads each parent's import and the package.json chain, independent of the resolver, confirms every removed edge is a tag imported from a declared package. Nodes, refs, files and every other edge are unchanged, and 13 React and 5 Vue repos are byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…skara-1be179 # Conflicts: # CHANGELOG.md
colbymchenry
added a commit
that referenced
this pull request
Oct 10, 2026
…onent (#2476) The jsx-render pass read a tag's name only where whitespace, `/` or `>` ended it, so a generic component's tag, whose type arguments follow its name, was never read: outline's `<PaginatedList<Document> items={…} />` and excalidraw's `<DropdownMenuItemContentRadio<"contain" | "overlap">` left the parent with no edge to the component it renders. The tag pattern now also ends a name at `<`, as a lookahead, so that `<` still opens the next match, which `opensTag` (#2442) reads as a type argument, not a tag. This lands after the package-import gate for JSX tags (#2475). On main alone the same change also linked 5 package tags to project namesakes by name (refine's react95 `<Select<number>>`, antd `<Table<IPost>>` and `<Form<ILoginForm>>`; signoz's antd `<Form<…>>` ×2 to a styled `Form`), which the gate declines. Fresh before/after indexes with the gate as the base: 22 jsx-render edges added and nothing else changed (outline 10 to `PaginatedList`, signoz 8, excalidraw 2, mastodon 2). Each target is what the file's import or own declaration binds, checked with the TypeScript checker with module resolution. refine-examples, bulletproof-react, mantis, proshop_mern, next-saas-starter and create-t3-turbo are byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
inphase1993-max
approved these changes
Oct 10, 2026
inphase1993-max
approved these changes
Oct 10, 2026
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.
What
A JSX tag the file imports from a package (antd's
<Button>, react-router's<Link>,@mui/material/Typography's default) renders that package's component, which the index does not hold.jsxChildtook a project node of the same name anyway: a unique name without looking at the file's imports at all, and a repeated one by language and order, because a package import never resolves to a file. On SigNoz, antd and @signozhq/ui<Button>s renderedexport const Button = styled(Link)from a 404 page's styles.jsxChild: a component the file declares itself still comes first. Then a tag the file imports from a package binds nothing: the import's specifier passesctx.isOutOfRepoImport, the test the name matcher applies to calls and values (no project file answers it, and a package.json on the file's way to the root declares it). An alias that reads like a package (components/Modal), an undeclared bare specifier and a workspace package keep the old rules.vueTemplateEdges'sresolve): the same rule for a PascalCase or kebab-case tag, a handler, and the composable a destructured handler comes from. A package-imported tag no longer falls back to a Nuxt auto-imported component either.Tests
__tests__/jsx-package-imports.test.ts, 5 cases:Validation
Fresh before/after indexes of 35 repos on main b635dd4: 6,550 jsx-render edges removed, and one added: a same-file component (refine's win95
AddButton) that the 30-children cap had crowded out. A script that reads each parent's import and the package.json chain, independent of the resolver, confirms every removed edge is a tag imported from a declared package. Nodes, refs, files and every other edge are unchanged, and 13 React and 5 Vue repos are byte-identical.Checked again while stacking #2476 on this commit:
npx tsc --noEmitis clean, and this branch plus #2476 passes 554/554 tests across 28 JSX, React, Vue, router, dead-code and framework files, including this PR'sjsx-package-imports.test.ts.Merged with main, and re-run there
Main had gained #2428, #2452, #2439, #2432 and #2440 since this commit's base (cd97bb3). Before the merge,
origin/main(c0f45c7) was merged into this branch (711e436). OnlyCHANGELOG.mdconflicted; both sides' bullets were kept, main's first. The gate's source and test patch is unchanged by the merge (samegit patch-id), and squash 3418399 is tree-identical to 711e436.Re-run on that tree against main c0f45c7, fresh indexes of 10 repos:
No node, ref, file or other edge changed. Where both runs measured the same repo, the counts equal this PR's original A/B on b635dd4. A second, independent check (the TypeScript checker with module resolution and each app's tsconfig
paths, following re-exports) classifies all 1,210 removals in SigNoz, mantis, outline, bulletproof-react and mastodon as tags imported from a package. On the merged tree,npx tsc --noEmitis clean and 639/639 tests pass across 32 files, including the tests of the main commits merged in.Per-repo results of the original A/B (35 repos, main b635dd4)
Added after the merge by the session that wrote the commit. Fresh
codegraph init -yindexes on both arms, with the same native kernel, compared withscripts/dump-graph.mjs. Each count is jsx-render edges removed / added. A script that reads the parent file's import and the package.json chain graded every removal as a tag imported from a declared package.Notes on these results:
@refinedev/*in refine;@signozhq/uiin SigNoz;@mui/materialin cord-field, berry, mantis, chickadee and matx;"*"path alias, so they change only on a main that has fix(js,ts): a catch-all or node_modules path alias no longer makes package imports look local #2437.VideoClubPageCreateTitlehas 33 tags, and main stopped at the 30-children cap before the file's ownAddButton. react95's<Separator>no longer takes a slot, soAddButtonnow fits.@acme/uitags, which come from a workspace package.ui. That is the repo's own sibling package (modules/ui):modules/ext/package.jsonlists it as a dependency, but the resolver maps it into the repo.Not in this PR
import Modal from 'components/Modal', whose default export isCustomModal), barrels and forwarding indexes. Those are fix(react): a JSX tag renders what the file imports under that name #2451's.importedJsxChild'sif (!module) return undefined;becomesreturn ctx.isOutOfRepoImport?.(mapping.source, file, language) ? null : undefined;;jsxChildgoes;importedFromPackagestays, for the Vue resolver.matchFunctionRef), such as outline'sLinkfrom react-router-dom passed as a value. Another session is changing that inname-matcher.ts.const { toggle } = useFullscreen()and@click="toggle", the handler is still matched to a projecttoggleby name. That needs a local-binding rule, like fix(react): a type argument or a name the component binds itself renders no JSX child #2442's for JSX, not an import rule.🤖 Generated with Claude Code