Skip to content

fix(cli): route presets through /init registry endpoint#1764

Merged
zernonia merged 6 commits intodevfrom
fix/preset-registry-init
Apr 11, 2026
Merged

fix(cli): route presets through /init registry endpoint#1764
zernonia merged 6 commits intodevfrom
fix/preset-registry-init

Conversation

@zernonia
Copy link
Copy Markdown
Member

@zernonia zernonia commented Apr 11, 2026

Summary

Fixes #1763. The create/preset flow silently drops most of the design system config — font, theme, radius, menu color, chart palette, and generated CSS vars never reach the generated project. Only a handful of scalar fields were copied to components.json, and even those diverged between the web encoder and CLI decoder.

This PR mirrors shadcn-ui's preset pipeline so presets now deliver the full config.

What changed

Single source of truth — new shadcn-vue/preset subpath export (browser-safe, bit-packed encoder/decoder). Deletes the diverging apps/v4/lib/preset-encoding.ts; web composable + parse-preset-input.ts import from the CLI package.

Server — new Nuxt route apps/v4/server/routes/init.get.ts decodes the preset, validates via designSystemConfigSchema, and returns a full registry:base item via the existing buildRegistryBase (extended to include font, rtl, fontHeading).

CLI — new packages/cli/src/preset/presets.ts with DEFAULT_PRESETS, resolveInitUrl, resolveRegistryBaseConfig. Rewrote init.ts action to inject the /init URL into components[0], fetch the registry:base item, and mergeConfig its config into the final components.json — the same flow shadcn-ui uses.

Sync — CLI BASE_COLORS now matches the web (neutral, stone, zinc, mauve, olive, mist, taupe); FONTS expanded to the full 11 used by the site; rawConfigSchema.menuColor widened to include translucent variants.

Removed — old packages/cli/src/utils/preset-encoding.ts, resolvePreset, and the PRESETS constant (superseded by DEFAULT_PRESETS).

Breaking

  • Old preset codes (byte-format like 4PLVH59KVRVY6) no longer decode. New codes use a version-prefixed bit-packed format (a…). Old codes never applied correctly anyway, so existing users aren't losing working behavior.
  • Named presets renamed from reka-vega/reka-nova/… to vega/nova/… to match shadcn-ui.
  • CLI --font geist--font geist-sans (aligned with web registry font-geist-sans).
  • CLI --base-color gray/slate removed; added mauve/olive/mist/taupe.

Test plan

  • pnpm test in packages/cli — 392 pass, 0 new failures
  • pnpm typecheck in packages/cli — no new errors (unrelated pre-existing only)
  • pnpm build in packages/clidist/preset/index.{js,d.ts} emitted
  • pnpm nuxt typecheck in apps/v4 — no new errors (one fewer than main)
  • Manual end-to-end: bunx shadcn-vue@latest init --preset <code> --template vite on a fresh Vite app, confirm components.json reflects the selected font / base color and that the generated style.css has the correct theme tokens

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Shareable compact preset codes, named presets, preset builder, init/create URLs, and a new server init endpoint.
  • Improvements

    • CLI init accepts preset names, codes, or URLs and merges resolved registry/base config; better config parsing, validation, and clearer errors.
    • Expanded font list and base colors; added translucent menu-color options.
    • Safer preset handling when values are invalid and non-mutating updates.
  • Tests / Tools

    • Added CLI smoke-test script and updated preset-related tests.

zernonia and others added 2 commits April 11, 2026 21:05
Mirror shadcn-ui's preset pipeline so the `create`/preset flow
actually applies font, theme, base color, radius, menu options,
chart palette, and generated CSS vars — not just writes a handful
of strings to components.json.

- Add `shadcn-vue/preset` package subpath (browser-safe bit-packed
  encoder/decoder) as the single source of truth shared between CLI
  and web (deletes the diverging copy in apps/v4/lib)
- Add `packages/cli/src/preset/presets.ts` with `DEFAULT_PRESETS`,
  `resolveInitUrl`, `resolveRegistryBaseConfig`, `promptForPreset`
- Add Nuxt `/init` server route that decodes the preset, validates
  via `designSystemConfigSchema`, and returns a full `registry:base`
  item via the existing `buildRegistryBase`
- Extend `buildRegistryBase` to include `font`, `rtl`, and
  `fontHeading` dependencies in the emitted config
- Rewrite `init.ts` preset handling to inject the init URL into the
  component list, fetch the registry:base config, and `mergeConfig`
  it into the final components.json — following shadcn-ui's flow
- Sync CLI `BASE_COLORS` and `FONTS` constants with the web
  registry (replaces gray/slate with mauve/olive/mist/taupe;
  expands fonts to the full 11 used by the site)
- Widen `rawConfigSchema.menuColor` to include the translucent
  variants used by presets
- Update tests to the new preset naming (`vega`/`nova`/...) and
  drop `resolvePreset` tests (function removed)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 11, 2026

📝 Walkthrough

Walkthrough

Replaces legacy preset encoders with a new bit-packed base62 preset system; adds URL-driven design-system parsing and a /init server route; refactors CLI preset handling (name|code|URL) with DEFAULT_PRESETS and registry resolution; updates registry constants/schemas; and removes old preset-encoding modules.

Changes

Cohort / File(s) Summary
Preset core & exports
packages/cli/src/preset/preset.ts, packages/cli/src/preset/index.ts, packages/cli/package.json, packages/cli/tsdown.config.ts
Introduces a new bit-packed base62 preset implementation (PresetConfig, encode/decode, validation, generators, constants); adds package export and build entry for ./preset.
Preset helpers & CLI prompts
packages/cli/src/preset/presets.ts
Adds DEFAULT_PRESETS, URL builders (resolveCreateUrl, resolveInitUrl), interactive flows (promptForPreset, promptToOpenPresetBuilder), and resolveRegistryBaseConfig to derive registry base config from an init URL.
CLI init command
packages/cli/src/commands/init.ts, packages/cli/test/commands/init.test.ts, packages/cli/scripts/test-preset-init.sh
Removes old resolvePreset; --preset now accepts name
Removed legacy encoders
apps/v4/lib/preset-encoding.ts, packages/cli/src/utils/preset-encoding.ts
Deleted prior deterministic byte-mapping/base62 preset encoders and associated helpers (isEncodedPreset, old decodePreset) in favor of the new preset module.
Frontend parsing & composable updates
apps/v4/lib/parse-design-system-config.ts, apps/v4/lib/parse-preset-input.ts, apps/v4/composables/useDesignSystemSearchParams.ts
Adds parseDesignSystemConfig to build/validate config from search params; parsePresetInput now uses isPresetCode; composable updated to use PresetConfig typing, fallback on undecodable presets, and immutable write of preset updates.
Server init route & registry base builder
apps/v4/server/routes/init.get.ts, apps/v4/registry/config.ts
New Nuxt GET /init route that parses query, builds registry base via buildRegistryBase, validates and returns JSON; buildRegistryBase signature updated to accept rtl and returns font and rtl plus adjusted CSS var/body rules.
Registry constants, API & schemas
packages/cli/src/registry/constants.ts, packages/cli/src/registry/schema.ts, packages/cli/src/registry/api.ts
Adds SHADCN_VUE_URL env override and derived REGISTRY_URL; expands FONTS and BASE_COLORS, removes old PRESETS; expands menuColor enum; API now sources presets from DEFAULT_PRESETS and softens base-color lookup errors.
Tests & expectations
packages/cli/test/utils/registry-api.test.ts
Updated tests to assert DEFAULT_PRESETS keys (no reka- prefix) and adjusted nova preset iconLibrary expectation.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Server as init.get
    participant Parser as parseDesignSystemConfig
    participant Decoder as decodePreset
    participant Registry as buildRegistryBase
    participant Validator as registryItemSchema

    Client->>Server: GET /init?preset=<code>&template=vite
    Server->>Parser: parseDesignSystemConfig(searchParams)
    Parser->>Decoder: decodePreset(code)
    Decoder-->>Parser: PresetConfig|null
    Parser->>Parser: assemble configInput (preset + overrides)
    Parser->>Parser: designSystemConfigSchema.safeParse
    alt parse fail
        Parser-->>Server: { success: false, error }
        Server-->>Client: 400 Bad Request
    else parse success
        Parser-->>Server: { success: true, data }
        Server->>Registry: buildRegistryBase(data)
        Registry-->>Server: registryBase
        Server->>Validator: registryItemSchema.safeParse(registryBase)
        alt validation fail
            Validator-->>Server: errors
            Server-->>Client: 500 Internal Server Error
        else
            Server-->>Client: 200 OK + JSON (Cache-Control)
        end
    end
Loading
sequenceDiagram
    participant User
    participant CLI as init Command
    participant Prompt as promptForPreset
    participant URL as resolveInitUrl
    participant Registry as resolveRegistryBaseConfig
    participant Server as init.get

    User->>CLI: run init --preset [name|code|url]
    CLI->>Prompt: promptForPreset(options)
    Prompt->>User: interactive selection
    User-->>Prompt: select named preset
    Prompt->>URL: resolveInitUrl(selectedPreset)
    URL-->>Prompt: init URL
    Prompt->>Registry: resolveRegistryBaseConfig(initUrl)
    Registry->>Server: fetch registry items for initUrl
    Server-->>Registry: registry items (include registry:base)
    Registry-->>CLI: { registryBaseConfig, installStyleIndex, url }
    CLI->>CLI: merge registryBaseConfig into components.json (respecting CLI rtl)
    CLI-->>User: complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I bit-pack carrots into a base62 tune,

Hopped through presets beneath the moon,
From URL to registry the pathways align,
CLI and server hum—everything fine,
Hop, hop, the presets sparkle in thyme!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(cli): route presets through /init registry endpoint' clearly and specifically describes the main change—implementing preset routing through the /init registry endpoint to fix the broken create/presets flow.
Linked Issues check ✅ Passed The PR fully addresses issue #1763 by implementing the missing preset/create functionality: adds server /init endpoint for preset decoding/validation, introduces browser-safe preset encoder/decoder, rewrites CLI init flow to fetch and merge registry:base config, and ensures all design system config values (fonts, colors, theme, radius, etc.) propagate correctly.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the preset routing: preset encoding/decoding logic, server /init endpoint, CLI preset handling, constants/schema alignment, and related tests. No unrelated refactoring or scope creep detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/preset-registry-init

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@zernonia zernonia changed the base branch from main to dev April 11, 2026 13:16
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 11, 2026

Note

Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 11, 2026

✅ Created PR with unit tests: #1765

Three issues surfaced running the preset flow end-to-end against a
fresh Vite project:

- `designSystemConfigSchema.fontHeading` rejected its own default
  "inherit" (enum was just fontValues). Widen to include "inherit".
- `buildRegistryBase` added `font-${config.font}` to
  registryDependencies, mirroring shadcn-ui — but shadcn-vue does not
  publish font-* registry items. The CLI already handles fonts via
  the local FONTS constant + `getFontImport`, so drop the dep.
- `getRegistryBaseColor` called `handleError` on 404, aborting init
  when newer base colors (mauve/olive/mist/taupe) have no published
  `colors/<name>.json`. Return undefined on RegistryNotFoundError —
  the mapping is only used for inline class color remapping when
  cssVariables is false, so degrading gracefully is safe.

Verified end-to-end: `init --preset <code>` against a Vite project
now writes the correct style, font, baseColor, iconLibrary,
menuAccent, menuColor to components.json AND applies the full
theme, radius, and font import to style.css.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (4)
apps/v4/server/routes/init.get.ts (1)

16-22: Consider limiting error details in 500 response.

The formatted Zod error in data could expose internal schema structure. For a 500 (internal server error), consider logging the full error server-side while returning a generic message to clients.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/v4/server/routes/init.get.ts` around lines 16 - 22, Log the full Zod
error server-side and return a generic client-facing message: keep the existing
check of parseResult.success, but before throwing createError call your server
logger (or console.error) with parseResult.error.format() or the raw error to
capture details for debugging; then throw createError({ statusCode: 500,
statusMessage: 'Invalid registry base item', data: { message: 'Internal server
error' } }) (or omit the detailed data) so the createError payload no longer
exposes parseResult.error.format() to clients; refer to parseResult,
parseResult.error.format(), and createError in init.get.ts when making this
change.
packages/cli/test/commands/init.test.ts (1)

183-194: Consider adding coverage for fontHeading and rtl fields.

DEFAULT_PRESETS includes fontHeading and rtl fields (per the context snippet), but these aren't verified in the test. Adding assertions would ensure complete coverage of the preset structure.

💡 Optional addition
       expect(preset.menuAccent).toBeDefined()
       expect(preset.menuColor).toBeDefined()
       expect(preset.radius).toBeDefined()
+      expect(preset.fontHeading).toBeDefined()
+      expect(preset.rtl).toBeDefined()
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/test/commands/init.test.ts` around lines 183 - 194, The test
"each preset has a complete design system config" iterates over DEFAULT_PRESETS
but misses asserting two fields; update that test (the it block) to also assert
that preset.fontHeading and preset.rtl are defined by adding
expect(preset.fontHeading).toBeDefined() and expect(preset.rtl).toBeDefined()
alongside the existing assertions so every preset field is covered.
packages/cli/src/preset/presets.ts (1)

331-340: Consider adding isUrl validation before new URL().

The isShadcnVueInitUrl function catches exceptions from new URL(), but it could avoid the try/catch overhead by first checking isUrl(url) (which is already imported and re-exported).

♻️ Optional: use isUrl check
 function isShadcnVueInitUrl(url: string) {
+  if (!isUrl(url)) {
+    return false
+  }
   try {
     return new URL(url).pathname === '/init' && url.startsWith(SHADCN_VUE_URL)
   }
   catch {
     return false
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/preset/presets.ts` around lines 331 - 340, Refactor
isShadcnVueInitUrl to call the existing isUrl(url) first and immediately return
false if that check fails, then safely construct new URL(url) and evaluate
pathname === '/init' && url.startsWith(SHADCN_VUE_URL); this removes the need
for the try/catch around new URL(), references the isShadcnVueInitUrl and isUrl
symbols, and preserves the same boolean behavior.
apps/v4/composables/useDesignSystemSearchParams.ts (1)

25-37: Consider aligning chartColor default with DEFAULT_PRESET_CONFIG.

The chartColor default here is hardcoded to 'emerald', while DEFAULT_PRESET_CONFIG in packages/cli/src/preset/preset.ts derives it from PRESET_CHART_COLORS[0] which is 'neutral' (since PRESET_CHART_COLORS = PRESET_THEMES and themes start with 'neutral').

If consistency is desired, consider importing and using DEFAULT_PRESET_CONFIG.chartColor or ensuring both defaults match intentionally.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/v4/composables/useDesignSystemSearchParams.ts` around lines 25 - 37,
PRESET_DEFAULTS currently hardcodes chartColor to 'emerald' which is
inconsistent with DEFAULT_PRESET_CONFIG (which derives chartColor from
PRESET_CHART_COLORS[0]/'neutral'); update PRESET_DEFAULTS to use the shared
default instead of a literal: import and use DEFAULT_PRESET_CONFIG.chartColor
(or derive from PRESET_CHART_COLORS[0] if import is undesirable) and replace the
'emerald' literal in the chartColor field so both defaults stay consistent; the
key symbols to change are PRESET_DEFAULTS and its chartColor entry, and you may
reference DEFAULT_PRESET_CONFIG or PRESET_CHART_COLORS/PRESET_THEMES as the
source of truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/v4/server/routes/init.get.ts`:
- Around line 13-14: buildRegistryBase can throw when the requested base or icon
library is missing, causing an unhandled exception; wrap the call to
buildRegistryBase(result.data) in a try-catch, catch the Error, log/return a
proper 400/422 response (or propagate a controlled error) with a clear message,
and only call registryItemSchema.safeParse(registryBase) when the build
succeeded — update the block around buildRegistryBase, registryBase, parseResult
and registryItemSchema to handle and surface the error instead of allowing a
500.

In `@packages/cli/src/commands/init.ts`:
- Around line 371-384: Wrap the call to resolveRegistryBaseConfig(...) in its
own try-catch so failures during registry/preset resolution are logged with
specific context before delegating to the existing error handling; inside the
catch, include the registry URL (components[0] / cleanUrl) and operation name
(registry/preset config resolution) in a descriptive error message via the same
logging mechanism used elsewhere, then rethrow or pass the original error to
handleError so stack is preserved; ensure resolveRegistryBaseConfig,
options.registryBaseConfig, options.installStyleIndex, options.baseStyle and the
mutated components array are still set only on success.

In `@packages/cli/src/registry/schema.ts`:
- Around line 43-51: Update the preset schema to match the raw config's allowed
menuColor values: change presetSchema.menuColor (the enum used for presets) so
it includes "default-translucent" and "inverted-translucent" in addition to
"default" and "inverted", and ensure its default/optional behavior matches
rawConfigSchema.menuColor so presets can validly specify translucent menu
colors.

---

Nitpick comments:
In `@apps/v4/composables/useDesignSystemSearchParams.ts`:
- Around line 25-37: PRESET_DEFAULTS currently hardcodes chartColor to 'emerald'
which is inconsistent with DEFAULT_PRESET_CONFIG (which derives chartColor from
PRESET_CHART_COLORS[0]/'neutral'); update PRESET_DEFAULTS to use the shared
default instead of a literal: import and use DEFAULT_PRESET_CONFIG.chartColor
(or derive from PRESET_CHART_COLORS[0] if import is undesirable) and replace the
'emerald' literal in the chartColor field so both defaults stay consistent; the
key symbols to change are PRESET_DEFAULTS and its chartColor entry, and you may
reference DEFAULT_PRESET_CONFIG or PRESET_CHART_COLORS/PRESET_THEMES as the
source of truth.

In `@apps/v4/server/routes/init.get.ts`:
- Around line 16-22: Log the full Zod error server-side and return a generic
client-facing message: keep the existing check of parseResult.success, but
before throwing createError call your server logger (or console.error) with
parseResult.error.format() or the raw error to capture details for debugging;
then throw createError({ statusCode: 500, statusMessage: 'Invalid registry base
item', data: { message: 'Internal server error' } }) (or omit the detailed data)
so the createError payload no longer exposes parseResult.error.format() to
clients; refer to parseResult, parseResult.error.format(), and createError in
init.get.ts when making this change.

In `@packages/cli/src/preset/presets.ts`:
- Around line 331-340: Refactor isShadcnVueInitUrl to call the existing
isUrl(url) first and immediately return false if that check fails, then safely
construct new URL(url) and evaluate pathname === '/init' &&
url.startsWith(SHADCN_VUE_URL); this removes the need for the try/catch around
new URL(), references the isShadcnVueInitUrl and isUrl symbols, and preserves
the same boolean behavior.

In `@packages/cli/test/commands/init.test.ts`:
- Around line 183-194: The test "each preset has a complete design system
config" iterates over DEFAULT_PRESETS but misses asserting two fields; update
that test (the it block) to also assert that preset.fontHeading and preset.rtl
are defined by adding expect(preset.fontHeading).toBeDefined() and
expect(preset.rtl).toBeDefined() alongside the existing assertions so every
preset field is covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 71341d94-98bb-428c-8d37-74c63abd56f2

📥 Commits

Reviewing files that changed from the base of the PR and between d32152a and 2d8d0c4.

📒 Files selected for processing (18)
  • apps/v4/composables/useDesignSystemSearchParams.ts
  • apps/v4/lib/parse-design-system-config.ts
  • apps/v4/lib/parse-preset-input.ts
  • apps/v4/lib/preset-encoding.ts
  • apps/v4/registry/config.ts
  • apps/v4/server/routes/init.get.ts
  • packages/cli/package.json
  • packages/cli/src/commands/init.ts
  • packages/cli/src/preset/index.ts
  • packages/cli/src/preset/preset.ts
  • packages/cli/src/preset/presets.ts
  • packages/cli/src/registry/api.ts
  • packages/cli/src/registry/constants.ts
  • packages/cli/src/registry/schema.ts
  • packages/cli/src/utils/preset-encoding.ts
  • packages/cli/test/commands/init.test.ts
  • packages/cli/test/utils/registry-api.test.ts
  • packages/cli/tsdown.config.ts
💤 Files with no reviewable changes (2)
  • packages/cli/src/utils/preset-encoding.ts
  • apps/v4/lib/preset-encoding.ts

Comment thread apps/v4/server/routes/init.get.ts Outdated
Comment thread packages/cli/src/commands/init.ts
Comment thread packages/cli/src/registry/schema.ts
- init.get.ts: wrap buildRegistryBase in try/catch so invalid
  base/iconLibrary lookups return 400 with the underlying message
  instead of a bare 500; stop exposing the Zod error shape in the
  500 payload (log server-side only).
- schema.ts: widen presetSchema.menuColor to include the translucent
  variants, matching the earlier rawConfigSchema widening.
- presets.ts: guard isShadcnVueInitUrl with isUrl() instead of a
  try/catch around new URL().
- init.test.ts: extend the default-preset coverage to assert
  fontHeading and rtl are defined.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/cli/src/preset/presets.ts`:
- Around line 242-244: The promptForPreset function currently calls
process.exit(...) when the user makes no selection (and again around the 258-263
block); remove those process.exit calls and instead return a distinct control
result (e.g., an object with result.kind set to 'cancelled' or 'no-selection')
from promptForPreset so callers can handle shutdown/cleanup; update the
function's return type accordingly and ensure callers (notably the init command)
branch on result.kind and perform process.exit there if desired, preserving
existing finally/teardown behavior.
- Around line 331-335: The function isShadcnVueInitUrl uses
url.startsWith(SHADCN_VUE_URL) which is a weak host validation; update
isShadcnVueInitUrl to parse the candidate URL and the SHADCN_VUE_URL with new
URL(...) and compare their origin (or host) instead of using startsWith, e.g.,
after isUrl(url) create const candidate = new URL(url) and const trusted = new
URL(SHADCN_VUE_URL) and return candidate.origin === trusted.origin &&
candidate.pathname === '/init' (preserving the pathname check) to ensure proper
host validation for SHADCN_VUE_URL.
- Around line 313-327: The code currently allows registryBaseConfig to be
undefined which silently falls back to defaults; update the logic after
computing registryBaseConfig (the const registryBaseConfig = ... using
item?.type === 'registry:base' and item.config) to throw an explicit error when
registryBaseConfig is undefined (include initUrl and any item identifier in the
message for context), so that when isShadcnVueInitUrl(initUrl) or subsequent
processing expects a registry:base item the function fails fast instead of
continuing with defaults; keep the existing URL cleanup (cleanUrl) logic
unchanged and ensure the thrown error prevents returning the object that
contains registryBaseConfig, installStyleIndex, and url.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f5615c8b-dca0-4378-936a-0a3988fb6e7d

📥 Commits

Reviewing files that changed from the base of the PR and between 2d8d0c4 and a2f6d02.

📒 Files selected for processing (4)
  • apps/v4/server/routes/init.get.ts
  • packages/cli/src/preset/presets.ts
  • packages/cli/src/registry/schema.ts
  • packages/cli/test/commands/init.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/v4/server/routes/init.get.ts
  • packages/cli/src/registry/schema.ts
  • packages/cli/test/commands/init.test.ts

Comment thread packages/cli/src/preset/presets.ts
Comment thread packages/cli/src/preset/presets.ts Outdated
Comment thread packages/cli/src/preset/presets.ts
The preset init was loading the Google Fonts @import but never
declaring `--font-sans` anywhere, so Tailwind v4 kept falling back
to its default sans stack. Extend buildRegistryBase to look up the
font in apps/v4/registry/fonts.ts and emit:

- cssVars.theme[font.variable] = font.family — lands inside
  @theme inline { --font-sans: 'JetBrains Mono Variable', monospace }
- body: "@apply font-sans" (or font-mono / font-serif) — lands in
  @layer base so the body actually uses the font

Also add packages/cli/scripts/test-preset-init.sh: copies the vite
fixture to a temp dir, patches tsconfig.json with the @ alias,
runs the CLI against localhost:3000 /init, and prints
components.json + style.css + utils.ts so the whole preset flow
can be smoke-tested locally.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/cli/scripts/test-preset-init.sh (2)

27-28: Remove unused REPO_DIR variable.

At Line 28, REPO_DIR is assigned but never used.

Proposed fix
 CLI_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
-REPO_DIR="$(cd "$CLI_DIR/../.." && pwd)"
 FIXTURE="$CLI_DIR/test/fixtures/frameworks/vite"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/scripts/test-preset-init.sh` around lines 27 - 28, Remove the
unused REPO_DIR variable assignment in the script: delete the line that defines
REPO_DIR (the assignment using "$(cd "$CLI_DIR/../.." && pwd)") in
packages/cli/scripts/test-preset-init.sh since CLI_DIR is already defined and
REPO_DIR is never referenced; ensure no other references to REPO_DIR remain, and
run shellcheck or a quick test to confirm no breakage.

37-37: URL-encode preset in the reachability probe.

At Line 37, probing with raw $PRESET can produce false negatives when preset contains reserved URL characters (e.g., URL-form presets).

Proposed fix
-if ! curl -fsS -o /dev/null "http://localhost:3000/init?preset=$PRESET&track=0"; then
+ENC_PRESET="$(node -p "encodeURIComponent(process.argv[1])" "$PRESET")"
+if ! curl -fsS -o /dev/null "http://localhost:3000/init?preset=$ENC_PRESET&track=0"; then
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/scripts/test-preset-init.sh` at line 37, The reachability probe
uses an unencoded $PRESET in the curl URL which can break when $PRESET contains
reserved characters; update the curl invocation in the test-preset-init.sh
script to URL-encode the preset (use curl --get with --data-urlencode
"preset=$PRESET" and include track=0 as a separate query param) so the probe
constructs a safe /init?preset=...&track=0 request even when PRESET contains
special characters.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/cli/scripts/test-preset-init.sh`:
- Line 24: The script's safety flags currently use "set -uo pipefail" which
omits -e (exit-on-error); update the shell options by changing the invocation to
include -e (i.e., use "set -euo pipefail") so the test fails fast if any command
(copy/init/readback) errors, ensuring the success message cannot be reached on
failures.

---

Nitpick comments:
In `@packages/cli/scripts/test-preset-init.sh`:
- Around line 27-28: Remove the unused REPO_DIR variable assignment in the
script: delete the line that defines REPO_DIR (the assignment using "$(cd
"$CLI_DIR/../.." && pwd)") in packages/cli/scripts/test-preset-init.sh since
CLI_DIR is already defined and REPO_DIR is never referenced; ensure no other
references to REPO_DIR remain, and run shellcheck or a quick test to confirm no
breakage.
- Line 37: The reachability probe uses an unencoded $PRESET in the curl URL
which can break when $PRESET contains reserved characters; update the curl
invocation in the test-preset-init.sh script to URL-encode the preset (use curl
--get with --data-urlencode "preset=$PRESET" and include track=0 as a separate
query param) so the probe constructs a safe /init?preset=...&track=0 request
even when PRESET contains special characters.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2c455d57-b83c-48ba-bd0e-25ebfd90a469

📥 Commits

Reviewing files that changed from the base of the PR and between a2f6d02 and f37de85.

📒 Files selected for processing (2)
  • apps/v4/registry/config.ts
  • packages/cli/scripts/test-preset-init.sh

Comment thread packages/cli/scripts/test-preset-init.sh Outdated
- presets.ts: promptForPreset now returns a discriminated result
  ({ kind: 'preset' | 'custom' | 'cancelled' }) instead of calling
  process.exit, so the command-layer finally block still runs.
  Init.ts handles the three cases explicitly.
- presets.ts: resolveRegistryBaseConfig now fails fast when the init
  URL does not resolve to a registry:base item with a config, so
  broken preset propagation can't silently fall back to defaults.
- presets.ts: isShadcnVueInitUrl now compares URL.origin instead of
  using startsWith, closing a weak host-validation prefix match.
- test-preset-init.sh: re-enable 'set -e' and guard the init step
  with an explicit status check so artifacts can still be printed
  on failure but the script exits non-zero.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/cli/src/commands/init.ts (1)

319-329: ⚠️ Potential issue | 🟡 Minor

Return from the action instead of calling process.exit() here.

These branches are still inside the command handler's try, so process.exit() skips the surrounding finally despite the comment on Line 320. If you want the cleanup path to run, set process.exitCode for cancel and return from the handler.

Suggested fix
           if (result.kind === 'cancelled') {
             logger.break()
-            process.exit(1)
+            process.exitCode = 1
+            return
           }
@@
           if (result.kind === 'custom') {
             logger.break()
-            process.exit(0)
+            return
           }
#!/bin/bash
node -e 'try { process.exit(0) } finally { console.log("finally ran") }'
printf 'exit=%s\n' "$?"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/commands/init.ts` around lines 319 - 329, The cancel/custom
branches in the command handler use process.exit() which prevents the
surrounding try/finally cleanup from running; change both branches (the checks
on result.kind === 'cancelled' and result.kind === 'custom' where logger.break()
is called) to set process.exitCode (e.g., 1 for cancelled, 0 for custom) and
then return from the handler instead of calling process.exit() so the outer
finally block executes as intended.
🧹 Nitpick comments (1)
packages/cli/scripts/test-preset-init.sh (1)

28-28: Remove the unused REPO_DIR variable.

ShellCheck is right here: the path is never read, so it is just stale state to maintain in a smoke test.

Suggested fix
-REPO_DIR="$(cd "$CLI_DIR/../.." && pwd)"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/scripts/test-preset-init.sh` at line 28, The script defines an
unused variable REPO_DIR via the assignment REPO_DIR="$(cd "$CLI_DIR/../.." &&
pwd)" which is stale and flagged by ShellCheck; remove that line so the variable
is not set or referenced, leaving the rest of test-preset-init.sh unchanged (no
other code depends on REPO_DIR).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/cli/src/commands/init.ts`:
- Around line 277-282: The validation currently uses the `in` operator to check
`opts.preset` against `presetsByName`, which can match prototype properties;
change both checks that use `in` (the initial validation around opts.preset and
the later usage when resolving the preset) to use an own-property test like
`Object.prototype.hasOwnProperty.call(presetsByName, opts.preset)` so inherited
properties (e.g., "toString") are rejected; update the conditional that
references `presetsByName` and any later branch that indexes
`presetsByName[opts.preset]` to rely on this hasOwnProperty check before
treating the value as a valid preset.

---

Duplicate comments:
In `@packages/cli/src/commands/init.ts`:
- Around line 319-329: The cancel/custom branches in the command handler use
process.exit() which prevents the surrounding try/finally cleanup from running;
change both branches (the checks on result.kind === 'cancelled' and result.kind
=== 'custom' where logger.break() is called) to set process.exitCode (e.g., 1
for cancelled, 0 for custom) and then return from the handler instead of calling
process.exit() so the outer finally block executes as intended.

---

Nitpick comments:
In `@packages/cli/scripts/test-preset-init.sh`:
- Line 28: The script defines an unused variable REPO_DIR via the assignment
REPO_DIR="$(cd "$CLI_DIR/../.." && pwd)" which is stale and flagged by
ShellCheck; remove that line so the variable is not set or referenced, leaving
the rest of test-preset-init.sh unchanged (no other code depends on REPO_DIR).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dd01396d-3a29-4033-8d99-bcedfff590ee

📥 Commits

Reviewing files that changed from the base of the PR and between f37de85 and 9ecbbdb.

📒 Files selected for processing (3)
  • packages/cli/scripts/test-preset-init.sh
  • packages/cli/src/commands/init.ts
  • packages/cli/src/preset/presets.ts

Comment thread packages/cli/src/commands/init.ts
@zernonia zernonia merged commit 8437850 into dev Apr 11, 2026
5 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Apr 11, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: "Presets"/create feature doesn't work at all

1 participant