Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .project/known-issues.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Known issues — confirmed on TestApp smoke (2026-08-11)

> **2.0.2 (branch `fix/registry-gaps`)**: P0-1 (utils/lib via `registryDependencies`) and P0-2 (`paths` + `target`) are fixed; runtime filter keeps type files; type-only cycles are quiet. See CHANGELOG. P0-3 (relative imports vs remap) remains by design: remap at the same depth.

Severity: **P0** blocks shadcn-like DX · **P1** layout / stack · **P2** polish.

---
Expand Down
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Changelog

## [2.0.2] - unreleased

Registry gap fixes found while validating per-runtime registries (`r/<runtime>/`) with the real CLI.

### Fixed
- **Runtime filter dropped type files** (`--runtime` / `config.runtime`): `*.types.ts`, `*.types.tsx` and `*.d.ts` are now always kept, and so is every file of the same item that a kept file imports (relative imports, transitively, extension/`index` resolution). A file imported by a selected file is never dropped. No more `init --framework none` workaround.
- **`registry:utils` / `registry:lib` not resolved through `registryDependencies`** (`Component utils not found in ui registry, skipping`): `utils`, `utils/<name>`, `lib/<name>` (and bare lib names in `index.json`) resolve against their own endpoints (`utils/utils.json`, `lib/presets.json`) and are installed by `add` when missing. Catalog exclusion (picker, `--all`) is kept; explicit names and dependencies are no longer filtered. Dependency-only utils/lib never overwrite existing files, even with `--force`. `ui8kit add utils` now works.
- **`paths` ignored when a registry file sets `target`**: precedence is now `--path key=dir` flag > `config.paths` mapped by target prefix > file `target` (documented in `docs/configuration.md`). Behaviour is unchanged when no `paths`/`--path` apply.
- **Dependency cycles** (type-only imports, e.g. `feature-grid` ↔ `feature-cards`): no more `Circular registry dependency` / `Unresolved dependency cycle` warnings. Each item is installed once and back edges no longer disturb install order. `--verbose` lists type-only edges; a cycle made only of value imports gets one concise warning.
- Registry item cache is now namespaced per `--registry-url`, so two registries (e.g. per runtime) no longer return each other's cached items.

### Added
- `add --path <key=dir>` (repeatable) to override install folders for one run.
- `logger.verbose` (informational lines shown only with `--verbose`).
- Tests with a generated synthetic fixture registry (`tests/fixtures/build-fixture-registry.mjs`) and an end-to-end script `npm run test:e2e` (`scripts/e2e-fixture-registry.mjs`, init/add for svelte and react).

### Docs
- README, `docs/commands.md`, `docs/configuration.md`, `docs/registry-format.md`, `docs/troubleshooting.md`: runtime filter, utils/lib resolution, cycles, `paths` precedence.

## [2.0.1] - 2026-08-11

### Added
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ After init, edit to pin CDN + runtime (example for Vite/Svelte under `src/`):
}
```

**Path remap note:** bricks import `../../utils`. Keep utils at `{dir}/utils`. If you want MentorYChat-style `src/kit`, set `"paths": { "ui": "kit" }` in config for documentation — **in 2.0.1 `paths` is ignored when registry files set `target`** (they still install under `components/ui`). Workaround: after `add`, move `src/components/ui/*` → `src/kit/` and leave `src/utils` in place. Do **not** set `"utils": "kit/utils"` without rewriting imports.
**Path remap note:** bricks import `../../utils` (relative). Keep utils at `{dir}/utils`, and remap folders at the same depth (e.g. `"paths": { "ui": "kit" }`). Since 2.0.2 `paths` **is honoured when a registry file sets `target`**: `--path key=dir` flag > `config.paths` (mapped by target prefix) > the file's `target` — see [Configuration → paths precedence](./docs/configuration.md#paths-precedence-with-registry-target). Do **not** set `"utils": "kit/utils"` without rewriting imports.

### 3. Add UI (runtime file filter)

Expand All @@ -84,7 +84,7 @@ bunx ui8kit@2.0.1 add button stack title text --runtime svelte --force
bunx ui8kit@2.0.1 add --all --runtime svelte --force
```

`--runtime svelte` keeps `.svelte` + `*.shared.ts` + `*.variants.json` only.
`--runtime svelte` keeps `.svelte`, `*.shared.ts`, `*.variants.json`, **type files** (`*.types.ts`, `*.d.ts`) and **any file a kept file imports** (relative imports, followed transitively), so installs never end up with unresolved imports.

Also install npm peers used by utils if missing:

Expand Down Expand Up @@ -209,14 +209,18 @@ bunx ui8kit add button --registry-url https://unpkg.com/@ui8kit/registry@2.0.0/r

Install **preserves nested paths** (e.g. `components/ui/button/Button.svelte`), no basename flatten.

**Runtime filter:** when `--runtime` or `config.runtime` is set, only that stack’s files are written, plus shared brick assets (`*.shared.ts`, `*.variants.json`). Utils/lib items are installed in full when reachable. Items that declare a different item-level `runtime` field are skipped entirely.
**Runtime filter:** when `--runtime` or `config.runtime` is set, only that stack’s files are written, plus shared brick assets (`*.shared.ts`, `*.variants.json`), type files (`*.types.ts`, `*.d.ts`) and every file of the same item that a kept file imports (transitively). A file imported by a selected file is never dropped. Utils/lib items are installed in full. Items that declare a different item-level `runtime` field are skipped entirely.

**`utils` and `lib/*` dependencies:** `registryDependencies` entries `utils`, `lib/<name>` (or a bare lib item name listed in `index.json`) are resolved against their own endpoints (`utils/utils.json`, `lib/<name>.json`) and installed by `add` if missing — no more "not found in ui registry, skipping". Files that already exist are left untouched, even with `--force`, when the item was only pulled in as a dependency.

**Dependency cycles** (e.g. `feature-grid` ↔ `feature-cards` through type-only imports) are resolved quietly: each item is installed once. `--verbose` lists type-only edges.

```bash
# Multi-file brick on CDN → only Svelte + shared land on disk
bunx ui8kit add button --runtime svelte
```

Known gap: `ui8kit add utils` fails in 2.0.1 — use init core install or the [manual fallback](#manual-utils-fallback-if-init-skipped-core).
Since 2.0.2 `ui8kit add utils` works (explicit item; `--force` overwrites). The [manual fallback](#manual-utils-fallback-if-init-skipped-core) is only needed for older CLIs.

### `list` / `diff` / `info` / `cache` / `reset` / `scan` / `build`

Expand Down
14 changes: 14 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ ui8kit add [components...] [options]
| `-f, --force` | Overwrite existing files |
| `-r, --registry <type>` | Registry type (default `ui`) |
| `--runtime <name>` | Install only this runtime’s files (+ shared); skip items with a mismatched `runtime` field |
| `--path <key=dir>` | Override an install folder for this run (repeatable), e.g. `--path ui=kit`. Beats `config.paths` and registry `target`s. Keys: `lib`, `utils`, `components`, `ui`, `blocks`, `layouts`, `variants`, `widgets` |
| `--dry-run` | Plan only; no writes |
| `--retry` | Retry failed CDN requests |
| `--registry-url <url>` | Registry base override |
Expand Down Expand Up @@ -92,8 +93,21 @@ Registry bricks often ship **all** stacks in one item (no per-item `runtime`). W
| `latte` | `*.latte` |
| `twig` | `*.twig` / `*.html.twig` |

Also always kept:

- **Type files**: `*.types.ts`, `*.types.tsx`, `*.d.ts` (they are not tied to a runtime and blocks import them).
- **Imported files**: every file of the same item that a kept file imports with a relative specifier (`./x`, `../x`; extensions and `index` files are resolved; followed transitively). A file that another selected file imports is never dropped, even if it belongs to another runtime.

Shared always: `*.shared.ts`, `*.variants.json`. Types `registry:utils`, `registry:lib`, `registry:variants` are never sliced. If an item sets `runtime: "svelte"` and you pass `--runtime react`, the whole item is skipped.

### Dependency resolution

`add` resolves `registryDependencies` recursively and installs every item once (dependencies first).

- **`utils` and `lib/*`**: `utils`, `utils/<name>`, `lib/<name>` (and bare lib names present in `index.json`) are fetched from their own type endpoints (`utils/<name>.json`, `lib/<name>.json`). They are hidden from the interactive picker and from `--all` listings, but are installed whenever an item depends on them. When they are only pulled in as dependencies, existing files are **never overwritten** (not even with `--force`); `ui8kit add utils --force` overwrites explicitly. `init` still installs core `utils`/`lib`/`variants` unless `--skip-core`.
- **Cycles**: items may depend on each other (typically type-only imports such as `feature-grid` ↔ `feature-cards`). Cycles are not an error: no default warning, each item is installed once. With `--verbose` the type-only edges of a cycle are listed. A cycle where every edge is a value import gets a single one-line warning.
- **Missing items** still warn `Component <name> not found in <registry> registry, skipping`.

---

## `list`
Expand Down
17 changes: 17 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@ All optional; values are relative to `dir` (no leading `./` required):

`lib` · `utils` · `components` · `ui` · `blocks` · `layouts` · `variants` · `widgets`

### `paths` precedence with registry `target`

Registry files usually carry a `target` (e.g. `components/ui/button/Button.svelte`, or registry-specific such as `primitives/button/button.svelte`). Where a file lands is decided in this order:

1. **Explicit CLI flag**: `add --path ui=kit` (repeatable; keys as above).
2. **`config.paths`, mapped by target prefix**:
- if the target starts with a canonical folder — `components/ui` → `ui`, `components` → `components`, `blocks`, `layouts`, `variants`, `widgets`, `utils`, `lib` (longest match first) — that prefix is replaced by the configured folder;
- otherwise the target's first folder (e.g. `primitives/`, `presets/`) is treated as the item type's own folder and replaced by `paths[<key of the item type>]` (`registry:ui` → `ui`, `registry:lib` → `lib`, …).
3. **The file's `target`** (unchanged), when no `paths` entry applies.

Everything is relative to `dir`; keys you did not configure are not remapped. Without `paths` and without `--path` behaviour is exactly as in 2.0.0/2.0.1. Files without `target` keep using `paths` as before.

Example — `{ "paths": { "ui": "kit" } }`: `components/ui/button/Button.svelte` → `src/kit/button/Button.svelte`; `primitives/button/button.svelte` → `src/kit/button/button.svelte`; `blocks/hero.svelte` stays in `src/blocks/`.

Caveat: the CLI does not rewrite relative imports. Remap to folders at the same depth, and keep `utils` next to the remapped folders (bricks import `../../utils`, `../presets/defaults`, `../primitives/…`).

## Default aliases (when `dir` is `src`)

```json
Expand Down Expand Up @@ -101,6 +117,7 @@ Flags on `init` / `add` / `list` / `diff` override config for that invocation:
| `--registry-version` | `registryVersion` |
| `--strict-cdn` | `strictCdn` |
| `--runtime` | Prefer that runtime: file-level filter (+ skip mismatched item `runtime`) |
| `--path key=dir` | (`add`) Override one `paths` key for this run; repeatable |
| `--import-style` | `importStyle` (init) |

Use flags for one-off CI pins; put stable values in config for daily work.
Expand Down
2 changes: 1 addition & 1 deletion docs/registry-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Example item:
| `digest` | optional | Digest / release id |
| `dependencies` | optional | npm deps |
| `devDependencies` | optional | npm devDeps |
| `registryDependencies` | optional | Other item names |
| `registryDependencies` | optional | Other item names. `utils`, `utils/<name>` and `lib/<name>` address `registry:utils` / `registry:lib` items (endpoints `utils/<name>.json`, `lib/<name>.json`); cycles are allowed |
| `title` / `description` | optional | Docs / list UI |
| `tailwind` / `cssVars` | optional | Metadata for consumers/tooling |
| `meta` | optional | Free-form |
Expand Down
8 changes: 8 additions & 0 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,11 @@ node -v
```

Attach these when filing issues.

## "Unresolved import" after `add --runtime …`

Since 2.0.2 the runtime filter keeps `*.types.ts` / `*.d.ts` and every file imported by a kept file. If an import is still unresolved it points at **another registry item** — check that item is listed in `registryDependencies`. On 2.0.0/2.0.1 the workaround was `init --framework none`; it is no longer needed.

## "Circular registry dependency" / "Unresolved dependency cycle"

Removed in 2.0.2: cycles (usually type-only imports) are installed once without a warning; run with `--verbose` to list type-only edges. A single `Runtime import cycle between …` warning means every edge in the cycle is a value import — check those files for circular runtime imports.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ui8kit",
"version": "2.0.1",
"version": "2.0.2",
"description": "CLI to install and maintain UI8Kit component/block registries (multi-runtime, file-based)",
"type": "module",
"bin": {
Expand Down Expand Up @@ -57,6 +57,7 @@
"dev": "tsup --watch",
"type-check": "tsc --noEmit",
"get-cdn": "node scripts/get-cdn.js",
"test:e2e": "npm run build && node scripts/e2e-fixture-registry.mjs",
"smoke": "bash scripts/smoke-cli-workflow.sh",
"smoke:win": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/smoke-cli-workflow.ps1",
"clean:artifacts": "bash scripts/node-artifact-cleaner.sh --yes",
Expand Down
Loading
Loading