@dinachi/cli 0.5.0 → 0.6.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 (54) hide show
  1. package/README.md +13 -5
  2. package/dist/index.js +1086 -349
  3. package/package.json +17 -9
  4. package/templates/accordion/accordion.tsx +8 -3
  5. package/templates/alert-dialog/alert-dialog.tsx +24 -25
  6. package/templates/alert-dialog/index.ts +1 -1
  7. package/templates/autocomplete/autocomplete.tsx +0 -1
  8. package/templates/avatar/avatar.tsx +1 -3
  9. package/templates/badge/badge.tsx +167 -0
  10. package/templates/badge/index.ts +2 -0
  11. package/templates/button/button.tsx +6 -6
  12. package/templates/button/index.ts +2 -2
  13. package/templates/card/card.tsx +78 -0
  14. package/templates/card/index.ts +1 -0
  15. package/templates/checkbox/checkbox.tsx +2 -3
  16. package/templates/checkbox-group/checkbox-group.tsx +1 -3
  17. package/templates/collapsible/collapsible.tsx +1 -2
  18. package/templates/combobox/combobox.tsx +0 -1
  19. package/templates/context-menu/context-menu.tsx +0 -1
  20. package/templates/dialog/dialog.tsx +1 -1
  21. package/templates/drawer/drawer.tsx +0 -1
  22. package/templates/field/field.tsx +69 -85
  23. package/templates/fieldset/fieldset.tsx +0 -1
  24. package/templates/form/form.tsx +36 -28
  25. package/templates/input/index.ts +1 -2
  26. package/templates/input/input.tsx +0 -1
  27. package/templates/menu/menu.tsx +0 -1
  28. package/templates/menubar/menubar.tsx +21 -22
  29. package/templates/meter/meter.tsx +0 -1
  30. package/templates/navigation-menu/index.ts +1 -13
  31. package/templates/navigation-menu/navigation-menu.tsx +1 -3
  32. package/templates/number-field/number-field.tsx +0 -1
  33. package/templates/popover/popover.tsx +0 -1
  34. package/templates/preview-card/preview-card.tsx +0 -1
  35. package/templates/progress/progress.tsx +0 -1
  36. package/templates/radio/radio.tsx +0 -1
  37. package/templates/scroll-area/scroll-area.tsx +0 -1
  38. package/templates/select/select.tsx +1 -4
  39. package/templates/separator/separator.tsx +0 -1
  40. package/templates/slider/index.ts +10 -0
  41. package/templates/slider/slider.tsx +1 -3
  42. package/templates/switch/switch.tsx +0 -1
  43. package/templates/tabs/index.ts +8 -0
  44. package/templates/tabs/tabs.tsx +8 -3
  45. package/templates/textarea/index.ts +2 -0
  46. package/templates/textarea/textarea.tsx +23 -0
  47. package/templates/toast/toast.tsx +3 -2
  48. package/templates/toggle/toggle.tsx +0 -1
  49. package/templates/toggle-group/toggle-group.tsx +0 -1
  50. package/templates/toolbar/toolbar.tsx +0 -1
  51. package/templates/tooltip/tooltip.tsx +0 -1
  52. package/templates/tsconfig.json +20 -0
  53. package/templates/utils/utils.ts +0 -1
  54. package/templates/utils/variants.ts +0 -1
package/dist/index.js CHANGED
@@ -6,8 +6,8 @@ import { Command as Command3 } from "commander";
6
6
  // src/commands/add.ts
7
7
  import { Command } from "commander";
8
8
  import { execSync } from "child_process";
9
- import fs2 from "fs-extra";
10
- import path2 from "path";
9
+ import fs3 from "fs-extra";
10
+ import path3 from "path";
11
11
  import { fileURLToPath as fileURLToPath2 } from "url";
12
12
  import ora from "ora";
13
13
  import chalk from "chalk";
@@ -18,6 +18,48 @@ import path from "path";
18
18
  import { fileURLToPath } from "url";
19
19
  var __filename = fileURLToPath(import.meta.url);
20
20
  var __dirname = path.dirname(__filename);
21
+ function dirnameLike(input) {
22
+ const normalized = input.replace(/\/+$/, "");
23
+ const idx = normalized.lastIndexOf("/");
24
+ if (idx <= 0) {
25
+ return normalized;
26
+ }
27
+ return normalized.slice(0, idx);
28
+ }
29
+ function normalizeConfig(raw) {
30
+ const style = typeof raw.style === "string" ? raw.style : "default";
31
+ const rsc = typeof raw.rsc === "boolean" ? raw.rsc : false;
32
+ const tsx = typeof raw.tsx === "boolean" ? raw.tsx : true;
33
+ const tailwindConfig = typeof raw.tailwind?.config === "string" ? raw.tailwind.config : "tailwind.config.js";
34
+ const tailwindCss = typeof raw.tailwind?.css === "string" ? raw.tailwind.css : "src/index.css";
35
+ const tailwindBaseColor = typeof raw.tailwind?.baseColor === "string" ? raw.tailwind.baseColor : "slate";
36
+ const tailwindCssVariables = typeof raw.tailwind?.cssVariables === "boolean" ? raw.tailwind.cssVariables : true;
37
+ const rawComponentsAlias = typeof raw.aliases?.components === "string" ? raw.aliases.components : "./src/components";
38
+ const hasLegacyUiPathInComponents = rawComponentsAlias.replace(/\/+$/, "").endsWith("/ui");
39
+ const componentsAlias = hasLegacyUiPathInComponents ? dirnameLike(rawComponentsAlias) : rawComponentsAlias;
40
+ const uiAlias = typeof raw.aliases?.ui === "string" ? raw.aliases.ui : hasLegacyUiPathInComponents ? rawComponentsAlias : `${componentsAlias}/ui`;
41
+ const utilsAlias = typeof raw.aliases?.utils === "string" ? raw.aliases.utils : "./src/lib/utils";
42
+ const libAlias = typeof raw.aliases?.lib === "string" ? raw.aliases.lib : dirnameLike(utilsAlias);
43
+ const hooksAlias = typeof raw.aliases?.hooks === "string" ? raw.aliases.hooks : "./src/hooks";
44
+ return {
45
+ style,
46
+ rsc,
47
+ tsx,
48
+ tailwind: {
49
+ config: tailwindConfig,
50
+ css: tailwindCss,
51
+ baseColor: tailwindBaseColor,
52
+ cssVariables: tailwindCssVariables
53
+ },
54
+ aliases: {
55
+ components: componentsAlias,
56
+ utils: utilsAlias,
57
+ ui: uiAlias,
58
+ lib: libAlias,
59
+ hooks: hooksAlias
60
+ }
61
+ };
62
+ }
21
63
  function getUtilityRegistry() {
22
64
  return {
23
65
  cn: {
@@ -60,14 +102,27 @@ function getComponentRegistry() {
60
102
  dependencies: ["@base-ui/react", "class-variance-authority"],
61
103
  utilityDependencies: ["cn"]
62
104
  },
105
+ badge: {
106
+ name: "badge",
107
+ description: "A small status indicator for highlighting information.",
108
+ files: [{ name: "badge.tsx" }, { name: "index.ts" }],
109
+ dependencies: ["@base-ui/react", "class-variance-authority"],
110
+ utilityDependencies: ["cn"]
111
+ },
63
112
  button: {
64
113
  name: "button",
65
114
  description: "A customizable button component with multiple variants.",
66
115
  files: [{ name: "button.tsx" }, { name: "index.ts" }],
67
116
  dependencies: ["@base-ui/react", "class-variance-authority"],
68
- componentDependencies: ["core"],
69
117
  utilityDependencies: ["cn", "variants"]
70
118
  },
119
+ card: {
120
+ name: "card",
121
+ description: "A container for grouping related content with header, body, and footer sections.",
122
+ files: [{ name: "card.tsx" }, { name: "index.ts" }],
123
+ dependencies: [],
124
+ utilityDependencies: ["cn"]
125
+ },
71
126
  checkbox: {
72
127
  name: "checkbox",
73
128
  description: "A control that allows the user to select one or more options from a set.",
@@ -251,6 +306,13 @@ function getComponentRegistry() {
251
306
  dependencies: ["@base-ui/react"],
252
307
  utilityDependencies: ["cn"]
253
308
  },
309
+ textarea: {
310
+ name: "textarea",
311
+ description: "A multi-line text input for longer form content.",
312
+ files: [{ name: "textarea.tsx" }, { name: "index.ts" }],
313
+ dependencies: [],
314
+ utilityDependencies: ["cn"]
315
+ },
254
316
  toast: {
255
317
  name: "toast",
256
318
  description: "Generates toast notifications with support for different types, promises, actions, and global management.",
@@ -295,46 +357,327 @@ async function getConfig() {
295
357
  }
296
358
  try {
297
359
  const configContent = await fs.readFile(configPath, "utf-8");
298
- return JSON.parse(configContent);
299
- } catch (error) {
360
+ const parsed = JSON.parse(configContent);
361
+ return normalizeConfig(parsed);
362
+ } catch {
300
363
  return null;
301
364
  }
302
365
  }
303
366
 
367
+ // src/utils/package-manager.ts
368
+ import fs2 from "fs-extra";
369
+ import path2 from "path";
370
+ var LOCK_FILE_MAP = [
371
+ { file: "bun.lockb", manager: "bun" },
372
+ { file: "bun.lock", manager: "bun" },
373
+ { file: "pnpm-lock.yaml", manager: "pnpm" },
374
+ { file: "yarn.lock", manager: "yarn" },
375
+ { file: "package-lock.json", manager: "npm" }
376
+ ];
377
+ function walkUpDirectories(startDir) {
378
+ const dirs = [];
379
+ let current = path2.resolve(startDir);
380
+ while (true) {
381
+ dirs.push(current);
382
+ const parent = path2.dirname(current);
383
+ if (parent === current) {
384
+ break;
385
+ }
386
+ current = parent;
387
+ }
388
+ return dirs;
389
+ }
390
+ function detectManagerFromPackageJson(startDir) {
391
+ for (const dir of walkUpDirectories(startDir)) {
392
+ const packageJsonPath = path2.join(dir, "package.json");
393
+ if (!fs2.existsSync(packageJsonPath)) {
394
+ continue;
395
+ }
396
+ try {
397
+ const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8"));
398
+ const value = packageJson.packageManager ?? "";
399
+ if (value.startsWith("pnpm@")) return "pnpm";
400
+ if (value.startsWith("yarn@")) return "yarn";
401
+ if (value.startsWith("bun@")) return "bun";
402
+ if (value.startsWith("npm@")) return "npm";
403
+ } catch {
404
+ continue;
405
+ }
406
+ }
407
+ return null;
408
+ }
409
+ function detectPackageManager(startDir = process.cwd()) {
410
+ const packageJsonManager = detectManagerFromPackageJson(startDir);
411
+ if (packageJsonManager) {
412
+ return packageJsonManager;
413
+ }
414
+ for (const dir of walkUpDirectories(startDir)) {
415
+ for (const entry of LOCK_FILE_MAP) {
416
+ if (fs2.existsSync(path2.join(dir, entry.file))) {
417
+ return entry.manager;
418
+ }
419
+ }
420
+ }
421
+ return "npm";
422
+ }
423
+ function getInstallCommand(pm, deps) {
424
+ const depsStr = deps.join(" ");
425
+ switch (pm) {
426
+ case "bun":
427
+ return `bun add ${depsStr}`;
428
+ case "pnpm":
429
+ return `pnpm add ${depsStr}`;
430
+ case "yarn":
431
+ return `yarn add ${depsStr}`;
432
+ default:
433
+ return `npm install ${depsStr}`;
434
+ }
435
+ }
436
+
437
+ // src/utils/json.ts
438
+ function parseJsonWithComments(content) {
439
+ let result = "";
440
+ let inString = false;
441
+ let inSingleLineComment = false;
442
+ let inMultiLineComment = false;
443
+ let isEscaped = false;
444
+ for (let i = 0; i < content.length; i += 1) {
445
+ const char = content[i];
446
+ const next = content[i + 1];
447
+ if (inSingleLineComment) {
448
+ if (char === "\n") {
449
+ inSingleLineComment = false;
450
+ result += char;
451
+ }
452
+ continue;
453
+ }
454
+ if (inMultiLineComment) {
455
+ if (char === "*" && next === "/") {
456
+ inMultiLineComment = false;
457
+ i += 1;
458
+ } else if (char === "\n") {
459
+ result += char;
460
+ }
461
+ continue;
462
+ }
463
+ if (inString) {
464
+ result += char;
465
+ if (!isEscaped && char === '"') {
466
+ inString = false;
467
+ }
468
+ isEscaped = !isEscaped && char === "\\";
469
+ continue;
470
+ }
471
+ if (char === '"') {
472
+ inString = true;
473
+ isEscaped = false;
474
+ result += char;
475
+ continue;
476
+ }
477
+ if (char === "/" && next === "/") {
478
+ inSingleLineComment = true;
479
+ i += 1;
480
+ continue;
481
+ }
482
+ if (char === "/" && next === "*") {
483
+ inMultiLineComment = true;
484
+ i += 1;
485
+ continue;
486
+ }
487
+ result += char;
488
+ }
489
+ const withoutLineComments = result;
490
+ const withoutTrailingCommas = withoutLineComments.replace(/,\s*([}\]])/g, "$1");
491
+ return JSON.parse(withoutTrailingCommas);
492
+ }
493
+
494
+ // src/utils/dependencies.ts
495
+ var DEPENDENCY_VERSION_MAP = {
496
+ "@base-ui/react": "^1.2.0",
497
+ "lucide-react": "^0.552.0",
498
+ "class-variance-authority": "^0.7.1",
499
+ "tailwindcss-animate": "^1.0.7",
500
+ "clsx": "^2.1.1",
501
+ "tailwind-merge": "^3.3.1"
502
+ };
503
+ function toInstallSpec(dep) {
504
+ const version = DEPENDENCY_VERSION_MAP[dep];
505
+ return version ? `${dep}@${version}` : dep;
506
+ }
507
+
304
508
  // src/commands/add.ts
305
509
  var __filename2 = fileURLToPath2(import.meta.url);
306
- var __dirname2 = path2.dirname(__filename2);
307
- function resolveAliasPath(aliasPath, projectRoot) {
308
- const cleanPath = aliasPath.replace(/^@\//, "").replace(/^\.\//, "");
309
- return path2.join(projectRoot, cleanPath);
510
+ var __dirname2 = path3.dirname(__filename2);
511
+ function stripTemplateDirective(content) {
512
+ return content.replace(/^\/\/ @ts-nocheck\s*\n/m, "");
513
+ }
514
+ function stripExtension(filePath) {
515
+ return filePath.replace(/\.[^./\\]+$/, "");
516
+ }
517
+ function toImportPath(fromFilePath, toFilePath) {
518
+ const relativePath = path3.relative(path3.dirname(fromFilePath), stripExtension(toFilePath)).replace(/\\/g, "/");
519
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
520
+ }
521
+ function matchPathPattern(pattern, input) {
522
+ if (!pattern.includes("*")) {
523
+ return pattern === input ? "" : null;
524
+ }
525
+ const [prefix, suffix] = pattern.split("*");
526
+ if (!input.startsWith(prefix) || !input.endsWith(suffix)) {
527
+ return null;
528
+ }
529
+ return input.slice(prefix.length, input.length - suffix.length);
530
+ }
531
+ function readCompilerPathConfig(projectRoot) {
532
+ const configFiles = ["tsconfig.json", "jsconfig.json"];
533
+ for (const configFile of configFiles) {
534
+ const configPath = path3.join(projectRoot, configFile);
535
+ if (!fs3.existsSync(configPath)) {
536
+ continue;
537
+ }
538
+ try {
539
+ const content = fs3.readFileSync(configPath, "utf-8");
540
+ const parsed = parseJsonWithComments(content);
541
+ const compilerOptions = parsed.compilerOptions ?? {};
542
+ const baseUrl = path3.resolve(
543
+ projectRoot,
544
+ typeof compilerOptions.baseUrl === "string" ? compilerOptions.baseUrl : "."
545
+ );
546
+ const rawPaths = compilerOptions.paths ?? {};
547
+ const paths = {};
548
+ for (const [key, value] of Object.entries(rawPaths)) {
549
+ if (!Array.isArray(value)) {
550
+ continue;
551
+ }
552
+ const targets = value.filter((entry) => typeof entry === "string");
553
+ if (targets.length > 0) {
554
+ paths[key] = targets;
555
+ }
556
+ }
557
+ return { baseUrl, paths };
558
+ } catch {
559
+ continue;
560
+ }
561
+ }
562
+ return null;
563
+ }
564
+ function resolveConfiguredPath(aliasOrPath, projectRoot, compilerConfig) {
565
+ const normalized = aliasOrPath.replace(/\\/g, "/").trim();
566
+ if (path3.isAbsolute(normalized)) {
567
+ return normalized;
568
+ }
569
+ if (normalized.startsWith("./") || normalized.startsWith("../")) {
570
+ return path3.resolve(projectRoot, normalized);
571
+ }
572
+ if (normalized.startsWith("/")) {
573
+ return path3.resolve(projectRoot, `.${normalized}`);
574
+ }
575
+ if (compilerConfig) {
576
+ for (const [pattern, targets] of Object.entries(compilerConfig.paths)) {
577
+ const wildcard = matchPathPattern(pattern, normalized);
578
+ if (wildcard === null) {
579
+ continue;
580
+ }
581
+ const target = targets[0];
582
+ if (!target) {
583
+ continue;
584
+ }
585
+ const mappedTarget = target.includes("*") ? target.replace("*", wildcard) : target;
586
+ return path3.resolve(compilerConfig.baseUrl, mappedTarget);
587
+ }
588
+ }
589
+ if (normalized.startsWith("@/") || normalized.startsWith("~/")) {
590
+ return path3.resolve(projectRoot, normalized.slice(2));
591
+ }
592
+ return path3.resolve(projectRoot, normalized);
310
593
  }
311
- function getComponentDependencies(componentName) {
594
+ function rewriteTemplateImports(content, targetFilePath, utilsFilePath, libDirPath) {
595
+ const utilsImportPath = toImportPath(targetFilePath, utilsFilePath);
596
+ const variantsImportPath = toImportPath(targetFilePath, path3.join(libDirPath, "variants.ts"));
597
+ return content.replace(/(['"])@\/lib\/utils\1/g, `$1${utilsImportPath}$1`).replace(/(['"])@\/lib\/variants\1/g, `$1${variantsImportPath}$1`);
598
+ }
599
+ function getComponentDependencies(componentName, visited = /* @__PURE__ */ new Set()) {
600
+ if (visited.has(componentName)) {
601
+ return [];
602
+ }
603
+ visited.add(componentName);
312
604
  const registry = getComponentRegistry();
313
605
  const component = registry[componentName];
314
606
  if (!component) return [];
315
- let dependencies = component.componentDependencies || [];
607
+ const dependencies = [];
316
608
  for (const dep of component.componentDependencies || []) {
317
- dependencies = [...dependencies, ...getComponentDependencies(dep)];
609
+ if (!registry[dep]) {
610
+ continue;
611
+ }
612
+ dependencies.push(dep, ...getComponentDependencies(dep, visited));
318
613
  }
319
614
  return [...new Set(dependencies)];
320
615
  }
321
- async function ensureTailwindConfig(deps, configFileName) {
322
- const needsAnimatePlugin = deps.includes("tailwindcss-animate");
323
- if (!needsAnimatePlugin) return;
324
- let configPath = path2.join(process.cwd(), configFileName);
325
- if (!fs2.existsSync(configPath)) {
326
- const alternatives = ["tailwind.config.js", "tailwind.config.ts", "tailwind.config.mjs"];
327
- for (const alt of alternatives) {
328
- const altPath = path2.join(process.cwd(), alt);
329
- if (fs2.existsSync(altPath)) {
330
- configPath = altPath;
331
- break;
332
- }
616
+ function detectTailwindConfigPath(projectRoot, configuredName) {
617
+ const preferred = resolveConfiguredPath(configuredName, projectRoot, null);
618
+ if (fs3.existsSync(preferred)) {
619
+ return preferred;
620
+ }
621
+ const alternatives = [
622
+ "tailwind.config.ts",
623
+ "tailwind.config.js",
624
+ "tailwind.config.mjs",
625
+ "tailwind.config.cjs",
626
+ "tailwind.config.cts",
627
+ "tailwind.config.mts"
628
+ ];
629
+ for (const candidate of alternatives) {
630
+ const candidatePath = path3.join(projectRoot, candidate);
631
+ if (fs3.existsSync(candidatePath)) {
632
+ return candidatePath;
333
633
  }
334
634
  }
335
- if (!fs2.existsSync(configPath)) {
336
- const configContent = `/** @type {import('tailwindcss').Config} */
337
- export default {
635
+ return preferred;
636
+ }
637
+ function insertTailwindPlugin(content, pluginExpression) {
638
+ if (/plugins\s*:\s*\[/.test(content)) {
639
+ return content.replace(/plugins\s*:\s*\[([\s\S]*?)\]/m, (_match, pluginsContent) => {
640
+ const trimmedPlugins = pluginsContent.trim();
641
+ if (trimmedPlugins.length === 0) {
642
+ return `plugins: [
643
+ ${pluginExpression},
644
+ ]`;
645
+ }
646
+ const normalizedPlugins = pluginsContent.trimEnd();
647
+ const withTrailingComma = normalizedPlugins.endsWith(",") ? normalizedPlugins : `${normalizedPlugins},`;
648
+ return `plugins: [
649
+ ${withTrailingComma}
650
+ ${pluginExpression},
651
+ ]`;
652
+ });
653
+ }
654
+ const trimmed = content.trimEnd();
655
+ const hasSemicolon = trimmed.endsWith(";");
656
+ const withoutSemicolon = hasSemicolon ? trimmed.slice(0, -1) : trimmed;
657
+ const closingBraceIndex = withoutSemicolon.lastIndexOf("}");
658
+ if (closingBraceIndex === -1) {
659
+ return null;
660
+ }
661
+ const beforeClosingBrace = withoutSemicolon.slice(0, closingBraceIndex).trimEnd();
662
+ const needsComma = beforeClosingBrace.length > 0 && !beforeClosingBrace.endsWith(",") && !beforeClosingBrace.endsWith("{");
663
+ const next = `${withoutSemicolon.slice(0, closingBraceIndex)}${needsComma ? "," : ""}
664
+ plugins: [
665
+ ${pluginExpression},
666
+ ],
667
+ }${hasSemicolon ? ";" : ""}
668
+ `;
669
+ return next;
670
+ }
671
+ async function ensureTailwindConfig(deps, projectRoot, configuredFileName) {
672
+ if (!deps.includes("tailwindcss-animate")) {
673
+ return null;
674
+ }
675
+ const configPath = detectTailwindConfigPath(projectRoot, configuredFileName || "tailwind.config.js");
676
+ if (!fs3.existsSync(configPath)) {
677
+ const ext2 = path3.extname(configPath);
678
+ const isCjs2 = ext2 === ".cjs";
679
+ const configContent = isCjs2 ? `/** @type {import('tailwindcss').Config} */
680
+ module.exports = {
338
681
  content: [
339
682
  "./src/**/*.{js,ts,jsx,tsx}",
340
683
  "./app/**/*.{js,ts,jsx,tsx}",
@@ -344,265 +687,615 @@ export default {
344
687
  theme: {
345
688
  extend: {},
346
689
  },
347
- plugins: [
348
- require("tailwindcss-animate"),
690
+ plugins: [require("tailwindcss-animate")],
691
+ }
692
+ ` : `import tailwindcssAnimate from "tailwindcss-animate"
693
+
694
+ export default {
695
+ content: [
696
+ "./src/**/*.{js,ts,jsx,tsx}",
697
+ "./app/**/*.{js,ts,jsx,tsx}",
698
+ "./components/**/*.{js,ts,jsx,tsx}",
699
+ "./pages/**/*.{js,ts,jsx,tsx}",
349
700
  ],
350
- };`;
351
- await fs2.writeFile(configPath, configContent);
701
+ theme: {
702
+ extend: {},
703
+ },
704
+ plugins: [tailwindcssAnimate],
705
+ }
706
+ `;
707
+ await fs3.ensureDir(path3.dirname(configPath));
708
+ await fs3.writeFile(configPath, configContent);
352
709
  return { created: true, path: configPath };
353
- } else {
354
- const configContent = await fs2.readFile(configPath, "utf-8");
355
- if (!configContent.includes("tailwindcss-animate")) {
356
- let updatedContent = configContent;
357
- if (configContent.includes("plugins:")) {
358
- updatedContent = configContent.replace(
359
- /plugins:\s*\[([\s\S]*?)\]/,
360
- (match, pluginsContent) => {
361
- const cleanPlugins = pluginsContent.trim();
362
- const newPlugin = 'require("tailwindcss-animate")';
363
- if (cleanPlugins === "") {
364
- return `plugins: [
365
- ${newPlugin},
366
- ]`;
367
- } else {
368
- return `plugins: [
369
- ${pluginsContent},
370
- ${newPlugin},
371
- ]`;
372
- }
373
- }
374
- );
375
- } else {
376
- updatedContent = configContent.replace(
377
- /}\s*;?\s*$/,
378
- ' plugins: [\n require("tailwindcss-animate"),\n ],\n};'
379
- );
380
- }
381
- await fs2.writeFile(configPath, updatedContent);
382
- return { updated: true, path: configPath };
383
- }
384
710
  }
385
- return { exists: true, path: configPath };
711
+ const currentContent = await fs3.readFile(configPath, "utf-8");
712
+ if (currentContent.includes("tailwindcss-animate")) {
713
+ return { exists: true, path: configPath };
714
+ }
715
+ const ext = path3.extname(configPath);
716
+ const isCjs = ext === ".cjs" || /module\.exports\s*=/.test(currentContent);
717
+ const pluginExpression = isCjs ? 'require("tailwindcss-animate")' : "tailwindcssAnimate";
718
+ let updatedContent = currentContent;
719
+ if (!isCjs && !/from\s+['"]tailwindcss-animate['"]/.test(updatedContent)) {
720
+ updatedContent = `import tailwindcssAnimate from "tailwindcss-animate"
721
+ ${updatedContent}`;
722
+ }
723
+ const merged = insertTailwindPlugin(updatedContent, pluginExpression);
724
+ if (!merged) {
725
+ return { skipped: true, path: configPath };
726
+ }
727
+ await fs3.writeFile(configPath, merged);
728
+ return { updated: true, path: configPath };
386
729
  }
387
- async function handleIndexFile(sourcePath, targetPath, componentName, allFilesAdded, targetDir) {
388
- let templateContent = await fs2.readFile(sourcePath, "utf-8");
389
- templateContent = templateContent.replace(/^\/\/ @ts-nocheck\s*\n/m, "");
390
- if (!fs2.existsSync(targetPath)) {
391
- await fs2.writeFile(targetPath, templateContent);
392
- allFilesAdded.push({ name: "index.ts", path: path2.join(targetDir, "index.ts") });
393
- } else {
394
- const existingContent = await fs2.readFile(targetPath, "utf-8");
395
- const exportRegex = /export\s+\*\s+from\s+['"]\.\/([^'"]+)['"]/g;
396
- const templateExports = [];
397
- let match;
398
- while ((match = exportRegex.exec(templateContent)) !== null) {
399
- templateExports.push(match[1]);
730
+ function escapeRegex(value) {
731
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
732
+ }
733
+ async function handleIndexFile(sourcePath, targetPath, allFilesAdded, targetDir) {
734
+ const templateContent = stripTemplateDirective(await fs3.readFile(sourcePath, "utf-8"));
735
+ if (!fs3.existsSync(targetPath)) {
736
+ await fs3.writeFile(targetPath, templateContent);
737
+ allFilesAdded.push({ name: "index.ts", path: path3.join(targetDir, "index.ts") });
738
+ return;
739
+ }
740
+ const existingContent = await fs3.readFile(targetPath, "utf-8");
741
+ const exportLines = templateContent.split("\n").map((line) => line.trim()).filter((line) => /^export\s+/.test(line) && /from\s+['"]\.\/[^'"]+['"]/.test(line));
742
+ const linesToAppend = [];
743
+ for (const line of exportLines) {
744
+ const match = line.match(/from\s+['"]\.\/([^'"]+)['"]/);
745
+ if (!match) {
746
+ continue;
400
747
  }
401
- const componentExportExists = existingContent.includes(`export * from './${componentName}'`);
402
- if (!componentExportExists && templateExports.includes(componentName)) {
403
- const newExportLine = `export * from './${componentName}'`;
404
- const updatedContent = existingContent.trim() + "\n" + newExportLine + "\n";
405
- await fs2.writeFile(targetPath, updatedContent);
406
- allFilesAdded.push({ name: "index.ts", path: path2.join(targetDir, "index.ts") });
748
+ const modulePath = match[1];
749
+ const modulePathPattern = new RegExp(`from\\s+['"]\\./${escapeRegex(modulePath)}['"]`);
750
+ if (modulePathPattern.test(existingContent)) {
751
+ continue;
407
752
  }
753
+ linesToAppend.push(line.endsWith(";") ? line : `${line};`);
408
754
  }
755
+ if (linesToAppend.length === 0) {
756
+ return;
757
+ }
758
+ const updatedContent = `${existingContent.trimEnd()}
759
+ ${linesToAppend.join("\n")}
760
+ `;
761
+ await fs3.writeFile(targetPath, updatedContent);
762
+ allFilesAdded.push({ name: "index.ts", path: path3.join(targetDir, "index.ts") });
409
763
  }
410
- var addCommand = new Command("add").description("Add a component to your project").argument("[component]", "Name of the component (optional when using --all)").option("-y, --yes", "Skip confirmation prompts").option("-o, --overwrite", "Overwrite existing files").option("-a, --all", "Install all available components").action(async (componentName, options) => {
411
- const spinner = ora("Adding component...").start();
412
- try {
413
- const config = await getConfig();
414
- if (!config) {
415
- spinner.fail("\u274C No components.json found. Run `dinachi init` first.");
416
- process.exit(1);
417
- }
418
- const registry = getComponentRegistry();
419
- let componentsToInstall = [];
420
- if (options.all) {
421
- const allComponents = Object.keys(registry);
422
- spinner.text = `Installing all ${allComponents.length} components...`;
423
- const allComponentsWithDeps = /* @__PURE__ */ new Set();
424
- for (const name of allComponents) {
425
- allComponentsWithDeps.add(name);
426
- const deps = getComponentDependencies(name);
427
- deps.forEach((dep) => allComponentsWithDeps.add(dep));
428
- }
429
- componentsToInstall = Array.from(allComponentsWithDeps);
430
- } else {
431
- if (!componentName) {
432
- spinner.fail("\u274C Component name is required when not using --all flag.");
433
- console.log("Available components:");
434
- Object.keys(registry).forEach((name) => {
435
- console.log(` ${chalk.cyan(name)}`);
436
- });
764
+ var addCommand = new Command("add").description("Add a component to your project").argument("[component]", "Name of the component (optional when using --all)").option("-y, --yes", "Skip confirmation prompts").option("-o, --overwrite", "Overwrite existing files").option("-a, --all", "Install all available components").option("--skip-install", "Skip package installation").action(
765
+ async (componentName, options) => {
766
+ const spinner = ora("Adding component...").start();
767
+ try {
768
+ const config = await getConfig();
769
+ if (!config) {
770
+ spinner.fail("\u274C No components.json found. Run `dinachi init` first.");
437
771
  process.exit(1);
438
772
  }
439
- const component = registry[componentName];
440
- if (!component) {
441
- spinner.fail(`\u274C Component "${componentName}" not found.`);
442
- console.log("Available components:");
443
- Object.keys(registry).forEach((name) => {
444
- console.log(` ${chalk.cyan(name)}`);
445
- });
446
- process.exit(1);
773
+ const projectRoot = process.cwd();
774
+ const compilerPathConfig = readCompilerPathConfig(projectRoot);
775
+ const registry = getComponentRegistry();
776
+ let componentsToInstall = [];
777
+ if (options.all) {
778
+ const allComponents = Object.keys(registry);
779
+ spinner.text = `Installing all ${allComponents.length} components...`;
780
+ const allComponentsWithDeps = /* @__PURE__ */ new Set();
781
+ for (const name of allComponents) {
782
+ allComponentsWithDeps.add(name);
783
+ const deps = getComponentDependencies(name);
784
+ deps.forEach((dep) => allComponentsWithDeps.add(dep));
785
+ }
786
+ componentsToInstall = Array.from(allComponentsWithDeps);
787
+ } else {
788
+ if (!componentName) {
789
+ spinner.fail("\u274C Component name is required when not using --all flag.");
790
+ console.log("Available components:");
791
+ Object.keys(registry).forEach((name) => {
792
+ console.log(` ${chalk.cyan(name)}`);
793
+ });
794
+ process.exit(1);
795
+ }
796
+ const component = registry[componentName];
797
+ if (!component) {
798
+ spinner.fail(`\u274C Component "${componentName}" not found.`);
799
+ console.log("Available components:");
800
+ Object.keys(registry).forEach((name) => {
801
+ console.log(` ${chalk.cyan(name)}`);
802
+ });
803
+ process.exit(1);
804
+ }
805
+ componentsToInstall = [componentName, ...getComponentDependencies(componentName)];
447
806
  }
448
- componentsToInstall = [componentName, ...getComponentDependencies(componentName)];
449
- }
450
- if (!options.all) {
451
- spinner.text = `Installing ${componentsToInstall.join(", ")}...`;
452
- }
453
- const componentDir = resolveAliasPath(config.aliases.ui, process.cwd());
454
- await fs2.ensureDir(componentDir);
455
- let allFilesAdded = [];
456
- let allDepsInstalled = [];
457
- let allUtilityDeps = [];
458
- for (const name of componentsToInstall) {
459
- const comp = registry[name];
460
- if (!comp) continue;
461
- if (comp.utilityDependencies?.length) {
462
- allUtilityDeps.push(...comp.utilityDependencies);
807
+ if (!options.all) {
808
+ spinner.text = `Installing ${componentsToInstall.join(", ")}...`;
463
809
  }
464
- }
465
- const utilityRegistry = getUtilityRegistry();
466
- const uniqueUtilityDeps = [...new Set(allUtilityDeps)];
467
- const utilsDir = resolveAliasPath(config.aliases.lib, process.cwd());
468
- if (uniqueUtilityDeps.length > 0) {
469
- await fs2.ensureDir(utilsDir);
470
- for (const utilityName of uniqueUtilityDeps) {
471
- const utility = utilityRegistry[utilityName];
472
- if (!utility) continue;
473
- const utilityFilename = `${utility.name}.ts`;
474
- const sourcePath = path2.join(__dirname2, "../templates/utils", utilityFilename);
475
- const targetPath = path2.join(utilsDir, utilityFilename);
476
- if (!fs2.existsSync(targetPath)) {
477
- let content = await fs2.readFile(sourcePath, "utf-8");
478
- content = content.replace(/^\/\/ @ts-nocheck\s*\n/m, "");
479
- await fs2.writeFile(targetPath, content);
480
- allFilesAdded.push({
481
- name: utilityFilename,
482
- path: path2.join(utilsDir, utilityFilename)
483
- });
810
+ const componentDir = resolveConfiguredPath(config.aliases.ui, projectRoot, compilerPathConfig);
811
+ const libDir = resolveConfiguredPath(config.aliases.lib, projectRoot, compilerPathConfig);
812
+ const utilsFilePath = resolveConfiguredPath(config.aliases.utils, projectRoot, compilerPathConfig);
813
+ await fs3.ensureDir(componentDir);
814
+ await fs3.ensureDir(libDir);
815
+ const allFilesAdded = [];
816
+ const allDepsInstalled = [];
817
+ const allUtilityDeps = [];
818
+ for (const name of componentsToInstall) {
819
+ const comp = registry[name];
820
+ if (!comp) continue;
821
+ if (comp.utilityDependencies?.length) {
822
+ allUtilityDeps.push(...comp.utilityDependencies);
823
+ }
824
+ }
825
+ const utilityRegistry = getUtilityRegistry();
826
+ const uniqueUtilityDeps = [...new Set(allUtilityDeps)];
827
+ if (uniqueUtilityDeps.length > 0) {
828
+ for (const utilityName of uniqueUtilityDeps) {
829
+ const utility = utilityRegistry[utilityName];
830
+ if (!utility) continue;
831
+ const utilityFilename = `${utility.name}.ts`;
832
+ const sourcePath = path3.join(__dirname2, "../templates/utils", utilityFilename);
833
+ const targetPath = path3.join(libDir, utilityFilename);
834
+ if (!fs3.existsSync(targetPath)) {
835
+ const content = stripTemplateDirective(await fs3.readFile(sourcePath, "utf-8"));
836
+ await fs3.writeFile(targetPath, content);
837
+ allFilesAdded.push({
838
+ name: utilityFilename,
839
+ path: targetPath
840
+ });
841
+ }
484
842
  if (utility.dependencies?.length) {
485
843
  allDepsInstalled.push(...utility.dependencies);
486
844
  }
487
845
  }
488
846
  }
489
- }
490
- for (const name of componentsToInstall) {
491
- const comp = registry[name];
492
- if (!comp) continue;
493
- for (const file of comp.files) {
494
- const sourcePath = path2.join(__dirname2, "../templates", name, file.name);
495
- const targetPath = path2.join(componentDir, file.name);
496
- if (file.name === "index.ts") {
497
- await handleIndexFile(sourcePath, targetPath, name, allFilesAdded, componentDir);
498
- } else {
499
- if (fs2.existsSync(targetPath) && !options.overwrite) {
847
+ for (const name of componentsToInstall) {
848
+ const comp = registry[name];
849
+ if (!comp) continue;
850
+ for (const file of comp.files) {
851
+ const sourcePath = path3.join(__dirname2, "../templates", name, file.name);
852
+ const targetPath = path3.join(componentDir, file.name);
853
+ if (file.name === "index.ts") {
854
+ await handleIndexFile(sourcePath, targetPath, allFilesAdded, componentDir);
855
+ continue;
856
+ }
857
+ if (fs3.existsSync(targetPath) && !options.overwrite) {
500
858
  spinner.warn(`\u26A0\uFE0F ${file.name} already exists. Use --overwrite to replace it.`);
501
859
  continue;
502
860
  }
503
- let content = await fs2.readFile(sourcePath, "utf-8");
504
- content = content.replace(/^\/\/ @ts-nocheck\s*\n/m, "");
505
- await fs2.writeFile(targetPath, content);
506
- allFilesAdded.push({ name: file.name, path: path2.join(componentDir, file.name) });
861
+ const templateContent = stripTemplateDirective(await fs3.readFile(sourcePath, "utf-8"));
862
+ const rewrittenContent = rewriteTemplateImports(templateContent, targetPath, utilsFilePath, libDir);
863
+ await fs3.writeFile(targetPath, rewrittenContent);
864
+ allFilesAdded.push({ name: file.name, path: targetPath });
865
+ }
866
+ if (comp.dependencies?.length) {
867
+ allDepsInstalled.push(...comp.dependencies);
507
868
  }
508
869
  }
509
- if (comp.dependencies?.length) {
510
- allDepsInstalled.push(...comp.dependencies);
511
- }
512
- }
513
- let tailwindConfigInfo = null;
514
- if (allDepsInstalled.includes("tailwindcss-animate")) {
515
870
  spinner.text = "Updating Tailwind configuration...";
516
- const configFileName = config.tailwind?.config || "tailwind.config.js";
517
- tailwindConfigInfo = await ensureTailwindConfig(allDepsInstalled, configFileName);
518
- }
519
- const packageJsonPath = path2.join(process.cwd(), "package.json");
520
- const packageJson = JSON.parse(await fs2.readFile(packageJsonPath, "utf-8"));
521
- const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies };
522
- const missingDeps = [...new Set(allDepsInstalled)].filter((dep) => !allDeps[dep]);
523
- if (allDepsInstalled.length > 0) {
524
- spinner.text = "Installing dependencies...";
525
- if (missingDeps.length > 0) {
871
+ const tailwindConfigInfo = await ensureTailwindConfig(
872
+ allDepsInstalled,
873
+ projectRoot,
874
+ config.tailwind?.config || "tailwind.config.js"
875
+ );
876
+ const packageJsonPath = path3.join(projectRoot, "package.json");
877
+ if (!fs3.existsSync(packageJsonPath)) {
878
+ throw new Error("No package.json found in the current directory.");
879
+ }
880
+ const packageJson = JSON.parse(await fs3.readFile(packageJsonPath, "utf-8"));
881
+ const declaredDeps = { ...packageJson.dependencies ?? {}, ...packageJson.devDependencies ?? {} };
882
+ const uniqueDeps = [...new Set(allDepsInstalled)];
883
+ const missingDeps = uniqueDeps.filter((dep) => !declaredDeps[dep]);
884
+ if (!options.skipInstall && missingDeps.length > 0) {
885
+ spinner.text = "Installing dependencies...";
526
886
  try {
527
- const packageManager = getPackageManager();
528
- const installCmd = getInstallCommand(packageManager, missingDeps);
529
- execSync(installCmd, { stdio: "inherit" });
530
- } catch (error) {
887
+ const packageManager = detectPackageManager(projectRoot);
888
+ const installCmd = getInstallCommand(packageManager, missingDeps.map(toInstallSpec));
889
+ execSync(installCmd, { stdio: "inherit", cwd: projectRoot });
890
+ } catch {
531
891
  spinner.warn(`\u26A0\uFE0F Failed to install dependencies. Please install manually: ${missingDeps.join(" ")}`);
532
892
  }
533
- } else {
893
+ } else if (!options.skipInstall && uniqueDeps.length > 0) {
534
894
  spinner.text = "All dependencies already installed.";
535
895
  }
536
- }
537
- if (options.all) {
538
- spinner.succeed(`\u2705 Added all ${componentsToInstall.length} components!`);
539
- } else {
540
- spinner.succeed(`\u2705 Added ${componentsToInstall.join(", ")}!`);
541
- }
542
- console.log();
543
- console.log("Files added:");
544
- allFilesAdded.forEach((file) => {
545
- console.log(` ${chalk.green("+")} ${file.path}`);
546
- });
547
- if (tailwindConfigInfo) {
548
- console.log();
549
- if (tailwindConfigInfo.created) {
550
- console.log(` ${chalk.green("+")} ${tailwindConfigInfo.path} (created with tailwindcss-animate plugin)`);
551
- } else if (tailwindConfigInfo.updated) {
552
- console.log(` ${chalk.blue("~")} ${tailwindConfigInfo.path} (updated with tailwindcss-animate plugin)`);
896
+ if (options.all) {
897
+ spinner.succeed(`\u2705 Added all ${componentsToInstall.length} components!`);
898
+ } else {
899
+ spinner.succeed(`\u2705 Added ${componentsToInstall.join(", ")}!`);
553
900
  }
554
- }
555
- if (missingDeps.length > 0) {
556
- console.log();
557
- console.log("Dependencies installed:");
558
- missingDeps.forEach((dep) => {
559
- console.log(` ${chalk.green("\u2713")} ${dep}`);
560
- });
561
- } else if (allDepsInstalled.length > 0) {
562
901
  console.log();
563
- console.log("Dependencies (already installed):");
564
- [...new Set(allDepsInstalled)].forEach((dep) => {
565
- console.log(` ${chalk.blue("~")} ${dep}`);
902
+ console.log("Files added:");
903
+ allFilesAdded.forEach((file) => {
904
+ console.log(` ${chalk.green("+")} ${file.path}`);
566
905
  });
906
+ if (tailwindConfigInfo) {
907
+ console.log();
908
+ if (tailwindConfigInfo.created) {
909
+ console.log(` ${chalk.green("+")} ${tailwindConfigInfo.path} (created with tailwindcss-animate plugin)`);
910
+ } else if (tailwindConfigInfo.updated) {
911
+ console.log(` ${chalk.blue("~")} ${tailwindConfigInfo.path} (updated with tailwindcss-animate plugin)`);
912
+ } else if (tailwindConfigInfo.skipped) {
913
+ console.log(
914
+ ` ${chalk.yellow("!")} ${tailwindConfigInfo.path} (could not safely update automatically; add tailwindcss-animate manually)`
915
+ );
916
+ }
917
+ }
918
+ if (options.skipInstall && missingDeps.length > 0) {
919
+ console.log();
920
+ console.log("Dependencies to install manually:");
921
+ missingDeps.forEach((dep) => {
922
+ console.log(` ${chalk.yellow("\u2022")} ${toInstallSpec(dep)}`);
923
+ });
924
+ } else if (missingDeps.length > 0) {
925
+ console.log();
926
+ console.log("Dependencies installed:");
927
+ missingDeps.forEach((dep) => {
928
+ console.log(` ${chalk.green("\u2713")} ${toInstallSpec(dep)}`);
929
+ });
930
+ } else if (uniqueDeps.length > 0) {
931
+ console.log();
932
+ console.log("Dependencies (already installed):");
933
+ uniqueDeps.forEach((dep) => {
934
+ console.log(` ${chalk.blue("~")} ${dep}`);
935
+ });
936
+ }
937
+ } catch (error) {
938
+ spinner.fail(`\u274C Failed to add component: ${error instanceof Error ? error.message : error}`);
939
+ process.exit(1);
567
940
  }
568
- } catch (error) {
569
- spinner.fail(`\u274C Failed to add component: ${error instanceof Error ? error.message : error}`);
570
- process.exit(1);
571
941
  }
572
- });
573
- function getPackageManager() {
574
- if (fs2.existsSync("bun.lockb") || fs2.existsSync("bun.lock")) return "bun";
575
- if (fs2.existsSync("pnpm-lock.yaml")) return "pnpm";
576
- if (fs2.existsSync("yarn.lock")) return "yarn";
577
- return "npm";
578
- }
579
- function getInstallCommand(pm, deps) {
580
- const depsStr = deps.join(" ");
581
- switch (pm) {
582
- case "bun":
583
- return `bun add ${depsStr}`;
584
- case "pnpm":
585
- return `pnpm add ${depsStr}`;
586
- case "yarn":
587
- return `yarn add ${depsStr}`;
588
- default:
589
- return `npm install ${depsStr}`;
590
- }
591
- }
942
+ );
592
943
 
593
944
  // src/commands/init.ts
594
945
  import { Command as Command2 } from "commander";
595
946
  import { execSync as execSync2 } from "child_process";
596
- import fs3 from "fs-extra";
597
- import path3 from "path";
947
+ import fs4 from "fs-extra";
948
+ import path4 from "path";
598
949
  import prompts from "prompts";
599
950
  import chalk2 from "chalk";
600
951
  import ora2 from "ora";
601
- var initCommand = new Command2("init").description("Initialize Dinachi UI in your project").action(async () => {
952
+ function normalizeProjectPath(inputPath, projectRoot) {
953
+ const absolutePath = path4.isAbsolute(inputPath) ? path4.normalize(inputPath) : path4.resolve(projectRoot, inputPath);
954
+ const relativePath = path4.relative(projectRoot, absolutePath).replace(/\\/g, "/");
955
+ const withoutPrefix = relativePath.replace(/^\.\//, "").replace(/\/$/, "");
956
+ return withoutPrefix.length > 0 ? withoutPrefix : ".";
957
+ }
958
+ function toConfigPath(relativePath) {
959
+ return relativePath === "." ? "." : `./${relativePath.replace(/\\/g, "/")}`;
960
+ }
961
+ function createUtilsFileContent() {
962
+ return `import { type ClassValue, clsx } from "clsx"
963
+ import { twMerge } from "tailwind-merge"
964
+
965
+ export function cn(...inputs: ClassValue[]) {
966
+ return twMerge(clsx(inputs))
967
+ }
968
+ `;
969
+ }
970
+ function detectTailwindMajorVersion(projectRoot) {
971
+ const packageJsonPath = path4.join(projectRoot, "package.json");
972
+ if (!fs4.existsSync(packageJsonPath)) return 4;
973
+ try {
974
+ const raw = fs4.readFileSync(packageJsonPath, "utf-8");
975
+ const packageJson = JSON.parse(raw);
976
+ const deps = { ...packageJson.dependencies ?? {}, ...packageJson.devDependencies ?? {} };
977
+ const twVersion = deps.tailwindcss;
978
+ if (!twVersion) return 4;
979
+ const match = twVersion.match(/(\d+)/);
980
+ return match ? parseInt(match[1], 10) : 4;
981
+ } catch {
982
+ return 4;
983
+ }
984
+ }
985
+ function getThemeCSS(tailwindMajor, mode) {
986
+ const lightVars = `:root {
987
+ --background: oklch(0.986 0.0034 145.5499);
988
+ --foreground: oklch(0.1459 0.0497 142.4953);
989
+ --card: oklch(0.9781 0.0017 145.5621);
990
+ --card-foreground: oklch(0.1459 0.0497 142.4953);
991
+ --popover: oklch(1 0 0);
992
+ --popover-foreground: oklch(0.1324 0.0033 145.3864);
993
+ --primary: oklch(0.1324 0.0033 145.3864);
994
+ --primary-foreground: oklch(0.9729 0.0101 145.4971);
995
+ --secondary: oklch(0.9248 0.0051 145.5339);
996
+ --secondary-foreground: oklch(0.1324 0.0033 145.3864);
997
+ --muted: oklch(0.9631 0.0017 145.5619);
998
+ --muted-foreground: oklch(0.1849 0.0629 142.4953);
999
+ --accent: oklch(0.9248 0.0051 145.5339);
1000
+ --accent-foreground: oklch(0.1459 0.0497 142.4953);
1001
+ --destructive: oklch(0.5248 0.1368 20.8317);
1002
+ --destructive-foreground: oklch(1 0 0);
1003
+ --border: oklch(0.9239 0.0017 145.5613);
1004
+ --input: oklch(0.8481 0.0105 145.4823);
1005
+ --ring: oklch(0.1459 0.0497 142.4953);
1006
+ --radius: 0.625rem;
1007
+ }`;
1008
+ const darkVars = `.dark {
1009
+ --background: oklch(0.1149 0 0);
1010
+ --foreground: oklch(0.7999 0.0218 134.1191);
1011
+ --card: oklch(0.133 0.0021 196.9098);
1012
+ --card-foreground: oklch(0.7996 0.023 132.5769);
1013
+ --popover: oklch(0.1663 0.0138 135.2766);
1014
+ --popover-foreground: oklch(0.9742 0.0101 131.3574);
1015
+ --primary: oklch(0.9729 0.0101 145.4971);
1016
+ --primary-foreground: oklch(0.1324 0.0033 145.3864);
1017
+ --secondary: oklch(0.1844 0.0062 122.0354);
1018
+ --secondary-foreground: oklch(0.8009 0.0399 133.2927);
1019
+ --muted: oklch(0.1579 0.0017 196.9874);
1020
+ --muted-foreground: oklch(0.7897 0.0171 133.8518);
1021
+ --accent: oklch(0.1391 0.0113 136.9894);
1022
+ --accent-foreground: oklch(0.9742 0.0101 131.3574);
1023
+ --destructive: oklch(0.2258 0.0524 12.6119);
1024
+ --destructive-foreground: oklch(1 0 0);
1025
+ --border: oklch(0.1811 0.0128 129.2819);
1026
+ --input: oklch(0.2213 0.0193 135.2915);
1027
+ --ring: oklch(0.9248 0.0051 145.5339);
1028
+ }`;
1029
+ const parts = [];
1030
+ if (tailwindMajor >= 4) {
1031
+ if (mode === "full") {
1032
+ parts.push('@import "tailwindcss";');
1033
+ }
1034
+ parts.push(lightVars, darkVars);
1035
+ parts.push(`@theme inline {
1036
+ --color-background: var(--background);
1037
+ --color-foreground: var(--foreground);
1038
+ --color-card: var(--card);
1039
+ --color-card-foreground: var(--card-foreground);
1040
+ --color-popover: var(--popover);
1041
+ --color-popover-foreground: var(--popover-foreground);
1042
+ --color-primary: var(--primary);
1043
+ --color-primary-foreground: var(--primary-foreground);
1044
+ --color-secondary: var(--secondary);
1045
+ --color-secondary-foreground: var(--secondary-foreground);
1046
+ --color-muted: var(--muted);
1047
+ --color-muted-foreground: var(--muted-foreground);
1048
+ --color-accent: var(--accent);
1049
+ --color-accent-foreground: var(--accent-foreground);
1050
+ --color-destructive: var(--destructive);
1051
+ --color-destructive-foreground: var(--destructive-foreground);
1052
+ --color-border: var(--border);
1053
+ --color-input: var(--input);
1054
+ --color-ring: var(--ring);
1055
+ --radius-sm: calc(var(--radius) - 4px);
1056
+ --radius-md: calc(var(--radius) - 2px);
1057
+ --radius-lg: var(--radius);
1058
+ --radius-xl: calc(var(--radius) + 4px);
1059
+ }`);
1060
+ parts.push(`@layer base {
1061
+ * {
1062
+ @apply border-border outline-ring/50;
1063
+ }
1064
+ body {
1065
+ @apply bg-background text-foreground;
1066
+ }
1067
+ }`);
1068
+ } else {
1069
+ if (mode === "full") {
1070
+ parts.push("@tailwind base;\n@tailwind components;\n@tailwind utilities;");
1071
+ }
1072
+ parts.push(lightVars, darkVars);
1073
+ parts.push(`@layer base {
1074
+ * {
1075
+ @apply border-border;
1076
+ }
1077
+ body {
1078
+ @apply bg-background text-foreground;
1079
+ }
1080
+ }`);
1081
+ }
1082
+ return parts.join("\n\n") + "\n";
1083
+ }
1084
+ async function injectThemeCSS(cssFilePath, tailwindMajor) {
1085
+ await fs4.ensureDir(path4.dirname(cssFilePath));
1086
+ if (fs4.existsSync(cssFilePath)) {
1087
+ const existing = await fs4.readFile(cssFilePath, "utf-8");
1088
+ if (existing.includes("--primary:")) {
1089
+ return { path: cssFilePath, skipped: true };
1090
+ }
1091
+ const theme2 = getThemeCSS(tailwindMajor, "append");
1092
+ await fs4.writeFile(cssFilePath, existing.trimEnd() + "\n\n" + theme2);
1093
+ return { path: cssFilePath, updated: true };
1094
+ }
1095
+ const theme = getThemeCSS(tailwindMajor, "full");
1096
+ await fs4.writeFile(cssFilePath, theme);
1097
+ return { path: cssFilePath, created: true };
1098
+ }
1099
+ function getTW3ColorExtend() {
1100
+ return `colors: {
1101
+ border: "var(--border)",
1102
+ input: "var(--input)",
1103
+ ring: "var(--ring)",
1104
+ background: "var(--background)",
1105
+ foreground: "var(--foreground)",
1106
+ primary: {
1107
+ DEFAULT: "var(--primary)",
1108
+ foreground: "var(--primary-foreground)",
1109
+ },
1110
+ secondary: {
1111
+ DEFAULT: "var(--secondary)",
1112
+ foreground: "var(--secondary-foreground)",
1113
+ },
1114
+ destructive: {
1115
+ DEFAULT: "var(--destructive)",
1116
+ foreground: "var(--destructive-foreground)",
1117
+ },
1118
+ muted: {
1119
+ DEFAULT: "var(--muted)",
1120
+ foreground: "var(--muted-foreground)",
1121
+ },
1122
+ accent: {
1123
+ DEFAULT: "var(--accent)",
1124
+ foreground: "var(--accent-foreground)",
1125
+ },
1126
+ popover: {
1127
+ DEFAULT: "var(--popover)",
1128
+ foreground: "var(--popover-foreground)",
1129
+ },
1130
+ card: {
1131
+ DEFAULT: "var(--card)",
1132
+ foreground: "var(--card-foreground)",
1133
+ },
1134
+ },
1135
+ borderRadius: {
1136
+ lg: "var(--radius)",
1137
+ md: "calc(var(--radius) - 2px)",
1138
+ sm: "calc(var(--radius) - 4px)",
1139
+ },`;
1140
+ }
1141
+ function createTW3Config(isCjs) {
1142
+ const colorExtend = getTW3ColorExtend();
1143
+ if (isCjs) {
1144
+ return `/** @type {import('tailwindcss').Config} */
1145
+ module.exports = {
1146
+ content: [
1147
+ "./src/**/*.{js,ts,jsx,tsx}",
1148
+ "./app/**/*.{js,ts,jsx,tsx}",
1149
+ "./components/**/*.{js,ts,jsx,tsx}",
1150
+ ],
1151
+ theme: {
1152
+ extend: {
1153
+ ${colorExtend}
1154
+ },
1155
+ },
1156
+ plugins: [],
1157
+ }
1158
+ `;
1159
+ }
1160
+ return `/** @type {import('tailwindcss').Config} */
1161
+ export default {
1162
+ content: [
1163
+ "./src/**/*.{js,ts,jsx,tsx}",
1164
+ "./app/**/*.{js,ts,jsx,tsx}",
1165
+ "./components/**/*.{js,ts,jsx,tsx}",
1166
+ ],
1167
+ theme: {
1168
+ extend: {
1169
+ ${colorExtend}
1170
+ },
1171
+ },
1172
+ plugins: [],
1173
+ }
1174
+ `;
1175
+ }
1176
+ async function ensureTW3ColorConfig(projectRoot, configFileName) {
1177
+ const candidates = [
1178
+ configFileName,
1179
+ "tailwind.config.ts",
1180
+ "tailwind.config.js",
1181
+ "tailwind.config.mjs",
1182
+ "tailwind.config.cjs"
1183
+ ];
1184
+ let configPath = null;
1185
+ for (const candidate of candidates) {
1186
+ const candidatePath = path4.join(projectRoot, candidate);
1187
+ if (fs4.existsSync(candidatePath)) {
1188
+ configPath = candidatePath;
1189
+ break;
1190
+ }
1191
+ }
1192
+ if (!configPath) {
1193
+ configPath = path4.join(projectRoot, configFileName);
1194
+ const ext = path4.extname(configPath);
1195
+ const isCjs = ext === ".cjs";
1196
+ await fs4.writeFile(configPath, createTW3Config(isCjs));
1197
+ return { path: configPath, created: true };
1198
+ }
1199
+ const content = await fs4.readFile(configPath, "utf-8");
1200
+ if (content.includes('"var(--primary)"') || content.includes("'var(--primary)'")) {
1201
+ return { path: configPath, skipped: true };
1202
+ }
1203
+ const colorExtend = getTW3ColorExtend();
1204
+ const extendMatch = content.match(/extend\s*:\s*\{/);
1205
+ if (extendMatch && extendMatch.index !== void 0) {
1206
+ const insertPos = extendMatch.index + extendMatch[0].length;
1207
+ const updated = content.slice(0, insertPos) + "\n " + colorExtend + content.slice(insertPos);
1208
+ await fs4.writeFile(configPath, updated);
1209
+ return { path: configPath, updated: true };
1210
+ }
1211
+ const themeMatch = content.match(/theme\s*:\s*\{/);
1212
+ if (themeMatch && themeMatch.index !== void 0) {
1213
+ const insertPos = themeMatch.index + themeMatch[0].length;
1214
+ const extendBlock = `
1215
+ extend: {
1216
+ ${colorExtend}
1217
+ },`;
1218
+ const updated = content.slice(0, insertPos) + extendBlock + content.slice(insertPos);
1219
+ await fs4.writeFile(configPath, updated);
1220
+ return { path: configPath, updated: true };
1221
+ }
1222
+ const closingBrace = content.lastIndexOf("}");
1223
+ if (closingBrace !== -1) {
1224
+ const before = content.slice(0, closingBrace).trimEnd();
1225
+ const needsComma = !before.endsWith(",") && !before.endsWith("{");
1226
+ const themeBlock = `${needsComma ? "," : ""}
1227
+ theme: {
1228
+ extend: {
1229
+ ${colorExtend}
1230
+ },
1231
+ },
1232
+ `;
1233
+ const updated = before + themeBlock + content.slice(closingBrace);
1234
+ await fs4.writeFile(configPath, updated);
1235
+ return { path: configPath, updated: true };
1236
+ }
1237
+ return { path: configPath, skipped: true };
1238
+ }
1239
+ function readJsonConfig(filePath) {
1240
+ try {
1241
+ const content = fs4.readFileSync(filePath, "utf-8");
1242
+ return parseJsonWithComments(content);
1243
+ } catch {
1244
+ return null;
1245
+ }
1246
+ }
1247
+ async function ensureAtAlias(projectRoot, srcDir, isTypeScript) {
1248
+ const tsConfigPath = path4.join(projectRoot, "tsconfig.json");
1249
+ const jsConfigPath = path4.join(projectRoot, "jsconfig.json");
1250
+ const configPath = fs4.existsSync(tsConfigPath) ? tsConfigPath : fs4.existsSync(jsConfigPath) ? jsConfigPath : path4.join(projectRoot, isTypeScript ? "tsconfig.json" : "jsconfig.json");
1251
+ const existedBefore = fs4.existsSync(configPath);
1252
+ const parsedConfig = readJsonConfig(configPath);
1253
+ if (existedBefore && !parsedConfig) {
1254
+ return { path: configPath, skipped: true };
1255
+ }
1256
+ const parsed = parsedConfig ?? {};
1257
+ const compilerOptions = parsed.compilerOptions ?? {};
1258
+ const rawPaths = compilerOptions.paths ?? {};
1259
+ const paths = {};
1260
+ for (const [key, value] of Object.entries(rawPaths)) {
1261
+ if (!Array.isArray(value)) {
1262
+ continue;
1263
+ }
1264
+ const targets = value.filter((entry) => typeof entry === "string");
1265
+ if (targets.length > 0) {
1266
+ paths[key] = targets;
1267
+ }
1268
+ }
1269
+ const aliasTarget = srcDir === "." ? "*" : `${srcDir}/*`;
1270
+ const alreadyConfigured = compilerOptions.baseUrl === "." && Array.isArray(paths["@/*"]) && paths["@/*"][0] === aliasTarget;
1271
+ if (alreadyConfigured) {
1272
+ return { path: configPath };
1273
+ }
1274
+ const nextConfig = {
1275
+ ...parsed,
1276
+ compilerOptions: {
1277
+ ...compilerOptions,
1278
+ baseUrl: ".",
1279
+ paths: {
1280
+ ...paths,
1281
+ "@/*": [aliasTarget]
1282
+ }
1283
+ }
1284
+ };
1285
+ try {
1286
+ await fs4.writeFile(configPath, `${JSON.stringify(nextConfig, null, 2)}
1287
+ `);
1288
+ return { path: configPath, [existedBefore ? "updated" : "created"]: true };
1289
+ } catch {
1290
+ return { path: configPath, skipped: true };
1291
+ }
1292
+ }
1293
+ var initCommand = new Command2("init").description("Initialize Dinachi UI in your project").option("--skip-install", "Skip package installation").action(async (options) => {
602
1294
  console.log(chalk2.bold.cyan("\u{1F3A8} Welcome to Dinachi UI!"));
603
1295
  console.log();
604
- const packageJsonPath = path3.join(process.cwd(), "package.json");
605
- if (!fs3.existsSync(packageJsonPath)) {
1296
+ const projectRoot = process.cwd();
1297
+ const packageJsonPath = path4.join(projectRoot, "package.json");
1298
+ if (!fs4.existsSync(packageJsonPath)) {
606
1299
  console.log(chalk2.red("\u274C No package.json found. Please run this command in a valid project."));
607
1300
  process.exit(1);
608
1301
  }
@@ -629,69 +1322,116 @@ var initCommand = new Command2("init").description("Initialize Dinachi UI in you
629
1322
  }
630
1323
  const spinner = ora2("Setting up Dinachi UI...").start();
631
1324
  try {
632
- const normalizedComponentsPath = normalizePath(response.componentsPath);
633
- const normalizedUtilsPath = normalizePath(response.utilsPath);
634
- await fs3.ensureDir(normalizedComponentsPath);
635
- await fs3.ensureDir(normalizedUtilsPath);
636
- const utilsContent = `import { type ClassValue, clsx } from "clsx"
637
- import { twMerge } from "tailwind-merge"
638
-
639
- export function cn(...inputs: ClassValue[]) {
640
- return twMerge(clsx(inputs))
641
- }
642
- `;
643
- await fs3.writeFile(path3.join(normalizedUtilsPath, "utils.ts"), utilsContent);
1325
+ const normalizedComponentsPath = normalizeProjectPath(response.componentsPath, projectRoot);
1326
+ const normalizedUtilsPath = normalizeProjectPath(response.utilsPath, projectRoot);
1327
+ const componentsDirPath = path4.resolve(projectRoot, normalizedComponentsPath);
1328
+ const utilsDirPath = path4.resolve(projectRoot, normalizedUtilsPath);
1329
+ const utilsFilePath = path4.join(utilsDirPath, "utils.ts");
1330
+ await fs4.ensureDir(componentsDirPath);
1331
+ await fs4.ensureDir(utilsDirPath);
1332
+ const utilsContent = createUtilsFileContent();
1333
+ await fs4.writeFile(utilsFilePath, utilsContent);
1334
+ spinner.text = "Setting up color theme...";
1335
+ const tailwindMajor = detectTailwindMajorVersion(projectRoot);
1336
+ const cssFilePath = path4.resolve(projectRoot, projectConfig.cssPath);
1337
+ const themeCSSResult = await injectThemeCSS(cssFilePath, tailwindMajor);
1338
+ let twColorConfigResult = null;
1339
+ if (tailwindMajor < 4) {
1340
+ spinner.text = "Configuring Tailwind color mappings...";
1341
+ twColorConfigResult = await ensureTW3ColorConfig(projectRoot, projectConfig.tailwindConfig);
1342
+ }
644
1343
  spinner.text = "Installing dependencies...";
645
- const deps = [
646
- "class-variance-authority",
647
- "clsx",
648
- "tailwind-merge"
649
- ];
650
- const packageManager = getPackageManager2();
651
- const installCmd = getInstallCommand2(packageManager, deps);
652
- execSync2(installCmd, { stdio: "inherit" });
653
- const componentsAlias = pathToAlias(normalizedComponentsPath);
654
- const libAlias = pathToAlias(normalizedUtilsPath);
655
- const uiAlias = componentsAlias;
656
- const libParentAlias = pathToAlias(path3.dirname(normalizedUtilsPath) === "." ? normalizedUtilsPath : normalizedUtilsPath);
657
- const rscEnabled = projectConfig.framework === "next.js";
658
- const configContent = JSON.stringify({
659
- style: "default",
660
- rsc: rscEnabled,
661
- tsx: true,
662
- tailwind: {
663
- config: projectConfig.tailwindConfig,
664
- css: projectConfig.cssPath,
665
- baseColor: "slate",
666
- cssVariables: true
667
- },
668
- aliases: {
669
- components: pathToAlias(path3.dirname(normalizedComponentsPath)),
670
- utils: `${libAlias}/utils`,
671
- ui: uiAlias,
672
- lib: libAlias,
673
- hooks: `${pathToAlias(projectConfig.srcDir)}/hooks`
1344
+ const deps = ["class-variance-authority", "clsx", "tailwind-merge"];
1345
+ if (!options.skipInstall) {
1346
+ const packageManager = detectPackageManager(projectRoot);
1347
+ const installCmd = getInstallCommand(packageManager, deps.map(toInstallSpec));
1348
+ execSync2(installCmd, { stdio: "inherit", cwd: projectRoot });
1349
+ }
1350
+ const hooksPath = projectConfig.srcDir === "." ? "hooks" : path4.join(projectConfig.srcDir, "hooks").replace(/\\/g, "/");
1351
+ const configContent = JSON.stringify(
1352
+ {
1353
+ style: "default",
1354
+ rsc: projectConfig.framework === "next.js",
1355
+ tsx: true,
1356
+ tailwind: {
1357
+ config: projectConfig.tailwindConfig,
1358
+ css: projectConfig.cssPath,
1359
+ baseColor: "slate",
1360
+ cssVariables: true
1361
+ },
1362
+ aliases: {
1363
+ components: toConfigPath(path4.dirname(normalizedComponentsPath)),
1364
+ utils: toConfigPath(path4.join(normalizedUtilsPath, "utils")),
1365
+ ui: toConfigPath(normalizedComponentsPath),
1366
+ lib: toConfigPath(normalizedUtilsPath),
1367
+ hooks: toConfigPath(hooksPath)
1368
+ },
1369
+ iconLibrary: "lucide"
674
1370
  },
675
- iconLibrary: "lucide"
676
- }, null, 2);
677
- await fs3.writeFile("components.json", configContent);
1371
+ null,
1372
+ 2
1373
+ );
1374
+ await fs4.writeFile(path4.join(projectRoot, "components.json"), `${configContent}
1375
+ `);
1376
+ const aliasConfigUpdate = await ensureAtAlias(projectRoot, projectConfig.srcDir, projectConfig.isTypeScript);
678
1377
  spinner.succeed("\u2705 Dinachi UI setup complete!");
679
1378
  console.log();
680
1379
  console.log("Next steps:");
681
1380
  console.log(` 1. Add a component: ${chalk2.cyan("npx @dinachi/cli add button")}`);
682
- console.log(` 2. Components will be installed to: ${chalk2.cyan(normalizedComponentsPath)}`);
683
- console.log(` 3. Utils available at: ${chalk2.cyan(path3.join(normalizedUtilsPath, "utils.ts"))}`);
1381
+ console.log(` 2. Components will be installed to: ${chalk2.cyan(componentsDirPath)}`);
1382
+ console.log(` 3. Utils available at: ${chalk2.cyan(utilsFilePath)}`);
1383
+ console.log();
1384
+ if (aliasConfigUpdate.created) {
1385
+ console.log(` ${chalk2.green("+")} Added @/* path alias in ${aliasConfigUpdate.path}`);
1386
+ } else if (aliasConfigUpdate.updated) {
1387
+ console.log(` ${chalk2.blue("~")} Updated @/* path alias in ${aliasConfigUpdate.path}`);
1388
+ } else if (aliasConfigUpdate.skipped) {
1389
+ console.log(
1390
+ ` ${chalk2.yellow("!")} Could not update ${aliasConfigUpdate.path}. Configure @/* manually if you use alias imports.`
1391
+ );
1392
+ }
1393
+ if (themeCSSResult.created) {
1394
+ console.log(` ${chalk2.green("+")} Created ${projectConfig.cssPath} with color theme (light + dark)`);
1395
+ } else if (themeCSSResult.updated) {
1396
+ console.log(` ${chalk2.blue("~")} Updated ${projectConfig.cssPath} with color theme (light + dark)`);
1397
+ } else if (themeCSSResult.skipped) {
1398
+ console.log(` ${chalk2.gray("-")} Color theme already configured in ${projectConfig.cssPath}`);
1399
+ }
1400
+ if (twColorConfigResult) {
1401
+ if (twColorConfigResult.created) {
1402
+ console.log(` ${chalk2.green("+")} Created ${projectConfig.tailwindConfig} with color mappings`);
1403
+ } else if (twColorConfigResult.updated) {
1404
+ console.log(` ${chalk2.blue("~")} Updated ${projectConfig.tailwindConfig} with color mappings`);
1405
+ } else if (twColorConfigResult.skipped) {
1406
+ console.log(` ${chalk2.gray("-")} Color mappings already in ${projectConfig.tailwindConfig}`);
1407
+ }
1408
+ }
1409
+ if (!projectConfig.isTypeScript) {
1410
+ console.log();
1411
+ console.log(
1412
+ chalk2.yellow(
1413
+ "\u26A0\uFE0F Dinachi components are TypeScript-first. Your project can still consume TSX files, but type-check tooling is recommended."
1414
+ )
1415
+ );
1416
+ }
684
1417
  if (projectConfig.framework === "next.js") {
685
1418
  console.log();
686
1419
  console.log(chalk2.blue("\u{1F4DD} Next.js specific notes:"));
687
- console.log(` - RSC (React Server Components) enabled in config`);
688
- console.log(` - Make sure to add "use client" directive if needed`);
1420
+ console.log(" - RSC (React Server Components) enabled in config");
1421
+ console.log(' - Add "use client" for interactive components where needed');
689
1422
  console.log(` - Tailwind config: ${chalk2.cyan(projectConfig.tailwindConfig)}`);
690
1423
  } else if (projectConfig.framework === "remix") {
691
1424
  console.log();
692
1425
  console.log(chalk2.blue("\u{1F4DD} Remix specific notes:"));
693
- console.log(` - Components will be installed to: ${chalk2.cyan(normalizedComponentsPath)}`);
694
- console.log(` - Utils will be installed to: ${chalk2.cyan(normalizedUtilsPath)}`);
1426
+ console.log(` - Components directory: ${chalk2.cyan(componentsDirPath)}`);
1427
+ console.log(` - Utilities directory: ${chalk2.cyan(utilsDirPath)}`);
1428
+ }
1429
+ if (options.skipInstall) {
1430
+ console.log();
1431
+ console.log("Dependencies to install manually:");
1432
+ deps.forEach((dep) => {
1433
+ console.log(` ${chalk2.yellow("\u2022")} ${toInstallSpec(dep)}`);
1434
+ });
695
1435
  }
696
1436
  console.log();
697
1437
  console.log("\u{1F4A1} Tip: Install globally for shorter commands:");
@@ -702,42 +1442,17 @@ export function cn(...inputs: ClassValue[]) {
702
1442
  process.exit(1);
703
1443
  }
704
1444
  });
705
- function normalizePath(inputPath) {
706
- return inputPath.replace(/^\.\//, "").replace(/\/$/, "");
707
- }
708
- function pathToAlias(filePath) {
709
- const normalized = normalizePath(filePath);
710
- return `@/${normalized}`;
711
- }
712
- function getPackageManager2() {
713
- if (fs3.existsSync("bun.lockb") || fs3.existsSync("bun.lock")) return "bun";
714
- if (fs3.existsSync("pnpm-lock.yaml")) return "pnpm";
715
- if (fs3.existsSync("yarn.lock")) return "yarn";
716
- return "npm";
717
- }
718
- function getInstallCommand2(pm, deps) {
719
- const depsStr = deps.join(" ");
720
- switch (pm) {
721
- case "bun":
722
- return `bun add ${depsStr}`;
723
- case "pnpm":
724
- return `pnpm add ${depsStr}`;
725
- case "yarn":
726
- return `yarn add ${depsStr}`;
727
- default:
728
- return `npm install ${depsStr}`;
729
- }
730
- }
731
1445
  function detectProjectType() {
732
- const packageJsonPath = path3.join(process.cwd(), "package.json");
733
- if (!fs3.existsSync(packageJsonPath)) {
1446
+ const packageJsonPath = path4.join(process.cwd(), "package.json");
1447
+ if (!fs4.existsSync(packageJsonPath)) {
734
1448
  return getDefaultConfig("react", false);
735
1449
  }
736
- const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
737
- const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
738
- const hasSrcDir = fs3.existsSync(path3.join(process.cwd(), "src"));
739
- const hasAppDir = fs3.existsSync(path3.join(process.cwd(), "app"));
740
- const hasSrcAppDir = fs3.existsSync(path3.join(process.cwd(), "src", "app"));
1450
+ const packageJson = JSON.parse(fs4.readFileSync(packageJsonPath, "utf-8"));
1451
+ const deps = { ...packageJson.dependencies ?? {}, ...packageJson.devDependencies ?? {} };
1452
+ const hasSrcDir = fs4.existsSync(path4.join(process.cwd(), "src"));
1453
+ const hasAppDir = fs4.existsSync(path4.join(process.cwd(), "app"));
1454
+ const hasSrcAppDir = fs4.existsSync(path4.join(process.cwd(), "src", "app"));
1455
+ const isTypeScript = fs4.existsSync(path4.join(process.cwd(), "tsconfig.json")) || fs4.existsSync(path4.join(process.cwd(), "tsconfig.base.json")) || Boolean(deps.typescript);
741
1456
  const tailwindConfig = detectTailwindConfig();
742
1457
  const cssPath = detectCssPath(hasSrcDir, hasSrcAppDir);
743
1458
  if (deps.next) {
@@ -748,7 +1463,8 @@ function detectProjectType() {
748
1463
  utilsPath: "./src/lib",
749
1464
  tailwindConfig,
750
1465
  cssPath,
751
- srcDir: "src"
1466
+ srcDir: "src",
1467
+ isTypeScript
752
1468
  };
753
1469
  }
754
1470
  if (hasAppDir && !hasSrcDir) {
@@ -758,7 +1474,8 @@ function detectProjectType() {
758
1474
  utilsPath: "./lib",
759
1475
  tailwindConfig,
760
1476
  cssPath,
761
- srcDir: "."
1477
+ srcDir: ".",
1478
+ isTypeScript
762
1479
  };
763
1480
  }
764
1481
  return {
@@ -767,7 +1484,8 @@ function detectProjectType() {
767
1484
  utilsPath: hasSrcDir ? "./src/lib" : "./lib",
768
1485
  tailwindConfig,
769
1486
  cssPath,
770
- srcDir: hasSrcDir ? "src" : "."
1487
+ srcDir: hasSrcDir ? "src" : ".",
1488
+ isTypeScript
771
1489
  };
772
1490
  }
773
1491
  if (deps.vite || deps["@vitejs/plugin-react"]) {
@@ -777,7 +1495,8 @@ function detectProjectType() {
777
1495
  utilsPath: hasSrcDir ? "./src/lib" : "./lib",
778
1496
  tailwindConfig,
779
1497
  cssPath,
780
- srcDir: hasSrcDir ? "src" : "."
1498
+ srcDir: hasSrcDir ? "src" : ".",
1499
+ isTypeScript
781
1500
  };
782
1501
  }
783
1502
  if (deps["react-scripts"]) {
@@ -787,7 +1506,8 @@ function detectProjectType() {
787
1506
  utilsPath: "./src/lib",
788
1507
  tailwindConfig,
789
1508
  cssPath,
790
- srcDir: "src"
1509
+ srcDir: "src",
1510
+ isTypeScript
791
1511
  };
792
1512
  }
793
1513
  if (deps["@remix-run/react"]) {
@@ -797,62 +1517,79 @@ function detectProjectType() {
797
1517
  utilsPath: "./app/lib",
798
1518
  tailwindConfig,
799
1519
  cssPath: detectCssPath(false, false, true),
800
- srcDir: "app"
1520
+ srcDir: "app",
1521
+ isTypeScript
801
1522
  };
802
1523
  }
803
1524
  return getDefaultConfig("react", hasSrcDir);
804
1525
  }
805
1526
  function getDefaultConfig(framework, hasSrcDir) {
1527
+ const isTypeScript = fs4.existsSync(path4.join(process.cwd(), "tsconfig.json")) || fs4.existsSync(path4.join(process.cwd(), "tsconfig.base.json"));
806
1528
  return {
807
1529
  framework,
808
1530
  componentsPath: hasSrcDir ? "./src/components/ui" : "./components/ui",
809
1531
  utilsPath: hasSrcDir ? "./src/lib" : "./lib",
810
1532
  tailwindConfig: detectTailwindConfig(),
811
1533
  cssPath: hasSrcDir ? "src/index.css" : "index.css",
812
- srcDir: hasSrcDir ? "src" : "."
1534
+ srcDir: hasSrcDir ? "src" : ".",
1535
+ isTypeScript
813
1536
  };
814
1537
  }
815
1538
  function detectTailwindConfig() {
816
- if (fs3.existsSync(path3.join(process.cwd(), "tailwind.config.ts"))) {
817
- return "tailwind.config.ts";
818
- }
819
- if (fs3.existsSync(path3.join(process.cwd(), "tailwind.config.js"))) {
820
- return "tailwind.config.js";
821
- }
822
- if (fs3.existsSync(path3.join(process.cwd(), "tailwind.config.mjs"))) {
823
- return "tailwind.config.mjs";
1539
+ const configCandidates = [
1540
+ "tailwind.config.ts",
1541
+ "tailwind.config.js",
1542
+ "tailwind.config.mjs",
1543
+ "tailwind.config.cjs",
1544
+ "tailwind.config.cts",
1545
+ "tailwind.config.mts"
1546
+ ];
1547
+ for (const config of configCandidates) {
1548
+ if (fs4.existsSync(path4.join(process.cwd(), config))) {
1549
+ return config;
1550
+ }
824
1551
  }
825
1552
  return "tailwind.config.js";
826
1553
  }
827
1554
  function detectCssPath(hasSrcDir, hasSrcAppDir, isRemix = false) {
828
1555
  const cwd = process.cwd();
829
1556
  const possiblePaths = [
830
- // Next.js App Router
831
1557
  "app/globals.css",
832
1558
  "src/app/globals.css",
833
- // Next.js Pages / General
834
1559
  "styles/globals.css",
835
1560
  "src/styles/globals.css",
836
- // Vite / CRA
837
1561
  "src/index.css",
838
1562
  "index.css",
839
- // Remix
840
1563
  "app/tailwind.css",
841
1564
  "app/styles/tailwind.css"
842
1565
  ];
843
1566
  for (const cssPath of possiblePaths) {
844
- if (fs3.existsSync(path3.join(cwd, cssPath))) {
1567
+ if (fs4.existsSync(path4.join(cwd, cssPath))) {
845
1568
  return cssPath;
846
1569
  }
847
1570
  }
848
1571
  if (isRemix) return "app/tailwind.css";
849
1572
  if (hasSrcAppDir) return "src/app/globals.css";
850
1573
  if (hasSrcDir) return "src/index.css";
851
- return "app/globals.css";
1574
+ return "index.css";
852
1575
  }
853
1576
 
854
1577
  // src/index.ts
1578
+ import fs5 from "fs";
1579
+ import path5 from "path";
1580
+ import { fileURLToPath as fileURLToPath3 } from "url";
855
1581
  var program = new Command3();
856
- program.name("dinachi").description("Add Dinachi UI components to your project").version("0.5.0");
1582
+ var __filename3 = fileURLToPath3(import.meta.url);
1583
+ var __dirname3 = path5.dirname(__filename3);
1584
+ function getCliVersion() {
1585
+ try {
1586
+ const packageJsonPath = path5.resolve(__dirname3, "../package.json");
1587
+ const packageJson = JSON.parse(fs5.readFileSync(packageJsonPath, "utf-8"));
1588
+ return packageJson.version ?? "0.0.0";
1589
+ } catch {
1590
+ return "0.0.0";
1591
+ }
1592
+ }
1593
+ program.name("dinachi").description("Add Dinachi UI components to your project").version(getCliVersion());
857
1594
  program.addCommand(addCommand).addCommand(initCommand);
858
1595
  program.parse();