apex-design-cli 1.0.0

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.
Files changed (66) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +747 -0
  3. package/package.json +57 -0
  4. package/registry/components/accordion.json +26 -0
  5. package/registry/components/alert.json +25 -0
  6. package/registry/components/avatar.json +26 -0
  7. package/registry/components/badge.json +24 -0
  8. package/registry/components/breadcrumb.json +28 -0
  9. package/registry/components/button.json +25 -0
  10. package/registry/components/card.json +28 -0
  11. package/registry/components/checkbox.json +23 -0
  12. package/registry/components/command.json +31 -0
  13. package/registry/components/dialog.json +32 -0
  14. package/registry/components/divider.json +22 -0
  15. package/registry/components/dropdown-menu.json +36 -0
  16. package/registry/components/empty-state.json +21 -0
  17. package/registry/components/error-message.json +20 -0
  18. package/registry/components/field-group.json +20 -0
  19. package/registry/components/helper-text.json +20 -0
  20. package/registry/components/input.json +21 -0
  21. package/registry/components/label.json +20 -0
  22. package/registry/components/progress.json +22 -0
  23. package/registry/components/radio.json +23 -0
  24. package/registry/components/select.json +32 -0
  25. package/registry/components/spinner.json +20 -0
  26. package/registry/components/switch.json +22 -0
  27. package/registry/components/table.json +27 -0
  28. package/registry/components/tabs.json +25 -0
  29. package/registry/components/textarea.json +21 -0
  30. package/registry/components/theme-toggler.json +24 -0
  31. package/registry/components/toast.json +31 -0
  32. package/registry/components/tooltip.json +26 -0
  33. package/registry/components/use-theme.json +19 -0
  34. package/registry/components/utils.json +21 -0
  35. package/registry/registry.json +35 -0
  36. package/registry/source/accordion.tsx +55 -0
  37. package/registry/source/alert.tsx +102 -0
  38. package/registry/source/avatar.tsx +137 -0
  39. package/registry/source/badge.tsx +38 -0
  40. package/registry/source/breadcrumb.tsx +109 -0
  41. package/registry/source/button.tsx +58 -0
  42. package/registry/source/card.tsx +108 -0
  43. package/registry/source/checkbox.tsx +170 -0
  44. package/registry/source/command.tsx +195 -0
  45. package/registry/source/dialog.tsx +133 -0
  46. package/registry/source/divider.tsx +84 -0
  47. package/registry/source/dropdown-menu.tsx +209 -0
  48. package/registry/source/empty-state.tsx +88 -0
  49. package/registry/source/error-message.tsx +49 -0
  50. package/registry/source/field-group.tsx +53 -0
  51. package/registry/source/helper-text.tsx +40 -0
  52. package/registry/source/input.tsx +219 -0
  53. package/registry/source/label.tsx +60 -0
  54. package/registry/source/progress.tsx +84 -0
  55. package/registry/source/radio.tsx +161 -0
  56. package/registry/source/select.tsx +278 -0
  57. package/registry/source/spinner.tsx +84 -0
  58. package/registry/source/switch.tsx +104 -0
  59. package/registry/source/table.tsx +116 -0
  60. package/registry/source/tabs.tsx +55 -0
  61. package/registry/source/textarea.tsx +129 -0
  62. package/registry/source/theme-toggler.tsx +94 -0
  63. package/registry/source/toast.tsx +166 -0
  64. package/registry/source/tooltip.tsx +55 -0
  65. package/registry/source/use-theme.tsx +102 -0
  66. package/registry/source/utils.ts +13 -0
package/dist/index.js ADDED
@@ -0,0 +1,747 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+
6
+ // src/commands/init.ts
7
+ import fs3 from "fs-extra";
8
+ import path3 from "path";
9
+ import ora from "ora";
10
+ import prompts from "prompts";
11
+ import chalk2 from "chalk";
12
+
13
+ // src/utils/config.ts
14
+ import fs from "fs-extra";
15
+ import path from "path";
16
+ import { z } from "zod";
17
+ var CONFIG_FILE = "apex.config.json";
18
+ var ConfigSchema = z.object({
19
+ $schema: z.string().optional(),
20
+ version: z.string(),
21
+ project: z.object({
22
+ type: z.enum(["react", "next", "vite", "other"]),
23
+ typescript: z.boolean(),
24
+ packageManager: z.enum(["npm", "yarn", "pnpm", "bun"])
25
+ }),
26
+ paths: z.object({
27
+ components: z.string(),
28
+ lib: z.string(),
29
+ hooks: z.string()
30
+ }),
31
+ aliases: z.object({
32
+ enabled: z.boolean(),
33
+ components: z.string(),
34
+ lib: z.string(),
35
+ hooks: z.string()
36
+ }),
37
+ tailwind: z.object({
38
+ config: z.string(),
39
+ css: z.string()
40
+ })
41
+ });
42
+ async function loadConfig(cwd = process.cwd()) {
43
+ const configPath = path.join(cwd, CONFIG_FILE);
44
+ if (!await fs.pathExists(configPath)) {
45
+ return null;
46
+ }
47
+ try {
48
+ const raw = await fs.readJson(configPath);
49
+ return ConfigSchema.parse(raw);
50
+ } catch {
51
+ return null;
52
+ }
53
+ }
54
+ async function writeConfig(config, cwd = process.cwd()) {
55
+ const configPath = path.join(cwd, CONFIG_FILE);
56
+ await fs.writeJson(configPath, config, { spaces: 2 });
57
+ }
58
+ function getDefaultConfig(overrides = {}) {
59
+ return {
60
+ $schema: "https://apex-design-system.vercel.app/schema/config.json",
61
+ version: "1.0.0",
62
+ project: {
63
+ type: "react",
64
+ typescript: true,
65
+ packageManager: "npm",
66
+ ...overrides.project
67
+ },
68
+ paths: {
69
+ components: "./src/components/ui",
70
+ lib: "./src/lib",
71
+ hooks: "./src/hooks",
72
+ ...overrides.paths
73
+ },
74
+ aliases: {
75
+ enabled: true,
76
+ components: "@/components/ui",
77
+ lib: "@/lib",
78
+ hooks: "@/hooks",
79
+ ...overrides.aliases
80
+ },
81
+ tailwind: {
82
+ config: "./tailwind.config.js",
83
+ css: "./src/index.css",
84
+ ...overrides.tailwind
85
+ }
86
+ };
87
+ }
88
+
89
+ // src/utils/project-detector.ts
90
+ import fs2 from "fs-extra";
91
+ import path2 from "path";
92
+ async function detectProject(cwd = process.cwd()) {
93
+ const packageManager = await detectPackageManager(cwd);
94
+ const typescript = await detectTypeScript(cwd);
95
+ const type = await detectProjectType(cwd);
96
+ return {
97
+ type,
98
+ typescript,
99
+ packageManager,
100
+ rootDir: cwd
101
+ };
102
+ }
103
+ async function detectPackageManager(cwd) {
104
+ if (await fs2.pathExists(path2.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
105
+ if (await fs2.pathExists(path2.join(cwd, "yarn.lock"))) return "yarn";
106
+ if (await fs2.pathExists(path2.join(cwd, "bun.lockb"))) return "bun";
107
+ return "npm";
108
+ }
109
+ async function detectTypeScript(cwd) {
110
+ return await fs2.pathExists(path2.join(cwd, "tsconfig.json")) || await fs2.pathExists(path2.join(cwd, "tsconfig.app.json"));
111
+ }
112
+ async function detectProjectType(cwd) {
113
+ try {
114
+ const pkg = await fs2.readJson(path2.join(cwd, "package.json"));
115
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
116
+ if (allDeps["next"]) return "next";
117
+ if (allDeps["vite"]) return "vite";
118
+ if (allDeps["react"]) return "react";
119
+ } catch {
120
+ }
121
+ return "other";
122
+ }
123
+
124
+ // src/utils/installer.ts
125
+ import { execa } from "execa";
126
+ async function installDependencies(deps, packageManager, cwd = process.cwd()) {
127
+ if (deps.length === 0) return;
128
+ const installCmd = getInstallCommand(packageManager);
129
+ const args = [...installCmd.args, ...deps];
130
+ await execa(installCmd.command, args, {
131
+ cwd,
132
+ stdio: "pipe"
133
+ });
134
+ }
135
+ function getInstallCommand(pm) {
136
+ switch (pm) {
137
+ case "pnpm":
138
+ return { command: "pnpm", args: ["add"] };
139
+ case "yarn":
140
+ return { command: "yarn", args: ["add"] };
141
+ case "bun":
142
+ return { command: "bun", args: ["add"] };
143
+ case "npm":
144
+ default:
145
+ return { command: "npm", args: ["install"] };
146
+ }
147
+ }
148
+ function parseDependency(dep) {
149
+ const atIdx = dep.lastIndexOf("@");
150
+ if (atIdx > 0) {
151
+ return {
152
+ name: dep.substring(0, atIdx),
153
+ version: dep.substring(atIdx + 1)
154
+ };
155
+ }
156
+ return { name: dep, version: "latest" };
157
+ }
158
+
159
+ // src/utils/logger.ts
160
+ import chalk from "chalk";
161
+ var logger = {
162
+ info: (msg) => console.log(chalk.cyan("\u2139"), msg),
163
+ success: (msg) => console.log(chalk.green("\u2713"), msg),
164
+ warn: (msg) => console.log(chalk.yellow("\u26A0"), msg),
165
+ error: (msg) => console.log(chalk.red("\u2717"), msg),
166
+ break: () => console.log(""),
167
+ title: (msg) => {
168
+ console.log("");
169
+ console.log(chalk.bold.cyan(msg));
170
+ console.log(chalk.gray("\u2500".repeat(50)));
171
+ },
172
+ component: (name, description, badges = []) => {
173
+ const badgeStr = badges.map((b) => chalk.gray(`[${b}]`)).join(" ");
174
+ console.log(` ${chalk.cyan(name.padEnd(22))} ${badgeStr}`);
175
+ if (description) {
176
+ console.log(` ${chalk.gray(description)}`);
177
+ }
178
+ },
179
+ step: (current, total, msg) => {
180
+ console.log(chalk.gray(` [${current}/${total}]`), msg);
181
+ },
182
+ importExample: (importStr) => {
183
+ console.log(chalk.yellow(` ${importStr}`));
184
+ }
185
+ };
186
+
187
+ // src/commands/init.ts
188
+ var CSS_VARIABLES = `
189
+ /* APEX Design System - CSS Variables */
190
+ :root {
191
+ /* Backgrounds */
192
+ --color-bg-primary: #ffffff;
193
+ --color-bg-secondary: #f8fafc;
194
+ --color-bg-tertiary: #f1f5f9;
195
+ --color-bg-inverse: #0f172a;
196
+
197
+ /* Foregrounds */
198
+ --color-fg-primary: #0f172a;
199
+ --color-fg-secondary: #475569;
200
+ --color-fg-muted: #64748b;
201
+ --color-fg-inverse: #ffffff;
202
+ --color-fg-disabled: #94a3b8;
203
+
204
+ /* Borders */
205
+ --color-border-default: #e2e8f0;
206
+ --color-border-strong: #cbd5e1;
207
+ --color-border-focus: #3b82f6;
208
+ --color-border-error: #ef4444;
209
+
210
+ /* Shadows */
211
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
212
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
213
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
214
+ }
215
+
216
+ .dark {
217
+ --color-bg-primary: #0f172a;
218
+ --color-bg-secondary: #1e293b;
219
+ --color-bg-tertiary: #334155;
220
+ --color-bg-inverse: #f8fafc;
221
+
222
+ --color-fg-primary: #f8fafc;
223
+ --color-fg-secondary: #cbd5e1;
224
+ --color-fg-muted: #94a3b8;
225
+ --color-fg-inverse: #0f172a;
226
+ --color-fg-disabled: #475569;
227
+
228
+ --color-border-default: #334155;
229
+ --color-border-strong: #475569;
230
+ --color-border-focus: #60a5fa;
231
+ --color-border-error: #f87171;
232
+
233
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.3);
234
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.3);
235
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.5), 0 4px 6px -4px rgb(0 0 0 / 0.4);
236
+ }
237
+
238
+ /* Reduced Motion Support */
239
+ @media (prefers-reduced-motion: reduce) {
240
+ *,
241
+ *::before,
242
+ *::after {
243
+ animation-duration: 0.01ms !important;
244
+ animation-iteration-count: 1 !important;
245
+ transition-duration: 0.01ms !important;
246
+ scroll-behavior: auto !important;
247
+ }
248
+ }
249
+ `;
250
+ var UTILS_FILE = `import { type ClassValue, clsx } from "clsx";
251
+ import { twMerge } from "tailwind-merge";
252
+
253
+ export function cn(...inputs: ClassValue[]) {
254
+ return twMerge(clsx(inputs));
255
+ }
256
+ `;
257
+ async function init(options) {
258
+ console.log("");
259
+ console.log(chalk2.bold.cyan(" APEX Design System"));
260
+ console.log(chalk2.gray(" Initialize your project"));
261
+ console.log("");
262
+ const cwd = process.cwd();
263
+ const existingConfig = await loadConfig(cwd);
264
+ if (existingConfig && !options.yes) {
265
+ const { overwrite } = await prompts({
266
+ type: "confirm",
267
+ name: "overwrite",
268
+ message: "apex.config.json already exists. Overwrite?",
269
+ initial: false
270
+ });
271
+ if (!overwrite) {
272
+ logger.info("Cancelled.");
273
+ return;
274
+ }
275
+ }
276
+ const spinner = ora("Detecting project...").start();
277
+ const projectInfo = await detectProject(cwd);
278
+ spinner.succeed(`Detected: ${projectInfo.type} (${projectInfo.typescript ? "TypeScript" : "JavaScript"}) with ${projectInfo.packageManager}`);
279
+ let config;
280
+ if (options.yes) {
281
+ config = getDefaultConfig({
282
+ project: {
283
+ type: projectInfo.type,
284
+ typescript: projectInfo.typescript,
285
+ packageManager: projectInfo.packageManager
286
+ }
287
+ });
288
+ } else {
289
+ const answers = await prompts([
290
+ {
291
+ type: "text",
292
+ name: "componentsPath",
293
+ message: "Where should components be installed?",
294
+ initial: "./src/components/ui"
295
+ },
296
+ {
297
+ type: "text",
298
+ name: "libPath",
299
+ message: "Where should utilities go?",
300
+ initial: "./src/lib"
301
+ },
302
+ {
303
+ type: "text",
304
+ name: "hooksPath",
305
+ message: "Where should hooks be installed?",
306
+ initial: "./src/hooks"
307
+ },
308
+ {
309
+ type: "confirm",
310
+ name: "useAliases",
311
+ message: "Use path aliases (@/)?",
312
+ initial: projectInfo.typescript
313
+ },
314
+ {
315
+ type: "text",
316
+ name: "tailwindConfig",
317
+ message: "Path to tailwind config?",
318
+ initial: "./tailwind.config.js"
319
+ },
320
+ {
321
+ type: "text",
322
+ name: "cssFile",
323
+ message: "Path to global CSS file?",
324
+ initial: "./src/index.css"
325
+ }
326
+ ]);
327
+ config = getDefaultConfig({
328
+ project: {
329
+ type: projectInfo.type,
330
+ typescript: projectInfo.typescript,
331
+ packageManager: projectInfo.packageManager
332
+ },
333
+ paths: {
334
+ components: answers.componentsPath,
335
+ lib: answers.libPath,
336
+ hooks: answers.hooksPath
337
+ },
338
+ aliases: {
339
+ enabled: answers.useAliases,
340
+ components: answers.useAliases ? "@/components/ui" : answers.componentsPath,
341
+ lib: answers.useAliases ? "@/lib" : answers.libPath,
342
+ hooks: answers.useAliases ? "@/hooks" : answers.hooksPath
343
+ },
344
+ tailwind: {
345
+ config: answers.tailwindConfig,
346
+ css: answers.cssFile
347
+ }
348
+ });
349
+ }
350
+ const setupSpinner = ora("Setting up project...").start();
351
+ setupSpinner.text = "Writing apex.config.json...";
352
+ await writeConfig(config, cwd);
353
+ setupSpinner.text = "Creating directories...";
354
+ await fs3.ensureDir(path3.resolve(cwd, config.paths.components));
355
+ await fs3.ensureDir(path3.resolve(cwd, config.paths.lib));
356
+ await fs3.ensureDir(path3.resolve(cwd, config.paths.hooks));
357
+ setupSpinner.text = "Creating utility files...";
358
+ const utilsPath = path3.resolve(cwd, config.paths.lib, "utils.ts");
359
+ if (!await fs3.pathExists(utilsPath)) {
360
+ await fs3.writeFile(utilsPath, UTILS_FILE);
361
+ }
362
+ setupSpinner.text = "Adding design tokens to CSS...";
363
+ const cssPath = path3.resolve(cwd, config.tailwind.css);
364
+ if (await fs3.pathExists(cssPath)) {
365
+ const existingCss = await fs3.readFile(cssPath, "utf-8");
366
+ if (!existingCss.includes("--color-bg-primary")) {
367
+ await fs3.appendFile(cssPath, "\n" + CSS_VARIABLES);
368
+ }
369
+ } else {
370
+ await fs3.writeFile(cssPath, CSS_VARIABLES);
371
+ }
372
+ if (config.aliases.enabled) {
373
+ setupSpinner.text = "Configuring path aliases...";
374
+ await updateTsConfig(cwd);
375
+ }
376
+ setupSpinner.succeed("Project configured");
377
+ const depsSpinner = ora("Installing base dependencies...").start();
378
+ try {
379
+ await installDependencies(
380
+ ["clsx", "tailwind-merge", "class-variance-authority"],
381
+ config.project.packageManager,
382
+ cwd
383
+ );
384
+ depsSpinner.succeed("Base dependencies installed");
385
+ } catch {
386
+ depsSpinner.warn("Could not install dependencies automatically. Install manually:");
387
+ logger.info(` ${config.project.packageManager} install clsx tailwind-merge class-variance-authority`);
388
+ }
389
+ console.log("");
390
+ console.log(chalk2.bold.green(" \u2705 APEX Design System initialized!"));
391
+ console.log("");
392
+ console.log(chalk2.gray(" Next steps:"));
393
+ console.log(chalk2.cyan(" npx apex-design-cli list") + chalk2.gray(" Browse available components"));
394
+ console.log(chalk2.cyan(" npx apex-design-cli add button") + chalk2.gray("Install your first component"));
395
+ console.log("");
396
+ }
397
+ async function updateTsConfig(cwd) {
398
+ const tsconfigPath = path3.join(cwd, "tsconfig.json");
399
+ if (!await fs3.pathExists(tsconfigPath)) return;
400
+ try {
401
+ const tsconfig = await fs3.readJson(tsconfigPath);
402
+ if (!tsconfig.compilerOptions) {
403
+ tsconfig.compilerOptions = {};
404
+ }
405
+ if (!tsconfig.compilerOptions.baseUrl) {
406
+ tsconfig.compilerOptions.baseUrl = ".";
407
+ }
408
+ if (!tsconfig.compilerOptions.paths) {
409
+ tsconfig.compilerOptions.paths = {};
410
+ }
411
+ tsconfig.compilerOptions.paths["@/*"] = ["./src/*"];
412
+ await fs3.writeJson(tsconfigPath, tsconfig, { spaces: 2 });
413
+ } catch {
414
+ }
415
+ }
416
+
417
+ // src/commands/add.ts
418
+ import fs5 from "fs-extra";
419
+ import path5 from "path";
420
+ import ora2 from "ora";
421
+ import prompts2 from "prompts";
422
+ import chalk3 from "chalk";
423
+
424
+ // src/utils/registry.ts
425
+ import fs4 from "fs-extra";
426
+ import path4 from "path";
427
+ import { fileURLToPath } from "url";
428
+ var __filename = fileURLToPath(import.meta.url);
429
+ var __dirname = path4.dirname(__filename);
430
+ function getPackageRoot() {
431
+ let dir = __dirname;
432
+ while (dir !== path4.dirname(dir)) {
433
+ if (fs4.existsSync(path4.join(dir, "package.json"))) {
434
+ return dir;
435
+ }
436
+ dir = path4.dirname(dir);
437
+ }
438
+ return __dirname;
439
+ }
440
+ function getRegistryDir() {
441
+ return path4.join(getPackageRoot(), "registry");
442
+ }
443
+ async function loadRegistry() {
444
+ const registryPath = path4.join(getRegistryDir(), "registry.json");
445
+ return fs4.readJson(registryPath);
446
+ }
447
+ async function loadComponentRegistry(name) {
448
+ const componentPath = path4.join(getRegistryDir(), "components", `${name}.json`);
449
+ if (!await fs4.pathExists(componentPath)) {
450
+ return null;
451
+ }
452
+ return fs4.readJson(componentPath);
453
+ }
454
+ async function resolveComponentSource(file) {
455
+ const packageRoot = getPackageRoot();
456
+ const sourcePath = path4.resolve(packageRoot, file.path);
457
+ if (!await fs4.pathExists(sourcePath)) {
458
+ throw new Error(`Source file not found: ${sourcePath}`);
459
+ }
460
+ return fs4.readFile(sourcePath, "utf-8");
461
+ }
462
+ async function resolveDependencyTree(componentNames) {
463
+ const resolved = /* @__PURE__ */ new Map();
464
+ const queue = [...componentNames];
465
+ while (queue.length > 0) {
466
+ const name = queue.shift();
467
+ if (resolved.has(name)) continue;
468
+ const component = await loadComponentRegistry(name);
469
+ if (!component) continue;
470
+ resolved.set(name, component);
471
+ for (const dep of component.registryDependencies) {
472
+ if (!resolved.has(dep)) {
473
+ queue.push(dep);
474
+ }
475
+ }
476
+ }
477
+ return Array.from(resolved.values());
478
+ }
479
+ function groupByCategory(components) {
480
+ return components.reduce((acc, component) => {
481
+ const category = component.category;
482
+ if (!acc[category]) {
483
+ acc[category] = [];
484
+ }
485
+ acc[category].push(component);
486
+ return acc;
487
+ }, {});
488
+ }
489
+
490
+ // src/utils/transformer.ts
491
+ function transformImports(sourceCode, config) {
492
+ let transformed = sourceCode;
493
+ if (config.aliases.enabled) {
494
+ transformed = transformed.replace(
495
+ /from\s+['"]\.\.\/\.\.\/lib\/utils['"]/g,
496
+ `from '${config.aliases.lib}/utils'`
497
+ );
498
+ transformed = transformed.replace(
499
+ /from\s+['"]\.\.\/\.\.\/hooks\/useTheme['"]/g,
500
+ `from '${config.aliases.hooks}/use-theme'`
501
+ );
502
+ transformed = transformed.replace(
503
+ /from\s+['"]\.\.\/(\w+)\/\1['"]/g,
504
+ (_match, componentName) => {
505
+ const kebab = toKebabCase(componentName);
506
+ return `from '${config.aliases.components}/${kebab}'`;
507
+ }
508
+ );
509
+ transformed = transformed.replace(
510
+ /from\s+['"]\.\.\/(\w+)['"]/g,
511
+ (_match, componentName) => {
512
+ const kebab = toKebabCase(componentName);
513
+ return `from '${config.aliases.components}/${kebab}'`;
514
+ }
515
+ );
516
+ } else {
517
+ transformed = transformed.replace(
518
+ /from\s+['"]\.\.\/\.\.\/lib\/utils['"]/g,
519
+ `from '../../lib/utils'`
520
+ );
521
+ transformed = transformed.replace(
522
+ /from\s+['"]\.\.\/\.\.\/hooks\/useTheme['"]/g,
523
+ `from '../../hooks/use-theme'`
524
+ );
525
+ }
526
+ return transformed;
527
+ }
528
+ function toKebabCase(str) {
529
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
530
+ }
531
+
532
+ // src/commands/add.ts
533
+ async function add(componentNames, options) {
534
+ const cwd = process.cwd();
535
+ const config = await loadConfig(cwd);
536
+ if (!config) {
537
+ logger.error("apex.config.json not found. Run first:");
538
+ console.log(chalk3.cyan("\n npx apex-design-cli init\n"));
539
+ process.exit(1);
540
+ }
541
+ const spinner = ora2("Loading registry...").start();
542
+ const registry = await loadRegistry();
543
+ const validNames = [];
544
+ for (const name of componentNames) {
545
+ const normalized = name.toLowerCase().replace(/\s+/g, "-");
546
+ if (registry.components.includes(normalized)) {
547
+ validNames.push(normalized);
548
+ } else {
549
+ spinner.stop();
550
+ logger.warn(`Component "${name}" not found. Skipping.`);
551
+ spinner.start();
552
+ }
553
+ }
554
+ if (validNames.length === 0) {
555
+ spinner.fail("No valid components found.");
556
+ console.log(chalk3.gray("\n Run ") + chalk3.cyan("npx apex-design-cli list") + chalk3.gray(" to see available components.\n"));
557
+ return;
558
+ }
559
+ spinner.text = "Resolving dependencies...";
560
+ const allComponents = await resolveDependencyTree(validNames);
561
+ const requested = allComponents.filter((c) => validNames.includes(c.name));
562
+ const autoInstalled = allComponents.filter((c) => !validNames.includes(c.name));
563
+ spinner.succeed("Dependencies resolved");
564
+ if (autoInstalled.length > 0) {
565
+ for (const dep of autoInstalled) {
566
+ logger.info(`Auto-including ${chalk3.cyan(dep.title)} (required dependency)`);
567
+ }
568
+ }
569
+ const conflicts = [];
570
+ for (const component of allComponents) {
571
+ for (const file of component.files) {
572
+ const targetDir = getTargetDir(component, config, options.path);
573
+ const targetPath = path5.resolve(cwd, targetDir, path5.basename(file.target));
574
+ if (await fs5.pathExists(targetPath)) {
575
+ conflicts.push(targetPath);
576
+ }
577
+ }
578
+ }
579
+ if (conflicts.length > 0 && !options.overwrite) {
580
+ logger.warn(`${conflicts.length} file(s) already exist:`);
581
+ for (const conflict of conflicts) {
582
+ console.log(chalk3.gray(` ${path5.relative(cwd, conflict)}`));
583
+ }
584
+ const { proceed } = await prompts2({
585
+ type: "confirm",
586
+ name: "proceed",
587
+ message: "Overwrite existing files?",
588
+ initial: false
589
+ });
590
+ if (!proceed) {
591
+ logger.info("Cancelled.");
592
+ return;
593
+ }
594
+ }
595
+ const npmDeps = /* @__PURE__ */ new Set();
596
+ for (const component of allComponents) {
597
+ for (const dep of component.dependencies) {
598
+ const { name } = parseDependency(dep);
599
+ npmDeps.add(name);
600
+ }
601
+ }
602
+ if (npmDeps.size > 0) {
603
+ const depsSpinner = ora2("Installing npm dependencies...").start();
604
+ try {
605
+ await installDependencies(
606
+ Array.from(npmDeps),
607
+ config.project.packageManager,
608
+ cwd
609
+ );
610
+ depsSpinner.succeed(`Installed ${npmDeps.size} dependencies`);
611
+ } catch {
612
+ depsSpinner.warn("Could not install dependencies automatically.");
613
+ logger.info(` Install manually: ${Array.from(npmDeps).join(" ")}`);
614
+ }
615
+ }
616
+ const copySpinner = ora2("Copying components...").start();
617
+ const copiedFiles = [];
618
+ for (const component of allComponents) {
619
+ for (const file of component.files) {
620
+ try {
621
+ const sourceContent = await resolveComponentSource(file);
622
+ const transformed = transformImports(sourceContent, config);
623
+ const targetDir = getTargetDir(component, config, options.path);
624
+ const targetPath = path5.resolve(cwd, targetDir, path5.basename(file.target));
625
+ await fs5.ensureDir(path5.dirname(targetPath));
626
+ await fs5.writeFile(targetPath, transformed);
627
+ copiedFiles.push(path5.relative(cwd, targetPath));
628
+ } catch (err) {
629
+ copySpinner.stop();
630
+ logger.warn(`Could not copy ${file.target}: ${err instanceof Error ? err.message : "Unknown error"}`);
631
+ copySpinner.start();
632
+ }
633
+ }
634
+ }
635
+ copySpinner.succeed(`Copied ${copiedFiles.length} files`);
636
+ console.log("");
637
+ console.log(chalk3.bold.green(" \u2705 Components installed successfully!"));
638
+ console.log("");
639
+ console.log(chalk3.gray(" Added:"));
640
+ for (const component of requested) {
641
+ console.log(chalk3.cyan(` ${component.title}`));
642
+ }
643
+ if (autoInstalled.length > 0) {
644
+ for (const dep of autoInstalled) {
645
+ console.log(chalk3.gray(` ${dep.title} (auto-installed)`));
646
+ }
647
+ }
648
+ console.log("");
649
+ console.log(chalk3.gray(" Import in your app:"));
650
+ for (const component of requested) {
651
+ if (component.type === "registry:ui") {
652
+ const mainExports = component.exports.slice(0, 3).join(", ");
653
+ const suffix = component.exports.length > 3 ? ", ..." : "";
654
+ const importPath = config.aliases.enabled ? `${config.aliases.components}/${component.name}` : `${config.paths.components}/${component.name}`;
655
+ logger.importExample(`import { ${mainExports}${suffix} } from '${importPath}';`);
656
+ }
657
+ }
658
+ console.log("");
659
+ }
660
+ function getTargetDir(component, config, customPath) {
661
+ if (customPath) return customPath;
662
+ switch (component.type) {
663
+ case "registry:lib":
664
+ return config.paths.lib;
665
+ case "registry:hook":
666
+ return config.paths.hooks;
667
+ case "registry:ui":
668
+ default:
669
+ return config.paths.components;
670
+ }
671
+ }
672
+
673
+ // src/commands/list.ts
674
+ import chalk4 from "chalk";
675
+ var CATEGORY_LABELS = {
676
+ inputs: "Inputs",
677
+ display: "Display",
678
+ feedback: "Feedback",
679
+ overlay: "Overlay",
680
+ navigation: "Navigation",
681
+ "data-display": "Data Display",
682
+ utility: "Utility",
683
+ lib: "Utilities",
684
+ hooks: "Hooks"
685
+ };
686
+ var CATEGORY_ORDER = [
687
+ "inputs",
688
+ "display",
689
+ "feedback",
690
+ "overlay",
691
+ "navigation",
692
+ "data-display",
693
+ "utility",
694
+ "lib",
695
+ "hooks"
696
+ ];
697
+ async function list(options) {
698
+ const registry = await loadRegistry();
699
+ const components = [];
700
+ for (const name of registry.components) {
701
+ const component = await loadComponentRegistry(name);
702
+ if (component) {
703
+ components.push(component);
704
+ }
705
+ }
706
+ if (options.json) {
707
+ console.log(JSON.stringify(components, null, 2));
708
+ return;
709
+ }
710
+ const byCategory = groupByCategory(components);
711
+ console.log("");
712
+ console.log(chalk4.bold.cyan(" APEX Design System") + chalk4.gray(` v${registry.version}`));
713
+ console.log(chalk4.gray(` ${components.length} components available`));
714
+ console.log("");
715
+ for (const category of CATEGORY_ORDER) {
716
+ const items = byCategory[category];
717
+ if (!items || items.length === 0) continue;
718
+ const label = CATEGORY_LABELS[category] || category;
719
+ console.log(chalk4.bold.yellow(` ${label.toUpperCase()}`));
720
+ console.log(chalk4.gray(" " + "\u2500".repeat(48)));
721
+ for (const item of items) {
722
+ const badges = [];
723
+ if (item.dependencies.some((d) => d.startsWith("@radix-ui"))) {
724
+ badges.push(chalk4.blue("Radix"));
725
+ }
726
+ if (item.dependencies.some((d) => d.startsWith("class-variance-authority"))) {
727
+ badges.push(chalk4.magenta("CVA"));
728
+ }
729
+ const badgeStr = badges.length > 0 ? " " + badges.join(" ") : "";
730
+ console.log(` ${chalk4.cyan(item.name.padEnd(20))}${badgeStr}`);
731
+ console.log(` ${chalk4.gray(item.description)}`);
732
+ }
733
+ console.log("");
734
+ }
735
+ console.log(chalk4.gray(" Usage:"));
736
+ console.log(chalk4.cyan(" npx apex-design-cli add <component-name>"));
737
+ console.log(chalk4.cyan(" npx apex-design-cli add button input card"));
738
+ console.log("");
739
+ }
740
+
741
+ // src/index.ts
742
+ var program = new Command();
743
+ program.name("apex-design-cli").description("CLI tool for APEX Design System \u2014 Install professional React components with a single command").version("1.0.0");
744
+ program.command("init").description("Initialize your project for APEX Design System").option("-y, --yes", "Skip prompts and use defaults").action(init);
745
+ program.command("add").description("Add components to your project").argument("<components...>", "Components to install").option("-o, --overwrite", "Overwrite existing files").option("-p, --path <path>", "Custom installation path").action(add);
746
+ program.command("list").description("List all available components").option("-j, --json", "Output as JSON").action(list);
747
+ program.parse();