Conversation
- Add shadcn-converter.ts for SEED → shadcn format conversion - Generate /r/ directory with shadcn-compatible JSON files - Support `npx shadcn@latest add` command for SEED components - Handle innerDependencies → registryDependencies conversion - Remove "use client" directive (shadcn CLI adds it automatically) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
📝 WalkthroughWalkthrough기존 레지스트리 생성 흐름을 유지하면서, 수집된 항목들을 shadcn 형식으로 변환해 Changes
Sequence Diagram(s)sequenceDiagram
participant Gen as generate-registry.ts
participant Coll as Registry Collector
participant Conv as shadcn-converter
participant FS as File System
Gen->>Coll: 모든 레지스트리 항목 수집
Coll-->>Gen: items 맵, registryIdMap 반환
loop 각 항목
Gen->>Conv: convertToShadcnItem(item, {registryId, baseUrl})
Conv-->>Gen: ShadcnRegistryItem
Gen->>FS: write /public/r/{itemId}.json
end
Gen->>Conv: convertToShadcnRegistry(registries, {name, homepage, baseUrl})
Conv-->>Gen: registry.json 구조
Gen->>FS: write /public/r/registry.json
Gen->>Gen: 로그 출력 (완료, 경로, 항목수)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying seed-design-v3 with
|
| Latest commit: |
baf483c
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://28ef0306.seed-design.pages.dev |
| Branch Preview URL: | https://feat-shadcn-registry.seed-design.pages.dev |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@docs/scripts/generate-registry.ts`:
- Around line 96-105: The code silently overwrites items when item.id collides
across registries; update the loop that builds allShadcnItems and registryIdMap
to detect duplicates: inside the for (const { index, items } of registries) ...
for (const item of items) block, check if allShadcnItems.has(item.id) and if so
either throw a clear error (including item.id, existing
registryIdMap.get(item.id) and current index.id) to fail the generation, or
apply a deterministic renaming strategy (e.g., prefix or suffix using index.id)
and use the new unique id for both allShadcnItems.set and registryIdMap.set;
ensure whichever approach you pick is applied consistently so duplicate ids
cannot silently overwrite existing entries.
In `@docs/scripts/shadcn-converter.ts`:
- Around line 169-184: convertInnerDependencies currently ignores
registryId/baseUrl and always emits `@seed/`${itemId}; update it to follow the
documented behavior: for each innerDeps entry (function
convertInnerDependencies, parameter innerDeps, and ConvertOptions options) if
registryId equals the current registry id (options.registry.id) emit just the
itemId (e.g., "loading-indicator"), otherwise emit the full registry URL using
the provided base URL (e.g., `${options.baseUrl}/r/lib/${registryId}.json`) for
that registry's dependency; ensure you still return undefined for empty input
and preserve the final return type string[] | undefined.
🧹 Nitpick comments (2)
docs/scripts/shadcn-converter.ts (2)
139-160: 불필요한 분기 제거를 권장합니다.
두 분기 모두 동일한 객체를 반환하므로 가독성만 떨어집니다.♻️ 제안 변경
- // registry:page와 registry:file은 target 필수 - if (fileType === "registry:page" || fileType === "registry:file") { - return { - path: buildFilePath(snippet.path, registryId), - type: fileType, - content: removeUseClient(snippet.content), - target: buildTargetPath(snippet.path), - }; - } - - return { - path: buildFilePath(snippet.path, registryId), - type: fileType, - content: removeUseClient(snippet.content), - target: buildTargetPath(snippet.path), - }; + return { + path: buildFilePath(snippet.path, registryId), + type: fileType, + content: removeUseClient(snippet.content), + target: buildTargetPath(snippet.path), + };
197-226: 메타 빌더 중복 제거를 권장합니다.
동일 로직이 두 번 반복됩니다.♻️ 제안 변경
-function buildMeta(item: GeneratedRegistryItem): Record<string, unknown> | undefined { +function buildMetaBase( + item: { deprecated?: boolean; hideFromCLICatalog?: boolean }, +): Record<string, unknown> | undefined { const meta: Record<string, unknown> = {}; if (item.deprecated) { meta.deprecated = true; } if (item.hideFromCLICatalog) { meta.hideFromCLICatalog = true; } return Object.keys(meta).length > 0 ? meta : undefined; } + +function buildMeta(item: GeneratedRegistryItem): Record<string, unknown> | undefined { + return buildMetaBase(item); +} @@ function buildMetaFromIndexItem( item: GeneratedRegistry["items"][number], ): Record<string, unknown> | undefined { - const meta: Record<string, unknown> = {}; - - if (item.deprecated) { - meta.deprecated = true; - } - if (item.hideFromCLICatalog) { - meta.hideFromCLICatalog = true; - } - - return Object.keys(meta).length > 0 ? meta : undefined; + return buildMetaBase(item); }
| // 모든 아이템을 수집하여 플랫 구조로 저장 | ||
| const allShadcnItems: Map<string, GeneratedRegistryItem> = new Map(); | ||
| const registryIdMap: Map<string, string> = new Map(); // itemId -> registryId | ||
|
|
||
| for (const { index, items } of registries) { | ||
| for (const item of items) { | ||
| allShadcnItems.set(item.id, item); | ||
| registryIdMap.set(item.id, index.id); | ||
| } | ||
| } |
There was a problem hiding this comment.
아이템 ID 충돌 시 조용히 덮어쓰는 문제가 있습니다.
item.id가 레지스트리 간 중복되면 이전 항목이 소실되어 잘못된 JSON이 생성됩니다. 중복을 감지해 실패시키거나 네이밍 규칙을 변경해야 합니다.
🐛 제안 변경
for (const { index, items } of registries) {
for (const item of items) {
+ if (allShadcnItems.has(item.id)) {
+ throw new Error(
+ `Duplicate registry item id: ${item.id} (registries: ${registryIdMap.get(item.id)} and ${index.id})`,
+ );
+ }
allShadcnItems.set(item.id, item);
registryIdMap.set(item.id, index.id);
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 모든 아이템을 수집하여 플랫 구조로 저장 | |
| const allShadcnItems: Map<string, GeneratedRegistryItem> = new Map(); | |
| const registryIdMap: Map<string, string> = new Map(); // itemId -> registryId | |
| for (const { index, items } of registries) { | |
| for (const item of items) { | |
| allShadcnItems.set(item.id, item); | |
| registryIdMap.set(item.id, index.id); | |
| } | |
| } | |
| // 모든 아이템을 수집하여 플랫 구조로 저장 | |
| const allShadcnItems: Map<string, GeneratedRegistryItem> = new Map(); | |
| const registryIdMap: Map<string, string> = new Map(); // itemId -> registryId | |
| for (const { index, items } of registries) { | |
| for (const item of items) { | |
| if (allShadcnItems.has(item.id)) { | |
| throw new Error( | |
| `Duplicate registry item id: ${item.id} (registries: ${registryIdMap.get(item.id)} and ${index.id})`, | |
| ); | |
| } | |
| allShadcnItems.set(item.id, item); | |
| registryIdMap.set(item.id, index.id); | |
| } | |
| } |
🤖 Prompt for AI Agents
In `@docs/scripts/generate-registry.ts` around lines 96 - 105, The code silently
overwrites items when item.id collides across registries; update the loop that
builds allShadcnItems and registryIdMap to detect duplicates: inside the for
(const { index, items } of registries) ... for (const item of items) block,
check if allShadcnItems.has(item.id) and if so either throw a clear error
(including item.id, existing registryIdMap.get(item.id) and current index.id) to
fail the generation, or apply a deterministic renaming strategy (e.g., prefix or
suffix using index.id) and use the new unique id for both allShadcnItems.set and
registryIdMap.set; ensure whichever approach you pick is applied consistently so
duplicate ids cannot silently overwrite existing entries.
| function convertInnerDependencies( | ||
| innerDeps: GeneratedRegistryItem["innerDependencies"], | ||
| options: ConvertOptions, | ||
| ): string[] | undefined { | ||
| if (!innerDeps || innerDeps.length === 0) return undefined; | ||
|
|
||
| const result: string[] = []; | ||
|
|
||
| for (const { registryId, itemIds } of innerDeps) { | ||
| for (const itemId of itemIds) { | ||
| result.push(`@seed/${itemId}`); | ||
| } | ||
| } | ||
|
|
||
| return result.length > 0 ? result : undefined; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
fd shadcn-converter.tsRepository: daangn/seed-design
Length of output: 94
🏁 Script executed:
cat -n docs/scripts/shadcn-converter.ts | head -200 | tail -100Repository: daangn/seed-design
Length of output: 3379
🏁 Script executed:
# Get the full context of the function and surrounding code
cat -n docs/scripts/shadcn-converter.ts | sed -n '150,210p'Repository: daangn/seed-design
Length of output: 1981
🏁 Script executed:
# Search for ConvertOptions interface definition
rg "interface ConvertOptions|type ConvertOptions" -A 10 docs/scripts/Repository: daangn/seed-design
Length of output: 661
🏁 Script executed:
# Search for references to registryDependencies in the codebase
rg "registryDependencies|innerDependencies" docs/scripts/ -B 2 -A 2Repository: daangn/seed-design
Length of output: 3046
🏁 Script executed:
# Search for how baseUrl and registryId are used elsewhere
rg "baseUrl|registryId" docs/scripts/shadcn-converter.ts -B 2 -A 2Repository: daangn/seed-design
Length of output: 2497
🌐 Web query:
shadcn UI registryDependencies format cross-registry dependencies URL
💡 Result:
In a registry-item.json, registryDependencies is an array where each entry can be:
- A shadcn/ui built-in item name (same registry):
"button","input", etc. - A namespaced registry item (cross-registry by namespace):
"@acme/input-form" - A full URL to a remote registry item JSON (cross-registry by URL):
"https://example.com/r/editor.json"
Example:
{
"registryDependencies": [
"button",
"@acme/input-form",
"https://example.com/r/editor.json"
]
}The shadcn CLI will automatically resolve remote (URL) registry dependencies. [1]
Sources: [1] (ui.shadcn.com)
Citations:
registryDependencies 변환 로직이 함수 문서 및 shadcn 표준과 불일치합니다.
함수의 공식 문서(163-168줄)에서는 다음을 명시합니다:
- 동일 registry 내 의존성: 항목명만 (예:
"loading-indicator") - 다른 registry 의존성: 전체 URL (예:
"https://seed-design.io/r/lib/manner-temp-level.json")
그러나 현재 구현(177-181줄)은 registryId와 baseUrl을 무시하고 모든 의존성을 @seed/${itemId} 형식으로 생성하므로, 다른 레지스트리 의존성이 정상적으로 해석되지 않습니다. 함수의 문서화된 동작에 맞추도록 수정이 필요합니다.
🐛 제안 변경
function convertInnerDependencies(
innerDeps: GeneratedRegistryItem["innerDependencies"],
options: ConvertOptions,
): string[] | undefined {
if (!innerDeps || innerDeps.length === 0) return undefined;
const result: string[] = [];
for (const { registryId, itemIds } of innerDeps) {
for (const itemId of itemIds) {
- result.push(`@seed/${itemId}`);
+ if (registryId === options.registryId) {
+ result.push(itemId);
+ } else {
+ result.push(`${options.baseUrl}/r/${itemId}.json`);
+ }
}
}
return result.length > 0 ? result : undefined;
}🤖 Prompt for AI Agents
In `@docs/scripts/shadcn-converter.ts` around lines 169 - 184,
convertInnerDependencies currently ignores registryId/baseUrl and always emits
`@seed/`${itemId}; update it to follow the documented behavior: for each innerDeps
entry (function convertInnerDependencies, parameter innerDeps, and
ConvertOptions options) if registryId equals the current registry id
(options.registry.id) emit just the itemId (e.g., "loading-indicator"),
otherwise emit the full registry URL using the provided base URL (e.g.,
`${options.baseUrl}/r/lib/${registryId}.json`) for that registry's dependency;
ensure you still return undefined for empty input and preserve the final return
type string[] | undefined.
Summary
npx shadcn@latest add)로 SEED 컴포넌트 설치 지원/r/디렉토리에 shadcn 호환 JSON 파일 생성Usage
Test plan
bun generate:registry실행 확인🤖 Generated with Claude Code
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.