@zentauri-ui/zentauri-components 2.1.5 → 2.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -8
- package/cli/cli.integration.test.ts +36 -0
- package/cli/index.mjs +91 -12
- package/cli/index.test.ts +180 -0
- package/cli/props.json +609 -14
- package/cli/registry.json +22 -0
- package/cli/rewrite-imports.mjs +29 -4
- package/cli/rewrite-imports.test.ts +35 -0
- package/dist/{chunk-RENXBUZY.js → chunk-5ELR6MIN.js} +6 -6
- package/dist/{chunk-RENXBUZY.js.map → chunk-5ELR6MIN.js.map} +1 -1
- package/dist/chunk-5FU57ZVQ.js +19 -0
- package/dist/{chunk-D2GISTDL.js.map → chunk-5FU57ZVQ.js.map} +1 -1
- package/dist/chunk-74SKXGTM.js +4 -0
- package/dist/chunk-74SKXGTM.js.map +1 -0
- package/dist/{chunk-WBZKMSXW.mjs → chunk-7UXPXCKV.mjs} +3 -3
- package/dist/{chunk-WBZKMSXW.mjs.map → chunk-7UXPXCKV.mjs.map} +1 -1
- package/dist/chunk-COCPCZMR.mjs +77 -0
- package/dist/chunk-COCPCZMR.mjs.map +1 -0
- package/dist/chunk-CYKSS5S5.mjs +128 -0
- package/dist/chunk-CYKSS5S5.mjs.map +1 -0
- package/dist/chunk-DBNGLT5U.mjs +221 -0
- package/dist/chunk-DBNGLT5U.mjs.map +1 -0
- package/dist/{chunk-BL6UVCV7.mjs → chunk-FUCW5GPE.mjs} +36 -11
- package/dist/chunk-FUCW5GPE.mjs.map +1 -0
- package/dist/chunk-G7FVHZRB.js +225 -0
- package/dist/chunk-G7FVHZRB.js.map +1 -0
- package/dist/chunk-HMDH4BQJ.js +123 -0
- package/dist/chunk-HMDH4BQJ.js.map +1 -0
- package/dist/chunk-I7EBE7BD.js +98 -0
- package/dist/chunk-I7EBE7BD.js.map +1 -0
- package/dist/{chunk-PAG5CTLN.mjs → chunk-KVSRUAXP.mjs} +3 -3
- package/dist/{chunk-PAG5CTLN.mjs.map → chunk-KVSRUAXP.mjs.map} +1 -1
- package/dist/chunk-LHBJD57K.mjs +143 -0
- package/dist/chunk-LHBJD57K.mjs.map +1 -0
- package/dist/chunk-OYAJG2BO.js +83 -0
- package/dist/chunk-OYAJG2BO.js.map +1 -0
- package/dist/chunk-PG7LQVU6.js +86 -0
- package/dist/chunk-PG7LQVU6.js.map +1 -0
- package/dist/chunk-PTU5ZAYX.js +145 -0
- package/dist/chunk-PTU5ZAYX.js.map +1 -0
- package/dist/chunk-QKO5DA4N.mjs +81 -0
- package/dist/chunk-QKO5DA4N.mjs.map +1 -0
- package/dist/chunk-T7PIKDUZ.js +130 -0
- package/dist/chunk-T7PIKDUZ.js.map +1 -0
- package/dist/chunk-TDK5TVJE.mjs +3 -0
- package/dist/chunk-TDK5TVJE.mjs.map +1 -0
- package/dist/{chunk-NZSZE36T.js → chunk-TJ2EWPER.js} +42 -10
- package/dist/chunk-TJ2EWPER.js.map +1 -0
- package/dist/chunk-VBNW2B4D.mjs +3 -0
- package/dist/chunk-VBNW2B4D.mjs.map +1 -0
- package/dist/chunk-W6DO36XD.mjs +96 -0
- package/dist/chunk-W6DO36XD.mjs.map +1 -0
- package/dist/chunk-XR3J46TZ.js +4 -0
- package/dist/chunk-XR3J46TZ.js.map +1 -0
- package/dist/chunk-ZOHCADDL.mjs +121 -0
- package/dist/chunk-ZOHCADDL.mjs.map +1 -0
- package/dist/design-system/audio-player.d.ts +61 -0
- package/dist/design-system/audio-player.d.ts.map +1 -0
- package/dist/design-system/data-table.d.ts +8 -0
- package/dist/design-system/data-table.d.ts.map +1 -0
- package/dist/design-system/facade.js +11 -10
- package/dist/design-system/facade.js.map +1 -1
- package/dist/design-system/facade.mjs +10 -9
- package/dist/design-system/facade.mjs.map +1 -1
- package/dist/design-system/index.d.ts +2 -0
- package/dist/design-system/index.d.ts.map +1 -1
- package/dist/hooks/useTableFilter.js +6 -116
- package/dist/hooks/useTableFilter.js.map +1 -1
- package/dist/hooks/useTableFilter.mjs +1 -118
- package/dist/hooks/useTableFilter.mjs.map +1 -1
- package/dist/hooks/useTableSort.js +6 -91
- package/dist/hooks/useTableSort.js.map +1 -1
- package/dist/hooks/useTableSort.mjs +1 -93
- package/dist/hooks/useTableSort.mjs.map +1 -1
- package/dist/hooks/useVirtualList.js +6 -76
- package/dist/hooks/useVirtualList.js.map +1 -1
- package/dist/hooks/useVirtualList.mjs +1 -78
- package/dist/hooks/useVirtualList.mjs.map +1 -1
- package/dist/ui/audio-player/audio-player-base.d.ts +20 -0
- package/dist/ui/audio-player/audio-player-base.d.ts.map +1 -0
- package/dist/ui/audio-player/audio-player.d.ts +6 -0
- package/dist/ui/audio-player/audio-player.d.ts.map +1 -0
- package/dist/ui/audio-player/index.d.ts +5 -0
- package/dist/ui/audio-player/index.d.ts.map +1 -0
- package/dist/ui/audio-player/types.d.ts +44 -0
- package/dist/ui/audio-player/types.d.ts.map +1 -0
- package/dist/ui/audio-player/variants.d.ts +12 -0
- package/dist/ui/audio-player/variants.d.ts.map +1 -0
- package/dist/ui/audio-player.js +556 -0
- package/dist/ui/audio-player.js.map +1 -0
- package/dist/ui/audio-player.mjs +545 -0
- package/dist/ui/audio-player.mjs.map +1 -0
- package/dist/ui/buttons/animated.js +13 -12
- package/dist/ui/buttons/animated.js.map +1 -1
- package/dist/ui/buttons/animated.mjs +11 -10
- package/dist/ui/buttons/animated.mjs.map +1 -1
- package/dist/ui/buttons.js +15 -13
- package/dist/ui/buttons.mjs +13 -11
- package/dist/ui/checkbox.js +7 -123
- package/dist/ui/checkbox.js.map +1 -1
- package/dist/ui/checkbox.mjs +2 -126
- package/dist/ui/checkbox.mjs.map +1 -1
- package/dist/ui/data-table/data-table-base.d.ts +6 -0
- package/dist/ui/data-table/data-table-base.d.ts.map +1 -0
- package/dist/ui/data-table/data-table.d.ts +6 -0
- package/dist/ui/data-table/data-table.d.ts.map +1 -0
- package/dist/ui/data-table/index.d.ts +4 -0
- package/dist/ui/data-table/index.d.ts.map +1 -0
- package/dist/ui/data-table/types.d.ts +92 -0
- package/dist/ui/data-table/types.d.ts.map +1 -0
- package/dist/ui/data-table/variants.d.ts +8 -0
- package/dist/ui/data-table/variants.d.ts.map +1 -0
- package/dist/ui/data-table.js +620 -0
- package/dist/ui/data-table.js.map +1 -0
- package/dist/ui/data-table.mjs +611 -0
- package/dist/ui/data-table.mjs.map +1 -0
- package/dist/ui/dynamic-stepper.js +23 -22
- package/dist/ui/dynamic-stepper.js.map +1 -1
- package/dist/ui/dynamic-stepper.mjs +12 -11
- package/dist/ui/dynamic-stepper.mjs.map +1 -1
- package/dist/ui/inputs.js +7 -138
- package/dist/ui/inputs.js.map +1 -1
- package/dist/ui/inputs.mjs +2 -141
- package/dist/ui/inputs.mjs.map +1 -1
- package/dist/ui/pagination.js +25 -225
- package/dist/ui/pagination.js.map +1 -1
- package/dist/ui/pagination.mjs +13 -227
- package/dist/ui/pagination.mjs.map +1 -1
- package/dist/ui/table.js +1 -0
- package/dist/ui/table.mjs +1 -0
- package/package.json +1 -1
- package/src/design-system/audio-player.ts +109 -0
- package/src/design-system/data-table.ts +20 -0
- package/src/design-system/index.ts +2 -0
- package/src/ui/audio-player/audio-player-base.tsx +557 -0
- package/src/ui/audio-player/audio-player.test.tsx +485 -0
- package/src/ui/audio-player/audio-player.tsx +8 -0
- package/src/ui/audio-player/index.ts +24 -0
- package/src/ui/audio-player/types.ts +57 -0
- package/src/ui/audio-player/variants.ts +43 -0
- package/src/ui/data-table/data-table-base.tsx +701 -0
- package/src/ui/data-table/data-table.test.tsx +389 -0
- package/src/ui/data-table/data-table.tsx +11 -0
- package/src/ui/data-table/index.ts +24 -0
- package/src/ui/data-table/types.ts +121 -0
- package/src/ui/data-table/variants.ts +21 -0
- package/dist/chunk-BL6UVCV7.mjs.map +0 -1
- package/dist/chunk-D2GISTDL.js +0 -19
- package/dist/chunk-NZSZE36T.js.map +0 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Published artifacts live under `dist/`. Imports use **per-entry subpaths**: `@ze
|
|
|
15
15
|
|
|
16
16
|
| Surface | Count | Import shape |
|
|
17
17
|
| ------------------- | ----: | ----------------------------------------------------- |
|
|
18
|
-
| Static UI entries |
|
|
18
|
+
| Static UI entries | 42 | `@zentauri-ui/zentauri-components/ui/<name>` |
|
|
19
19
|
| Animated UI entries | 27 | `@zentauri-ui/zentauri-components/ui/<name>/animated` |
|
|
20
20
|
| Animation entries | 41 | `@zentauri-ui/zentauri-components/animations/<name>` |
|
|
21
21
|
| Chart entries | 9 | `@zentauri-ui/zentauri-components/charts/<type>` |
|
|
@@ -29,16 +29,16 @@ Generated from the component package Vitest JSON report via `pnpm --filter @zent
|
|
|
29
29
|
|
|
30
30
|
| Metric | Result |
|
|
31
31
|
| ---------- | ---------------- |
|
|
32
|
-
| Test files |
|
|
33
|
-
| Tests |
|
|
32
|
+
| Test files | 97 passed (97) |
|
|
33
|
+
| Tests | 808 passed (808) |
|
|
34
34
|
|
|
35
35
|
| Area | Test files | Tests |
|
|
36
36
|
| ------------------------------ | ---------- | ----- |
|
|
37
|
-
| Components and UI utilities |
|
|
37
|
+
| Components and UI utilities | 48 | 503 |
|
|
38
38
|
| Standalone animations | 1 | 45 |
|
|
39
39
|
| React hooks | 41 | 174 |
|
|
40
40
|
| Design system facade | 1 | 11 |
|
|
41
|
-
| CLI and import rewriting |
|
|
41
|
+
| CLI and import rewriting | 4 | 33 |
|
|
42
42
|
| Accessibility (axe + keyboard) | 2 | 42 |
|
|
43
43
|
|
|
44
44
|
### Per-suite snapshot
|
|
@@ -48,13 +48,15 @@ Generated from the component package Vitest JSON report via `pnpm --filter @zent
|
|
|
48
48
|
| `src/animations/animations.test.tsx` | 45 |
|
|
49
49
|
| `src/ui/buttons/button.test.tsx` | 44 |
|
|
50
50
|
| `src/ui/inputs/input.test.tsx` | 40 |
|
|
51
|
+
| `src/ui/audio-player/audio-player.test.tsx` | 34 |
|
|
51
52
|
| `src/ui/peer-isolation.test.ts` | 29 |
|
|
52
53
|
| `src/accessibility/axe-core.test.tsx` | 24 |
|
|
53
54
|
| `src/ui/combobox/combobox.test.tsx` | 24 |
|
|
54
|
-
| `cli/cli.integration.test.ts` |
|
|
55
|
+
| `cli/cli.integration.test.ts` | 21 |
|
|
55
56
|
| `src/accessibility/keyboard-interaction.test.tsx` | 18 |
|
|
56
57
|
| `src/ui/pagination/pagination.test.tsx` | 15 |
|
|
57
58
|
| `src/ui/timeline/timeline.test.tsx` | 14 |
|
|
59
|
+
| `src/ui/data-table/data-table.test.tsx` | 13 |
|
|
58
60
|
| `src/ui/context-menu/context-menu.test.tsx` | 12 |
|
|
59
61
|
| `src/lib/facade.test.ts` | 11 |
|
|
60
62
|
| `src/ui/alert/alert.test.tsx` | 11 |
|
|
@@ -77,6 +79,7 @@ Generated from the component package Vitest JSON report via `pnpm --filter @zent
|
|
|
77
79
|
| `src/ui/progress/progress.test.tsx` | 8 |
|
|
78
80
|
| `src/ui/scroll-area/scroll-area.test.tsx` | 8 |
|
|
79
81
|
| `src/ui/spinner/animated/spinner.test.tsx` | 8 |
|
|
82
|
+
| `cli/rewrite-imports.test.ts` | 7 |
|
|
80
83
|
| `src/hooks/useDynamicStepper/useDynamicStepper.test.ts` | 7 |
|
|
81
84
|
| `src/hooks/useHotkeys/useHotkeys.test.ts` | 7 |
|
|
82
85
|
| `src/hooks/useTableSort/useTableSort.test.ts` | 7 |
|
|
@@ -100,7 +103,6 @@ Generated from the component package Vitest JSON report via `pnpm --filter @zent
|
|
|
100
103
|
| `src/ui/empty-state/empty-state.test.tsx` | 6 |
|
|
101
104
|
| `src/ui/search/filter-search-suggestions.test.ts` | 6 |
|
|
102
105
|
| `src/ui/toast/toast.test.tsx` | 6 |
|
|
103
|
-
| `cli/rewrite-imports.test.ts` | 5 |
|
|
104
106
|
| `src/hooks/useCookie/useCookie.test.ts` | 5 |
|
|
105
107
|
| `src/hooks/useDisclosure/useDisclosure.test.ts` | 5 |
|
|
106
108
|
| `src/hooks/useEventListener/useEventListener.test.ts` | 5 |
|
|
@@ -109,6 +111,7 @@ Generated from the component package Vitest JSON report via `pnpm --filter @zent
|
|
|
109
111
|
| `src/ui/popover/popover.test.tsx` | 5 |
|
|
110
112
|
| `src/ui/radio-group/radio-group.test.tsx` | 5 |
|
|
111
113
|
| `src/ui/toggle/toggle.test.tsx` | 5 |
|
|
114
|
+
| `cli/index.test.ts` | 4 |
|
|
112
115
|
| `src/hooks/useBodyScrollLock/useBodyScrollLock.test.ts` | 4 |
|
|
113
116
|
| `src/hooks/useControllableState/useControllableState.test.ts` | 4 |
|
|
114
117
|
| `src/hooks/useDebouncedValue/useDebouncedValue.test.ts` | 4 |
|
|
@@ -219,6 +222,7 @@ Import static primitives from `@zentauri-ui/zentauri-components/ui/<subpath>` wh
|
|
|
219
222
|
| Command | `command` | `command/animated` |
|
|
220
223
|
| Context menu | `context-menu` | — |
|
|
221
224
|
| Copy button | `copy-button` | `copy-button/animated` |
|
|
225
|
+
| DataTable | `data-table` | — |
|
|
222
226
|
| Divider | `divider` | `divider/animated` |
|
|
223
227
|
| Drawer | `drawer` | `drawer/animated` |
|
|
224
228
|
| Dropdown | `dropdown` | — |
|
|
@@ -848,7 +852,7 @@ From this package directory in the monorepo:
|
|
|
848
852
|
|
|
849
853
|
- `pnpm build` (or `npm run build`) — production bundle via `tsup` (Rollup treeshake + `scripts/prepend-use-client.mjs` via `onSuccess` so each UI entry under `dist/ui/`, animation entry under `dist/animations/`, chart entry under `dist/charts/`, and `dist/ui/<name>/animated.*` starts with `"use client"` where needed)
|
|
850
854
|
- `pnpm dev` — `tsup` watch mode (same `onSuccess` hook after each rebuild)
|
|
851
|
-
- `pnpm test` / `pnpm test:watch` — **Vitest** and **Testing Library** unit tests // currently covered
|
|
855
|
+
- `pnpm test` / `pnpm test:watch` — **Vitest** and **Testing Library** unit tests // currently covered 808 test cases in total
|
|
852
856
|
- `pnpm test:a11y` — focused accessibility coverage for package-level UI primitives and compound components: **axe-core** audits for every interactive component plus **keyboard-interaction** tests (focus order, arrow-key nav, Home/End, Escape/Enter) for the compound components
|
|
853
857
|
- `pnpm check:tokens` — enforce the `--zui-*` token contract across design-system, variant, and local custom-property usage without generating a large checked-in token catalog
|
|
854
858
|
- **`pnpm run generate:registry`** — runs `scripts/generate-registry.mjs`, which reads **`uiComponentNames`**, **`uiAnimatedComponentNames`**, **`animationEntryNames`**, **`chartEntryNames`**, and **`hooksEntryNames`** from `tsup.config.ts`, applies fixed **`nameAliases`**, scans each component/chart source to build **`peerHints`**, and writes **`cli/registry.json`** (`components` + `animations` + `hooks` + `peerHints`). Run this after adding or renaming UI, animation, chart, or hook entries so the CLI stays in sync (the script prints counts).
|
|
@@ -295,6 +295,42 @@ describe("zentauri-ui CLI", () => {
|
|
|
295
295
|
}
|
|
296
296
|
});
|
|
297
297
|
|
|
298
|
+
it("should add data-table with vendored sibling UI dependencies", () => {
|
|
299
|
+
const dir = mkdtempSync(join(tmpdir(), "zentauri-cli-data-table-"));
|
|
300
|
+
try {
|
|
301
|
+
runCli(dir, ["init"]);
|
|
302
|
+
const out = runCli(dir, ["add", "data-table"]);
|
|
303
|
+
expect(
|
|
304
|
+
existsSync(join(dir, "src/components/ui/data-table/data-table.tsx")),
|
|
305
|
+
).toBe(true);
|
|
306
|
+
expect(
|
|
307
|
+
existsSync(join(dir, "src/components/ui/buttons/button.tsx")),
|
|
308
|
+
).toBe(true);
|
|
309
|
+
expect(
|
|
310
|
+
existsSync(join(dir, "src/components/ui/checkbox/checkbox.tsx")),
|
|
311
|
+
).toBe(true);
|
|
312
|
+
expect(existsSync(join(dir, "src/components/ui/inputs/input.tsx"))).toBe(
|
|
313
|
+
true,
|
|
314
|
+
);
|
|
315
|
+
expect(
|
|
316
|
+
existsSync(join(dir, "src/components/ui/pagination/pagination.tsx")),
|
|
317
|
+
).toBe(true);
|
|
318
|
+
expect(existsSync(join(dir, "src/components/ui/table/table.tsx"))).toBe(
|
|
319
|
+
true,
|
|
320
|
+
);
|
|
321
|
+
const base = readFileSync(
|
|
322
|
+
join(dir, "src/components/ui/data-table/data-table-base.tsx"),
|
|
323
|
+
"utf8",
|
|
324
|
+
);
|
|
325
|
+
expect(base).toContain('from "@/components/ui/buttons"');
|
|
326
|
+
expect(base).not.toContain('from "../buttons"');
|
|
327
|
+
expect(out).toContain("Adding data-table…");
|
|
328
|
+
expect(out).toContain("Adding buttons…");
|
|
329
|
+
} finally {
|
|
330
|
+
rmSync(dir, { recursive: true, force: true });
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
|
|
298
334
|
it("should print react-icons peer hint for components that use icons", () => {
|
|
299
335
|
const dir = mkdtempSync(join(tmpdir(), "zentauri-cli-peer-"));
|
|
300
336
|
try {
|
package/cli/index.mjs
CHANGED
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
* command, or refused `init` overwrite. Successful runs leave default exit 0.
|
|
85
85
|
*/
|
|
86
86
|
|
|
87
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
87
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
88
88
|
import {
|
|
89
89
|
readFile,
|
|
90
90
|
writeFile,
|
|
@@ -548,6 +548,27 @@ function isAnimatedComponent(name, registry) {
|
|
|
548
548
|
return (registry.animatedComponents ?? []).includes(name);
|
|
549
549
|
}
|
|
550
550
|
|
|
551
|
+
function expandComponentDependencies(names, registry) {
|
|
552
|
+
const dependencies = registry.componentDependencies ?? {};
|
|
553
|
+
const expanded = [];
|
|
554
|
+
const seen = new Set();
|
|
555
|
+
const queue = [...names];
|
|
556
|
+
|
|
557
|
+
while (queue.length > 0) {
|
|
558
|
+
const name = queue.shift();
|
|
559
|
+
if (seen.has(name)) {
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
seen.add(name);
|
|
563
|
+
expanded.push(name);
|
|
564
|
+
for (const dependency of dependencies[name] ?? []) {
|
|
565
|
+
queue.push(dependency);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return expanded;
|
|
570
|
+
}
|
|
571
|
+
|
|
551
572
|
function printList(registry) {
|
|
552
573
|
const ui = Array.from(
|
|
553
574
|
new Set([
|
|
@@ -922,7 +943,13 @@ async function collectHookTransitiveClosure(packageRoot, seedHooks) {
|
|
|
922
943
|
* // src/components/ui/buttons/Button.tsx
|
|
923
944
|
* // with imports pointing at @/lib/utils, @/hooks/useX, etc.
|
|
924
945
|
*/
|
|
925
|
-
async function copyUiComponent(
|
|
946
|
+
async function copyUiComponent(
|
|
947
|
+
componentName,
|
|
948
|
+
config,
|
|
949
|
+
configDir,
|
|
950
|
+
packageRoot,
|
|
951
|
+
registry,
|
|
952
|
+
) {
|
|
926
953
|
const isChartEntry = componentName.startsWith("charts/");
|
|
927
954
|
const isAnimationEntry = componentName.startsWith("animations/");
|
|
928
955
|
const animationName = componentName.slice("animations/".length);
|
|
@@ -979,6 +1006,7 @@ async function copyUiComponent(componentName, config, configDir, packageRoot) {
|
|
|
979
1006
|
utilsAlias: config.aliases.utils,
|
|
980
1007
|
hooksAlias: config.aliases.hooks,
|
|
981
1008
|
uiAlias: config.aliases.ui,
|
|
1009
|
+
uiComponents: registry.uiComponents ?? [],
|
|
982
1010
|
});
|
|
983
1011
|
const rewrittenCode = designSystemImportTarget
|
|
984
1012
|
? rewriteDesignSystemBarrelImports(code, designSystemImportTarget)
|
|
@@ -1310,8 +1338,9 @@ async function cmdAdd(names, cwd, options = {}) {
|
|
|
1310
1338
|
}
|
|
1311
1339
|
|
|
1312
1340
|
const resolvedNames = payload.map((n) => resolveComponentName(n, registry));
|
|
1341
|
+
const requestedNames = [...resolvedNames];
|
|
1313
1342
|
if (animated) {
|
|
1314
|
-
for (const name of
|
|
1343
|
+
for (const name of requestedNames) {
|
|
1315
1344
|
if (!isAnimatedComponent(name, registry)) {
|
|
1316
1345
|
console.error(`Component "${name}" has no animated entry.`);
|
|
1317
1346
|
process.exitCode = 1;
|
|
@@ -1319,17 +1348,24 @@ async function cmdAdd(names, cwd, options = {}) {
|
|
|
1319
1348
|
}
|
|
1320
1349
|
}
|
|
1321
1350
|
}
|
|
1351
|
+
const namesToCopy = expandComponentDependencies(requestedNames, registry);
|
|
1322
1352
|
|
|
1323
1353
|
await ensureUtilsFile(config, configDir, packageRoot);
|
|
1324
|
-
await copyDesignSystemFiles(
|
|
1354
|
+
await copyDesignSystemFiles(namesToCopy, config, configDir, packageRoot);
|
|
1325
1355
|
|
|
1326
1356
|
const allHooks = new Set();
|
|
1327
|
-
for (const name of
|
|
1357
|
+
for (const name of namesToCopy) {
|
|
1328
1358
|
console.log(`Adding ${name}…`);
|
|
1329
|
-
if (animated) {
|
|
1359
|
+
if (animated && requestedNames.includes(name)) {
|
|
1330
1360
|
console.log(`Including animated entry for ${name}…`);
|
|
1331
1361
|
}
|
|
1332
|
-
const uh = await copyUiComponent(
|
|
1362
|
+
const uh = await copyUiComponent(
|
|
1363
|
+
name,
|
|
1364
|
+
config,
|
|
1365
|
+
configDir,
|
|
1366
|
+
packageRoot,
|
|
1367
|
+
registry,
|
|
1368
|
+
);
|
|
1333
1369
|
for (const h of uh) {
|
|
1334
1370
|
allHooks.add(h);
|
|
1335
1371
|
}
|
|
@@ -1345,7 +1381,7 @@ async function cmdAdd(names, cwd, options = {}) {
|
|
|
1345
1381
|
}
|
|
1346
1382
|
|
|
1347
1383
|
console.log("Done.");
|
|
1348
|
-
printAdoptionHints(
|
|
1384
|
+
printAdoptionHints(namesToCopy, registry, config, configDir);
|
|
1349
1385
|
}
|
|
1350
1386
|
|
|
1351
1387
|
async function cmdTheme(hex, options, cwd) {
|
|
@@ -1464,7 +1500,50 @@ async function main() {
|
|
|
1464
1500
|
process.exitCode = 1;
|
|
1465
1501
|
}
|
|
1466
1502
|
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1503
|
+
export {
|
|
1504
|
+
buildCompactThemeCss,
|
|
1505
|
+
cmdAdd,
|
|
1506
|
+
cmdInit,
|
|
1507
|
+
cmdTheme,
|
|
1508
|
+
collectHookTransitiveClosure,
|
|
1509
|
+
copyDesignSystemFiles,
|
|
1510
|
+
copyHookFolder,
|
|
1511
|
+
copyUiComponent,
|
|
1512
|
+
defaultConfig,
|
|
1513
|
+
detectFramework,
|
|
1514
|
+
findComponentsJson,
|
|
1515
|
+
getMissingDependencies,
|
|
1516
|
+
importPathFor,
|
|
1517
|
+
isTestFile,
|
|
1518
|
+
loadRegistry,
|
|
1519
|
+
main,
|
|
1520
|
+
normalizeHexColor,
|
|
1521
|
+
printAdoptionHints,
|
|
1522
|
+
printInfo,
|
|
1523
|
+
printList,
|
|
1524
|
+
resolveComponentName,
|
|
1525
|
+
resolveHookName,
|
|
1526
|
+
validateConfig,
|
|
1527
|
+
walkFiles,
|
|
1528
|
+
};
|
|
1529
|
+
|
|
1530
|
+
function isDirectCliRun() {
|
|
1531
|
+
if (!process.argv[1]) {
|
|
1532
|
+
return false;
|
|
1533
|
+
}
|
|
1534
|
+
try {
|
|
1535
|
+
return (
|
|
1536
|
+
realpathSync(process.argv[1]) ===
|
|
1537
|
+
realpathSync(fileURLToPath(import.meta.url))
|
|
1538
|
+
);
|
|
1539
|
+
} catch {
|
|
1540
|
+
return resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
if (isDirectCliRun()) {
|
|
1545
|
+
main().catch((err) => {
|
|
1546
|
+
console.error(err instanceof Error ? err.message : err);
|
|
1547
|
+
process.exitCode = 1;
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdtempSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
rmSync,
|
|
6
|
+
writeFileSync,
|
|
7
|
+
} from "node:fs";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
|
|
11
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
cmdAdd,
|
|
15
|
+
cmdInit,
|
|
16
|
+
cmdTheme,
|
|
17
|
+
defaultConfig,
|
|
18
|
+
importPathFor,
|
|
19
|
+
normalizeHexColor,
|
|
20
|
+
resolveComponentName,
|
|
21
|
+
resolveHookName,
|
|
22
|
+
validateConfig,
|
|
23
|
+
} from "./index.mjs";
|
|
24
|
+
|
|
25
|
+
type TestRegistry = Parameters<typeof resolveComponentName>[1] &
|
|
26
|
+
Parameters<typeof resolveHookName>[1];
|
|
27
|
+
|
|
28
|
+
function makeTempDir(prefix: string) {
|
|
29
|
+
return mkdtempSync(join(tmpdir(), prefix));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function silenceConsole() {
|
|
33
|
+
const logs: string[] = [];
|
|
34
|
+
const errors: string[] = [];
|
|
35
|
+
|
|
36
|
+
vi.spyOn(console, "log").mockImplementation((...args) => {
|
|
37
|
+
logs.push(args.join(" "));
|
|
38
|
+
});
|
|
39
|
+
vi.spyOn(console, "error").mockImplementation((...args) => {
|
|
40
|
+
errors.push(args.join(" "));
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return { errors, logs };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
describe("CLI module commands", () => {
|
|
47
|
+
afterEach(() => {
|
|
48
|
+
vi.restoreAllMocks();
|
|
49
|
+
process.exitCode = undefined;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("resolves aliases, hooks, imports, hex colors, and config validation", () => {
|
|
53
|
+
const registry: TestRegistry = {
|
|
54
|
+
components: ["buttons", "card", "charts/line"],
|
|
55
|
+
hooks: ["useWindowSize"],
|
|
56
|
+
nameAliases: { button: "buttons" },
|
|
57
|
+
animatedComponents: ["buttons", "spinner"],
|
|
58
|
+
uiComponents: ["buttons", "card"],
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
expect(resolveComponentName("button", registry)).toBe("buttons");
|
|
62
|
+
expect(resolveComponentName("CARD", registry)).toBe("card");
|
|
63
|
+
expect(resolveHookName("usewindowsize", registry)).toBe("useWindowSize");
|
|
64
|
+
expect(importPathFor("useWindowSize", "hook", registry)).toBe(
|
|
65
|
+
"@zentauri-ui/zentauri-components/hooks/useWindowSize",
|
|
66
|
+
);
|
|
67
|
+
expect(importPathFor("charts/line", "component", registry)).toBe(
|
|
68
|
+
"@zentauri-ui/zentauri-components/charts/line",
|
|
69
|
+
);
|
|
70
|
+
expect(importPathFor("spinner", "component", registry)).toBe(
|
|
71
|
+
"@zentauri-ui/zentauri-components/ui/spinner/animated",
|
|
72
|
+
);
|
|
73
|
+
expect(normalizeHexColor("38b")).toBe("#3388bb");
|
|
74
|
+
expect(() => validateConfig(defaultConfig())).not.toThrow();
|
|
75
|
+
expect(() => resolveComponentName("missing", registry)).toThrow(
|
|
76
|
+
/Unknown component/,
|
|
77
|
+
);
|
|
78
|
+
expect(() => normalizeHexColor("not-a-color")).toThrow(
|
|
79
|
+
/Invalid brand color/,
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("initializes components.json with framework-aware guidance", async () => {
|
|
84
|
+
const dir = makeTempDir("zentauri-cli-module-init-");
|
|
85
|
+
const { errors, logs } = silenceConsole();
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
writeFileSync(
|
|
89
|
+
join(dir, "package.json"),
|
|
90
|
+
JSON.stringify({ dependencies: { next: "16.0.0" } }),
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
await cmdInit(dir);
|
|
94
|
+
|
|
95
|
+
expect(existsSync(join(dir, "components.json"))).toBe(true);
|
|
96
|
+
expect(
|
|
97
|
+
JSON.parse(readFileSync(join(dir, "components.json"), "utf8")),
|
|
98
|
+
).toEqual(defaultConfig());
|
|
99
|
+
expect(logs.join("\n")).toContain("Detected framework: Next.js");
|
|
100
|
+
expect(logs.join("\n")).toContain('@source "./src/components/ui";');
|
|
101
|
+
|
|
102
|
+
await cmdInit(dir);
|
|
103
|
+
|
|
104
|
+
expect(process.exitCode).toBe(1);
|
|
105
|
+
expect(errors.join("\n")).toContain("Refusing to overwrite existing");
|
|
106
|
+
} finally {
|
|
107
|
+
rmSync(dir, { recursive: true, force: true });
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("vendors components, animated entries, design tokens, and transitive hooks", async () => {
|
|
112
|
+
const dir = makeTempDir("zentauri-cli-module-add-");
|
|
113
|
+
const { logs } = silenceConsole();
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
await cmdInit(dir);
|
|
117
|
+
await cmdAdd(["button"], dir);
|
|
118
|
+
await cmdAdd(["hook", "usePrefersReducedMotion"], dir);
|
|
119
|
+
await cmdAdd(["button"], dir, { animated: true });
|
|
120
|
+
|
|
121
|
+
expect(
|
|
122
|
+
existsSync(join(dir, "src/components/ui/buttons/button.tsx")),
|
|
123
|
+
).toBe(true);
|
|
124
|
+
expect(
|
|
125
|
+
existsSync(join(dir, "src/components/ui/buttons/animated/index.ts")),
|
|
126
|
+
).toBe(true);
|
|
127
|
+
expect(
|
|
128
|
+
existsSync(join(dir, "src/components/design-system/button.ts")),
|
|
129
|
+
).toBe(true);
|
|
130
|
+
expect(
|
|
131
|
+
existsSync(join(dir, "src/components/design-system/tokens.ts")),
|
|
132
|
+
).toBe(true);
|
|
133
|
+
expect(
|
|
134
|
+
existsSync(
|
|
135
|
+
join(
|
|
136
|
+
dir,
|
|
137
|
+
"src/hooks/usePrefersReducedMotion/usePrefersReducedMotion.ts",
|
|
138
|
+
),
|
|
139
|
+
),
|
|
140
|
+
).toBe(true);
|
|
141
|
+
expect(
|
|
142
|
+
existsSync(join(dir, "src/hooks/useMediaQuery/useMediaQuery.ts")),
|
|
143
|
+
).toBe(true);
|
|
144
|
+
expect(
|
|
145
|
+
readFileSync(
|
|
146
|
+
join(dir, "src/components/ui/buttons/button-base.tsx"),
|
|
147
|
+
"utf8",
|
|
148
|
+
),
|
|
149
|
+
).toContain('from "@/lib/utils"');
|
|
150
|
+
expect(logs.join("\n")).toContain("Including animated entry for buttons");
|
|
151
|
+
expect(logs.join("\n")).toContain("Missing peer dependencies");
|
|
152
|
+
} finally {
|
|
153
|
+
rmSync(dir, { recursive: true, force: true });
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("generates theme CSS to stdout or a requested file", async () => {
|
|
158
|
+
const dir = makeTempDir("zentauri-cli-module-theme-");
|
|
159
|
+
const { errors, logs } = silenceConsole();
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
await cmdTheme("#2563eb", { dark: "#60a5fa" }, dir);
|
|
163
|
+
expect(logs.join("\n")).toContain("--zui-brand: #2563eb;");
|
|
164
|
+
expect(logs.join("\n")).toContain("--zui-brand-dark: #60a5fa;");
|
|
165
|
+
|
|
166
|
+
await cmdTheme("38bdf8", { out: "src/styles/zentauri-theme.css" }, dir);
|
|
167
|
+
const themePath = join(dir, "src/styles/zentauri-theme.css");
|
|
168
|
+
expect(existsSync(themePath)).toBe(true);
|
|
169
|
+
expect(readFileSync(themePath, "utf8")).toContain(
|
|
170
|
+
"--zui-brand: #38bdf8;",
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
await cmdTheme("", {}, dir);
|
|
174
|
+
expect(process.exitCode).toBe(1);
|
|
175
|
+
expect(errors.join("\n")).toContain("Usage: zentauri-components theme");
|
|
176
|
+
} finally {
|
|
177
|
+
rmSync(dir, { recursive: true, force: true });
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|