feat(blocks): first ds-block-empty-state + contract-test target resolution - #9
Merged
Merged
Conversation
…ution
Three concerns bundled in one commit because they are tightly coupled:
1. New block: ds-block-empty-state (category: onboarding)
- Single-file block that composes ds-empty + ds-button + ds-colored-badge
via registryDependencies (per the shadcn-registry-adoption plan's
Blocks extension point convention).
- First entry in BLOCK_REGISTRY. Showcase aggregator wires EmptyState
and pulls source from SOURCES.blocks.
- 4-surface pattern (showcase tree, consumer tree, registry.json,
audit drift entry) same as components.
- API: title, description, icon, badge, action - all optional, sensible
defaults so the showcase preview renders with no props.
2. fix(contract-test): simulate shadcn CLI file rename via registry.json targets
- The contract test previously did a flat cpSync(SOURCE_DIR, components/ui)
which kept the registry/base-nova/ds-X/ds-X.tsx layout. Imports like
@/components/ui/ds-empty would resolve to components/ui/ds-empty.tsx
(the rename shadcn CLI applies at install time), not to
components/ui/ds-empty/ds-empty.tsx.
- The old layout happened to work because no consumer file imported
other ds-* components. ds-block-empty-state breaks that - it
composes three ds-* items.
- Fix: read each item's target field from registry.json and copy to
the resolved consumer-relative path. Handles @ui/, @components/,
@lib/, @hooks/, @/ prefixes.
- This makes the contract test accurately simulate what shadcn CLI
does at install time, not just what the source tree looks like.
3. memory: project-level learnings
- New file feedback_registry_deps_coupling.md captures the lesson
from the ds-breadcrumb lucide-react incident (install list in
contract-test.mjs must mirror registry.json dependencies).
- MEMORY.md index updated.
Validation (all green):
- npm run build -w @workspace/registry
- node apps/web/scripts/build-sources.mjs (8 components, 1 block)
- node apps/web/scripts/build-registry.mjs (catalog + 9 items)
- node apps/web/scripts/check-registry-drift.mjs
- cd apps/web && npx tsc --noEmit
- next build (7/7 pages)
- node apps/web/scripts/contract-test.mjs
Co-Authored-By: Claude <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…tion When a block in packages/registry/src/blocks/ imports from @workspace/registry/components/<id> (same package), TypeScript's package.json exports map resolution fails without an explicit rootDir: TS2209 'The project root is ambiguous, but is required to resolve export map entry colored-badge'. The build tsconfig had it (rootDir: ./src) but the typecheck tsconfig did not, so npm run typecheck failed while npm run build worked. Self-references via the workspace package only became relevant with ds-block-empty-state, the first block that composes other ds-* items. Components didn't hit this because they re-export from @workspace/ui (cross-package), not within the registry itself. Co-Authored-By: Claude <noreply@anthropic.com>
BlockEntry previously had no Demo field, so deriveBlockPreview rendered <Block /> with no props. With all block props optional and a default title of "Nothing here yet", the showcase preview tab showed placeholder content instead of a realistic example. Pattern was already in place for components (ComponentEntry has Demo, deriveComponentPreview uses First.Demo). Extended the same to blocks: - BlockEntry gains a required Demo field. - deriveBlockPreview now renders <Demo /> instead of <Block />. - The block author exports a BlockDemo that wraps the block with realistic props (badge, title, description, action). This is parallel to the per-component Demo export convention; it makes the preview tab useful without forcing the block itself to have a no-args default that doubles as marketing. Also regenerated sources.generated.ts (now contains the EmptyStateBlockDemo export) and public/r/registry.json (catalog unchanged at 9 items, but content hashes shift because the showcase source got longer). Validation: drift clean, typecheck clean, next build green (7/7 pages). Co-Authored-By: Claude <noreply@anthropic.com>
Previous commit (7f55aee) wired the Demo export pattern into the aggregator and the /blocks index page, but missed two other places that rendered blocks directly: - apps/web/app/blocks/[category_id]/page.tsx rendered <item.Block /> in the ItemCard preview slot. With the Demo convention, this should be <item.Demo /> so the card shows a realistic example instead of the empty-state default rendering. - apps/web/app/blocks/[category_id]/[block_id]/page.tsx had a hardcoded "rendered block placeholder" string inside the Preview tab. Replaced it with <block.Demo /> so the tab actually renders the block. Components pages already used <component.Demo /> correctly, so the asymmetry was on the blocks side. Co-Authored-By: Claude <noreply@anthropic.com>
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.
Summary
First entry in
BLOCK_REGISTRY(theds-block-*category). Single-file block that composes three existingds-*items viaregistryDependencies. Also fixes a contract-test bug surfaced by the block.What's new
ds-block-empty-stateregistry:blockAPI:
title?,description?,icon?,badge?: { label, color },action?: { label, onClick }. All optional; sensible defaults render with no props.Files
Showcase tree:
packages/registry/src/blocks/empty-state/index.tsxpackages/registry/src/blocks/empty-state/meta.tsapps/web/lib/registry/index.tsx—BLOCK_REGISTRYwired (was[])Consumer tree:
registry/base-nova/ds-block-empty-state/ds-block-empty-state.tsxManifests:
registry.json— entry withtype: registry:block+registryDependencies: [ds-empty, ds-button, ds-colored-badge]docs/registry/audit-2026-07-29.json— drift audit entryapps/web/public/r/ds-block-empty-state.json— generatedapps/web/public/r/registry.json— regenerated (catalog now 9 items)Memory:
.claude/agent-memory/main/feedback_registry_deps_coupling.md— new.claude/agent-memory/main/MEMORY.md— index updatedContract-test fix (bundled)
The block imports
@/components/ui/ds-empty,@/components/ui/ds-button,@/components/ui/ds-colored-badge. The contract test was doing a flatcpSync(SOURCE_DIR, components/ui)that preserved the directory layoutregistry/base-nova/ds-X/ds-X.tsx, which doesn't match what shadcn CLI does at install time (renames tocomponents/ui/ds-X.tsx). The old layout happened to pass because no consumer file imported otherds-*items — the block is the first cross-import.Fix: read each item's
targetfromregistry.jsonand copy to the resolved consumer-relative path (handles@ui/,@components/,@lib/,@hooks/,@/). Now the shim accurately simulates install behavior.Validation
All green:
npm run build -w @workspace/registrynode apps/web/scripts/build-sources.mjs— 8 components, 1 blocknode apps/web/scripts/build-registry.mjs— catalog + 9 itemsnode apps/web/scripts/check-registry-drift.mjs— no driftcd apps/web && npx tsc --noEmitnext build— 7/7 pagesnode apps/web/scripts/contract-test.mjs— consumer-shim tsc OK🤖 Generated with Claude Code