refactor(web): route all icon imports through one barrel - #7963
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
|
UI consistency: one web file still bypasses the new
import {
BotIcon,
CircleAlertIcon,
PencilRulerIcon,
type LucideIcon,
LockIcon,
LockOpenIcon,
PenLineIcon,
SparklesIcon,
XIcon,
} from "lucide-react";Two consequences that come from lines changed in this PR:
Smallest fix: change that one specifier to } from "~/icons";(Posted as a conversation comment rather than inline because the file is not part of this diff.) Posted via Macroscope — UI Consistency |
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — The UI changes are intended as a pure Lucide re-export refactor, but the PR adds a substantial custom TSX scanner that controls the generated production icon barrel. An unresolved concrete concern indicates JSX attribute expressions can cause later icons to be omitted during regeneration, so the tooling and its bundle impact require human review. You can add or adjust custom eligibility rules. Learn more. |
|
Good catch — fixed in c67ca0c. The codemod's grep had skipped ChatComposer.tsx because the file contains literal NUL bytes in a template string (grep binary detection); the sweep now reads file bytes instead, and a re-scan confirms zero direct lucide-react imports remain outside the barrel. Macroscope's UI Consistency check passes on the latest commit. |
|
Factual check on the Macroscope UI-consistency comment against current head The separate unresolved generator finding is still valid: declaration-level |
4e958a7 to
7e8c6c1
Compare
53f060e to
c0fb01a
Compare
c0fb01a to
d0a4c9c
Compare
collectRuntimeIconNames treated block and line comments inside import
braces as part of member names, so icons:generate and icons:check threw
on valid TypeScript like `import { /* primary action */ XIcon } from
"~/icons"`. Strip comments from the brace contents before splitting
members, with regression coverage for inline, multiline, and trailing
comment forms.
Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 6b9f440. Configure here.
| characters[index] = " "; | ||
| } | ||
| continue; | ||
| } |
There was a problem hiding this comment.
JSX tag state ignores attribute expressions
Medium Severity
jsx-tag never enters attribute {...} expressions and treats every > as the tag closer. Common TSX such as onClick={() => ...} or count={n > 0} drops the scanner into jsx-text, which blanks later ~/icons imports so the generated barrel can omit them.
Reviewed by Cursor Bugbot for commit 6b9f440. Configure here.


What Changed
~/iconsbarrel, and a lint rule blocks directlucide-reactimports.apps/web/scripts/generate-icon-barrel.ts— scansapps/web/srcandapps/web/testfor icon imports, validates runtime names againstlucide-react, and writessrc/icons/index.tsas a pure re-export;--checkverifies freshness without writing.apps/web/src/icons/icon-import-parser.ts— shared import parser + name validation, withicon-imports.test.ts/index.test.tscoverage.lucide-reactto~/icons. No call-site logic changes.vite.config.ts—no-restricted-importsnow forbids root and subpathlucide-reactimports outside the barrel, pointing at the generator.docs/internals/icons.md— maintainer doc for the seam and the two scripts.3b3af3cb4— the parser now strips block and line comments inside import braces (review finding; see Verification).Why
Reproduction:
apps/web/src.import { XIcon } from "lucide-react"— across ~140 files.Actual: no owned icon module, no enforcement; new direct imports could land at any time.
Expected: one module owns interface glyphs, a lint rule keeps every import on it, and regeneration is a single command.
Cause: the web client never grew an icon module or a lint rule for Lucide imports.
Why this owner and approach: a generated pure re-export barrel is the smallest seam that changes nothing at runtime while making
~/iconsthe single plug-in point for any future icon-pack decision; scanning real usage keeps the barrel exactly as large as the app; the lint rule makes the seam the only path, so it cannot rot silently.Refs: no upstream issue — maintenance refactor.
UI Changes
No user-visible change, verified by identical captures. The Settings screen (
/settings/general— the most icon-dense surface: sidebar nav with eight section icons, search, and panel row icons) was captured at the merge-base build and at this head:d22709f75(merge-base of this branch andorigin/main), built the same way as the head build3b3af3cb4prefers-color-schemeResult: SHA-256 identical for all four frame pairs; ImageMagick
compare -metric AEreports 0 of 5,184,000 pixels differ — full window and sidebar crop, in both dark and light. Both appearances are captured even though the claim is appearance-independent (an import reroute cannot depend on theme); dark is shown first and all light media is collapsed below.Fidelity note: an earlier capture round differed by 13 single pixels on text-antialiasing edges (0.00025%); two captures of the same merge-base build differ from each other by the same 13 pixels, so that is capture-time rasterization noise, not the code change. The published frames below are from the byte-identical round. The comparison GIFs look static because the frames are identical — that is the evidence.
Dark mode
Before
After
Before/after comparison GIF
Full-window context, dark mode
Before (merge-base
d22709f75):After (head
3b3af3cb4):Light mode evidence
Before
After
Before/after comparison GIF
Full-window context, light mode
Before (merge-base
d22709f75):After (head
3b3af3cb4):No action sequence is included: the PR changes no interaction, so there is no action to record.
Verification
All results are real, at final head
3b3af3cb4:vp i— exit 0 (fresh worktree install).CI=true vp test run apps/web/src/icons/icon-imports.test.ts apps/web/src/icons/index.test.ts— exit 0; 2 files, 5 tests passed (includes a new regression test for commented imports).CI=true vp test run apps/web/src/components/chat/ComposerStashMenu.test.tsx— exit 0; 1 file, 4 tests passed (smallest existing slice importing the barrel; also covers the file touched by the feat(web): attach PDFs, ZIPs, and other files to a turn #8236 rebase conflict).CI=true vp run --filter @t3tools/web icons:check— exit 0; verified 201 generated exports.CI=true vp run --filter @t3tools/web typecheck— exit 0.CI=true vp lint apps/web/src/icons/icon-import-parser.ts apps/web/src/icons/icon-imports.test.ts— exit 0; no errors.CI=true vp fmt --check apps/web/src/icons/icon-import-parser.ts apps/web/src/icons/icon-imports.test.ts— exit 0./settings/generalcaptured at merge-base and head in dark and light — SHA-256 equal, 0 pixels differ (media above). macOS 26, headless Chrome 151, dev variant, empty isolated state.3b3af3cb4with a regression test; the declaration-level type-import finding was already handled at the current head — evidence replies posted on both threads.Risks and limitations
origin/maintipd22709f75equals the merge-base;git merge-base --is-ancestor origin/main HEADexits 0 — the branch is 0 commits behind base and needed no rebase in this refresh.icons:checkis not a separate CI job. A missing barrel export already fails the required web typecheck; the explicit check catches dead exports and ordering drift during maintenance.Checklist
Original implementation and rebase by GPT-5.6 Sol using the Codex harness in T3 Code. This refresh — the review-finding fix, the pixel-identity media, and this body — by GLM 5.3 using the Claude Code harness in T3 Code.
Note
Route all
apps/weblucide-reactimports through~/iconsbarrelapps/webcomponents to import icons from~/iconsinstead oflucide-react.no-restricted-importsESLint rule in vite.config.ts.vite.config.tsfails on directlucide-reactimports. Developers must runicons:generateto update the barrel before using new icons.Macroscope summarized 6b9f440.
Note
Low Risk
Mechanical import reroute with identity-tested re-exports; main risk is developer friction if someone bypasses
~/iconsor forgets to regenerate the barrel.Overview
All web UI glyphs now import from
~/iconsinstead oflucide-react. The new generated barrel insrc/icons/index.tsis a pure Lucide re-export so runtime components stay the same; ~140 call sites only change their import path.Tooling and enforcement:
scripts/generate-icon-barrel.tsscanssrcandtest, validates names against Lucide, and writes the barrel (icons:generate/icons:check).icon-import-parser.tspowers that scan with tests for comments, JSX, and literals. Root ESLintno-restricted-importsblocks directlucide-reactimports outside the barrel. Maintainer docs describe the workflow.No product behavior change — this is a single seam for future icon-pack swaps and consistent enforcement when adding icons.
Reviewed by Cursor Bugbot for commit 6b9f440. Bugbot is set up for automated code reviews on this repo. Configure here.