compare_component
Check component availability and differences between HeroUI v2 and v3 versions to identify migration requirements and changes needed.
Instructions
Show presence/status of a component between v2 and v3, with alias lookup.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| component | Yes |
Implementation Reference
- src/core/migration.ts:377-468 (handler)The `compareComponent` function handles the logic for comparing components between v2 and v3 versions, determining the status (same, renamed, removed, unknown, or compound) and identifying breaking changes, subcomponent mappings, and property changes.
export async function compareComponent(component: string): Promise<ComponentComparisonResult> { const root = process.cwd(); const idx2 = path.join(root, "data", "index", "components.v2.json"); const idx3 = path.join(root, "data", "index", "components.v3.json"); let list2: any[] = []; let list3: any[] = []; try { list2 = JSON.parse(await fs.readFile(idx2, 'utf8')); } catch { } try { list3 = JSON.parse(await fs.readFile(idx3, 'utf8')); } catch { } // normalize helper removes hyphen/underscore and lowercases const normalize = (s: string) => s.toLowerCase().replace(/[-_]/g, ''); const targetNorm = normalize(component); const find = (list: any[]) => list.find((e) => { if (normalize(e.slug) === targetNorm) return true; if (e.aliases && e.aliases.some((a: string) => normalize(a) === targetNorm)) return true; return false; }); const e2 = find(list2); const e3 = find(list3); const exists2 = !!e2; const exists3 = !!e3; // build alias list using resolver if available const aliases: string[] = []; try { const { resolveAlias } = await import("../knowledge/aliases.js"); const res = resolveAlias(component); if (res.alias) aliases.push(res.alias); if (res.canonical && res.canonical !== component) aliases.push(res.canonical); } catch { } // determine status let status: ComponentComparisonResult['status']; if (exists2 && exists3) { if (normalize(e2.slug) === normalize(e3.slug)) { status = 'same'; } else { status = 'renamed'; } } else if (exists2 && !exists3) { status = 'removed'; } else if (!exists2 && exists3) { status = 'unknown'; } else { status = 'unknown'; } const breakingChanges: string[] = []; if (KNOWN_V2_IMPORTS[component]) { breakingChanges.push(KNOWN_V2_IMPORTS[component]); } // subcomponent mappings: any map entry starting with component name (case-insensitive) but not equal const subcomponentMappings: Array<{ legacy: string; replacement: string; note?: string }> = []; for (const [key, val] of Object.entries(KNOWN_V2_IMPORTS)) { if (key.toLowerCase().startsWith(component.toLowerCase()) && key.toLowerCase() !== component.toLowerCase()) { subcomponentMappings.push({ legacy: key, replacement: val, note: val }); } } // prop changes relevant to this component const propChanges: Array<{ prop: string; replacement?: string; removed?: boolean; note: string }> = []; for (const p of KNOWN_V2_PROPS) { if (!p.components || p.components.some((c) => normalize(c) === targetNorm)) { propChanges.push({ prop: p.prop, replacement: p.replacement, removed: p.removed, note: p.note }); } } // if component had subcomponent mappings but no v3 entry, it may have been // folded into a compound API rather than simply removed. We mark as // `compound` only if none of the mappings are just "NOT IN v3" notes. if (status === 'removed' && subcomponentMappings.length > 0) { const onlyNotIn = subcomponentMappings.every(m => /NOT IN v3/i.test(m.note || '')); if (!onlyNotIn) { status = 'compound'; } } return { component, aliases, existsInV2: exists2, existsInV3: exists3, status, breakingChanges, subcomponentMappings, propChanges, sources: [e2?.source || '', e3?.source || ''].filter(Boolean), };