Skip to content

Architecture

Nik edited this page Aug 5, 2026 · 1 revision

Architecture — what is generated, and from what

Everything the registry publishes is derived from one input: the meta.json sidecar next to each component. One command regenerates all of it.

npm run registry:build

The chain

registry:build runs these in order, and the order matters — later scripts read earlier scripts' output:

Step Script Writes
1 scripts/build-index.ts registry/index.tsx — the lazy demo map, derived from the folder structure (a folder is only registered if it has a demo.tsx).
2 scripts/build-registry.ts registry.json from the meta.json sidecars, then shells out to npx shadcn build, which writes public/r/<name>.json.
3 scripts/build-llms.ts public/llms.txt, public/llms-full.txt.
4 scripts/build-autoplay.ts lib/autoplay.generated.json, lib/card-frame.generated.json.
5 scripts/build-contributors.ts lib/contributors.generated.json (slug to GitHub login, from git history).
6 scripts/build-posters.ts poster frames under public/posters.
7 scripts/build-mcp-snapshot.ts mcp/data/registry-snapshot.json. Requires registry.json and public/llms-full.txt to already exist, and throws naming the missing one if run standalone.
8 scripts/build-readme.ts rewrites the counts inside README.md, in place.
9 scripts/build-status.ts lib/status.generated.json. Runs last because it measures the output of the steps above; running it earlier measures a stale build.
10 cli/scripts/build-cli-snapshot.mjs cli/data/registry-index.json, assembled from registry.json + public/llms.txt.

The chain runs automatically before dev, typecheck, verify and build (predev / pretypecheck / preverify in package.json, and build is literally registry:build && next build). A fresh clone needs no extra step.

Never hand-edit generated output

registry.json, everything under public/r/, public/llms*.txt, lib/*.generated.json, registry/index.tsx, mcp/data/**, cli/data/** and the marker spans in README.md are all build output. Editing them does nothing durable: the next build overwrites the edit. A change belongs in the source it was generated from — usually a meta.json sidecar, occasionally the component source (prop signatures are scanned out of component.tsx), or the generator script itself.

scripts/test-source-invariants.ts scans only app/**, lib/** and registry/** for exactly this reason: a finding in build output is the same finding reported twice.

Committed vs. not

Most generated files are gitignored, because every branch that adds a component regenerates all of them, so committing them guarantees merge conflicts in files nobody wrote. registry/index.tsx was the historical worst case — it used to be hand-maintained, one line per component.

Two exceptions, verified against .gitignore and git ls-files:

  • registry.json is committed. The generator is deterministic (a sorted sidecar scan, no timestamps), so regenerating it produces no diff. It is committed because automated registry-directory discovery scans public repos for that filename.
  • lib/component-order.json is committed and is deliberately not in the registry:build chain. It is derived from git history, and a Vercel shallow clone cannot be trusted to produce it, so it is a committed snapshot regenerated by hand with npm run order:build. build-registry.ts prints a warning naming every component missing from it; a missing entry sorts that component last under the catalog's "Newest" view instead of first.

public/llms.txt and public/llms-full.txt are gitignored, despite AGENTS.md currently saying otherwise. .gitignore lists both and git ls-files returns neither.

Warnings that are not failures — and why

build-registry.ts warns rather than fails in two places, both annotated in the source:

  • A meta.json whose name does not match its folder. build-registry.ts keys items by meta.name while build-index.ts keys demos by folder name, so a mismatch ships an installable /r/<name>.json whose preview has no demo — installable but unpreviewable.
  • A component missing from lib/component-order.json.

Both are warnings so an in-flight batch is not blocked. The first carries an explicit upgrade note to become a hard failure once the tree is clean.

Origin

lib/registry-origin.ts is the single source of the public origin, read by both the build scripts and the client-side showcase, so install commands in llms.txt, registry.json and the site can never disagree. It defaults to https://design.helpmarq.com and is overridable with NEXT_PUBLIC_REGISTRY_ORIGIN. The default is a real default rather than an env-only value on purpose: the env var is only set on Vercel, so a fallback pointing anywhere else would bake the wrong URL into every artifact built off platform. It also trims and treats an empty string as unset.

Consumers of the generated output

  • registry.json / public/r/** — the shadcn CLI, and any tool that expects a standard shadcn registry index.
  • public/llms.txt — one fetch returns the whole catalog as plain text. llms-full.txt adds the hand-written behavioural description per component.
  • mcp/data/registry-snapshot.json — baked into the published MCP package, because once installed from npm the server has no repo to read. Regenerated by mcp/'s own prepack before every publish. Tools: search_components, list_components, get_component, list_categories, install_command, get_conventions.
  • cli/data/registry-index.json — the CLI's offline fallback when it cannot reach the network and has no usable cache. CLI-owned: it reads registry.json and public/llms.txt directly and does not depend on mcp/ in any way.

Clone this wiki locally