Found while implementing #5930, verifying the premise that resolveIcon is "the same resolver ui:button uses". It is the same algorithm, but not the same function.
What is on the tree
packages/components/src/renderers/form/button.tsx (the ui:button registration, namespace ui) does not import resolveIcon. It carries a byte-equivalent reimplementation at module scope — its own toPascalCase, its own iconNameMap containing the single entry Home -> House, and its own index into icons from lucide-react:
letIcon: LucideIcon|null=null;if(schema.icon){consticonName=toPascalCase(schema.icon);constmappedIconName=iconNameMap[iconName]||iconName;Icon=(iconsasany)[mappedIconName]asLucideIcon;}packages/components/src/renderers/action/resolve-icon.ts is the shared version, and the action:* family (action-button, action-menu, action-group, action-icon) plus renderers/complex/data-table.tsx all import it.
Why it matters
Behaviour is identical today — same population, same casing rule, same Home -> House alias, same "unknown name renders nothing" outcome — so nothing is currently mis-rendering. The risk is divergence: an alias added to iconNameMap in resolve-icon.ts (which is exactly how lucide retirements have been absorbed — see #5586, #5622) does not reach ui:button. The copy would keep resolving the retired spelling to nothing while every action:* site started resolving it correctly, and no gate separates them because check:icon-record-names declares both modules as legitimate record readers.
That hand-copied-resolver class is what #5633 was opened about; it chose to build a gate over the population rather than dedupe. This is the dedupe half, still outstanding.
Shape of the repair
Import resolveIcon in form/button.tsx, delete the local toPascalCase and iconNameMap, and drop form/button.tsx from DECLARED_RECORD_READERS in scripts/check-lucide-icon-record-names.mjs — the census rediscovers readers from source on every run and fails on drift in both directions, so that edit is verified rather than asserted.
Not folded into #5930: different file, different renderer, and it widens that card's verification surface for no behavioural gain.
Found while implementing #5930, verifying the premise that
resolveIconis "the same resolverui:buttonuses". It is the same algorithm, but not the same function.What is on the tree
packages/components/src/renderers/form/button.tsx(theui:buttonregistration, namespaceui) does not importresolveIcon. It carries a byte-equivalent reimplementation at module scope — its owntoPascalCase, its owniconNameMapcontaining the single entryHome -> House, and its own index intoiconsfromlucide-react:packages/components/src/renderers/action/resolve-icon.tsis the shared version, and theaction:*family (action-button,action-menu,action-group,action-icon) plusrenderers/complex/data-table.tsxall import it.Why it matters
Behaviour is identical today — same population, same casing rule, same
Home -> Housealias, same "unknown name renders nothing" outcome — so nothing is currently mis-rendering. The risk is divergence: an alias added toiconNameMapinresolve-icon.ts(which is exactly how lucide retirements have been absorbed — see #5586, #5622) does not reachui:button. The copy would keep resolving the retired spelling to nothing while everyaction:*site started resolving it correctly, and no gate separates them becausecheck:icon-record-namesdeclares both modules as legitimate record readers.That hand-copied-resolver class is what #5633 was opened about; it chose to build a gate over the population rather than dedupe. This is the dedupe half, still outstanding.
Shape of the repair
Import
resolveIconinform/button.tsx, delete the localtoPascalCaseandiconNameMap, and dropform/button.tsxfromDECLARED_RECORD_READERSinscripts/check-lucide-icon-record-names.mjs— the census rediscovers readers from source on every run and fails on drift in both directions, so that edit is verified rather than asserted.Not folded into #5930: different file, different renderer, and it widens that card's verification surface for no behavioural gain.