CodeVetter · Repo Unpack · Data flow
shadcn-ui/ui
Input boundaries are CLI args, components.json (cosmiconfig), env files (.env*), and registry item JSON fetched over HTTP(S)/local disk/GitHub. Zod schemas validate all external data. Resolution turns item addresses into URLs via per-registry templates with {name}/{style} placeholders and ${ENV} expansion; resolveRegistryTree recursively merges items+dependencies into one install tree. Updaters then write package.json deps, tailwind config, env files, fonts, transformed source files, and finally CSS. The website's build-registry.mts is the upstream producer of the JSON the CLI consumes.
- Config input: a cosmiconfig explorer (searchPlaces: ['components.json']) loads raw config, rawConfigSchema validates, then resolveConfigPaths resolves tsconfig/jsconfig path aliases (components/ui/lib/hooks/utils) into resolvedPaths — tsconfig load failure throws.
- Item address resolution order in fetchRegistryItems: github: scheme → fetchGitHubRegistryItem; local file → fetchRegistryLocal (with ~ expansion, zod parse); URL → fetchRegistry+parse; '@ns/item' → resolveRegistryItemsFromRegistries; bare name → `styles/{config.style ?? 'new-york-v4'}/{name}.json` under REGISTRY_URL.
- Registry URL building substitutes {name} and {style} placeholders, expands ${ENV_VAR} via expandEnvVars (from AsyncLocalStorage registry env context), appends configured query params, and builds auth headers that are dropped if expansion leaves them empty.
- HTTP fetch path: fetchRegistry → resolveRegistryUrl (v0 /chat/b/ URLs get /json suffix; relative paths prefixed with REGISTRY_URL) → fetchWithProxy (manual redirect loop, SOCKS via ALL_PROXY, EnvHttpProxyAgent for HTTP(S)_PROXY, per-origin header re-scoping) → status-mapped errors (401/403/404/410) → response.json().
- resolveRegistryTree recursively resolves registryDependencies (re-rewriting @namespaced deps through the registries config, erroring RegistryNotConfiguredError when unset) and merges each item's files/cssVars/css/envVars/dependencies/devDependencies/fonts into a single install tree validated by registryResolvedItemsTreeSchema.
- Install pipeline: addComponents → getWorkspaceConfig decides monorepo path (addWorkspaceComponents routes files to ui/hooks/lib workspace targets) vs addProjectComponents; both run resolveAndValidateRegistryTree → massageTreeForFonts → updateDependencies → updateTailwindConfig → updateEnvVars → updateFonts → updateFiles → updateCss (CSS written last so watchers rebuild after files land).
- File writing: updateFiles resolves each file's target path (alias-aware, src-dir aware, --path override), skips identical content via isContentSame, merges .env files instead of overwriting, and runs the transformer chain: transformImport, transformRsc, transformCssVars, transformTwPrefixes, transformIcons, transformMenu, transformAsChild, transformRtl, conditional transformNext (Next 16 middleware), transformFont, transformCleanup.
- Env input boundary: loadEnvFiles loads .env.local, .env.development.local, .env.development, .env from cwd via @dotenvx (no overload) so private-registry ${ENV} placeholders in URLs/headers/params resolve at fetch time.
- components.json write path (init): existing config (or backup) is deepmerged with new config; discovered registries are appended (built-ins filtered out); registry:base config merged last; design settings (menuColor/menuAccent/rtl/iconLibrary) are propagated into each workspace package's components.json; file written with 2-space JSON.
- Registry production: authored sources live in apps/v4/registry/bases/{base,radix,aria} plus registry/styles/style-*.css; build-registry.mts builds STYLE_COMBINATIONS (base × style), invokes the shadcn CLI to export public/r/styles/<style>/*.json, emits ui-rtl variants for nova styles, and formats outputs.
- Dependency writes: updateDependencies resolves the package manager (npm/pnpm/yarn/bun/deno/expo special-cased) and execa's an install of registry-declared dependencies/devDependencies; React 19 + npm triggers a peer-deps warning pointing at ui.shadcn.com/react-19.
- In-process caching: fetchRegistry dedupes in-flight fetches in a module-level Map keyed by url + sha256 of sorted headers; registry headers themselves live in AsyncLocalStorage context set by setRegistryHeaders and cleared via clearRegistryContext in each command's finally.
- Init URL contract: the site's /init endpoint emits a registry:base item whose `config` field the CLI reads via resolveRegistryBaseConfig; the CLI also strips the 'track' param before fetching and force-sets the 'base' param on the first component URL.