@dinachi/cli 0.6.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ import path3 from "path";
11
11
  import { fileURLToPath as fileURLToPath2 } from "url";
12
12
  import ora from "ora";
13
13
  import chalk from "chalk";
14
+ import prompts from "prompts";
14
15
 
15
16
  // src/utils/registry.ts
16
17
  import fs from "fs-extra";
@@ -374,25 +375,14 @@ var LOCK_FILE_MAP = [
374
375
  { file: "yarn.lock", manager: "yarn" },
375
376
  { file: "package-lock.json", manager: "npm" }
376
377
  ];
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;
378
+ function detectPackageManager(startDir = process.cwd()) {
379
+ for (const entry of LOCK_FILE_MAP) {
380
+ if (fs2.existsSync(path2.join(startDir, entry.file))) {
381
+ return entry.manager;
385
382
  }
386
- current = parent;
387
383
  }
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
- }
384
+ const packageJsonPath = path2.join(startDir, "package.json");
385
+ if (fs2.existsSync(packageJsonPath)) {
396
386
  try {
397
387
  const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8"));
398
388
  const value = packageJson.packageManager ?? "";
@@ -401,21 +391,6 @@ function detectManagerFromPackageJson(startDir) {
401
391
  if (value.startsWith("bun@")) return "bun";
402
392
  if (value.startsWith("npm@")) return "npm";
403
393
  } 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
394
  }
420
395
  }
421
396
  return "npm";
@@ -699,7 +674,8 @@ async function ensureTW4Plugin(deps, cssFilePath) {
699
674
  let updated;
700
675
  if (importMatch) {
701
676
  updated = content.replace(importMatch[0], `${importMatch[0].trimEnd()}
702
- ${pluginLine}`);
677
+ ${pluginLine}
678
+ `);
703
679
  } else {
704
680
  updated = `${pluginLine}
705
681
  ${content}`;
@@ -769,6 +745,19 @@ ${updatedContent}`;
769
745
  function escapeRegex(value) {
770
746
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
771
747
  }
748
+ function extractExportBlocks(content) {
749
+ const results = [];
750
+ const regex = /export\s+(?:type\s+)?{[^}]*}\s*from\s+['"]\.\/([^'"]+)['"]\s*;?/g;
751
+ let match;
752
+ while ((match = regex.exec(content)) !== null) {
753
+ results.push({ block: match[0].trimEnd(), modulePath: match[1] });
754
+ }
755
+ const starRegex = /export\s+\*\s+from\s+['"]\.\/([^'"]+)['"]\s*;?/g;
756
+ while ((match = starRegex.exec(content)) !== null) {
757
+ results.push({ block: match[0].trimEnd(), modulePath: match[1] });
758
+ }
759
+ return results;
760
+ }
772
761
  async function handleIndexFile(sourcePath, targetPath, allFilesAdded, targetDir) {
773
762
  const templateContent = stripTemplateDirective(await fs3.readFile(sourcePath, "utf-8"));
774
763
  if (!fs3.existsSync(targetPath)) {
@@ -777,28 +766,27 @@ async function handleIndexFile(sourcePath, targetPath, allFilesAdded, targetDir)
777
766
  return;
778
767
  }
779
768
  const existingContent = await fs3.readFile(targetPath, "utf-8");
780
- const exportLines = templateContent.split("\n").map((line) => line.trim()).filter((line) => /^export\s+/.test(line) && /from\s+['"]\.\/[^'"]+['"]/.test(line));
781
- const linesToAppend = [];
782
- for (const line of exportLines) {
783
- const match = line.match(/from\s+['"]\.\/([^'"]+)['"]/);
784
- if (!match) {
785
- continue;
786
- }
787
- const modulePath = match[1];
769
+ const exportBlocks = extractExportBlocks(templateContent);
770
+ const blocksToAppend = [];
771
+ for (const { block, modulePath } of exportBlocks) {
788
772
  const modulePathPattern = new RegExp(`from\\s+['"]\\./${escapeRegex(modulePath)}['"]`);
789
773
  if (modulePathPattern.test(existingContent)) {
790
774
  continue;
791
775
  }
792
- linesToAppend.push(line.endsWith(";") ? line : `${line};`);
776
+ const normalized = block.endsWith(";") ? block : `${block};`;
777
+ blocksToAppend.push(normalized);
793
778
  }
794
- if (linesToAppend.length === 0) {
779
+ if (blocksToAppend.length === 0) {
795
780
  return;
796
781
  }
797
782
  const updatedContent = `${existingContent.trimEnd()}
798
- ${linesToAppend.join("\n")}
783
+ ${blocksToAppend.join("\n")}
799
784
  `;
800
785
  await fs3.writeFile(targetPath, updatedContent);
801
- allFilesAdded.push({ name: "index.ts", path: path3.join(targetDir, "index.ts") });
786
+ const indexPath = path3.join(targetDir, "index.ts");
787
+ if (!allFilesAdded.some((f) => f.path === indexPath)) {
788
+ allFilesAdded.push({ name: "index.ts", path: indexPath });
789
+ }
802
790
  }
803
791
  var addCommand = new Command("add").description("Add a component to your project").argument("[components...]", "Names of the components to add (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(
804
792
  async (componentNames, options) => {
@@ -901,8 +889,19 @@ var addCommand = new Command("add").description("Add a component to your project
901
889
  continue;
902
890
  }
903
891
  if (fs3.existsSync(targetPath) && !options.overwrite) {
904
- spinner.warn(`\u26A0\uFE0F ${file.name} already exists. Use --overwrite to replace it.`);
905
- continue;
892
+ spinner.stop();
893
+ const { overwrite } = await prompts({
894
+ type: "confirm",
895
+ name: "overwrite",
896
+ message: `${file.name} already exists. Overwrite?`,
897
+ initial: false
898
+ });
899
+ if (!overwrite) {
900
+ console.log(` ${chalk.yellow("\u2298")} Skipped ${file.name}`);
901
+ spinner.start();
902
+ continue;
903
+ }
904
+ spinner.start();
906
905
  }
907
906
  const templateContent = stripTemplateDirective(await fs3.readFile(sourcePath, "utf-8"));
908
907
  const rewrittenContent = rewriteTemplateImports(templateContent, targetPath, utilsFilePath, libDir);
@@ -996,7 +995,7 @@ import { Command as Command2 } from "commander";
996
995
  import { execSync as execSync2 } from "child_process";
997
996
  import fs4 from "fs-extra";
998
997
  import path4 from "path";
999
- import prompts from "prompts";
998
+ import prompts2 from "prompts";
1000
999
  import chalk2 from "chalk";
1001
1000
  import ora2 from "ora";
1002
1001
  function normalizeProjectPath(inputPath, projectRoot) {
@@ -1032,7 +1031,16 @@ function detectTailwindMajorVersion2(projectRoot) {
1032
1031
  return 4;
1033
1032
  }
1034
1033
  }
1035
- function getThemeCSS(tailwindMajor, mode) {
1034
+ var DINACHI_THEME_PREFIXES = ["--color-", "--radius-"];
1035
+ function extractPreservedThemeVars(css) {
1036
+ const themeMatch = css.match(/@theme\s+inline\s*\{([^}]*)\}/);
1037
+ if (!themeMatch) return [];
1038
+ return themeMatch[1].split("\n").map((line) => line.trim()).filter((line) => {
1039
+ if (!line || line.startsWith("//") || line.startsWith("/*")) return false;
1040
+ return line.startsWith("--") && !DINACHI_THEME_PREFIXES.some((p) => line.startsWith(p));
1041
+ });
1042
+ }
1043
+ function getThemeCSS(tailwindMajor, mode, preservedThemeVars = []) {
1036
1044
  const lightVars = `:root {
1037
1045
  --background: oklch(0.986 0.0034 145.5499);
1038
1046
  --foreground: oklch(0.1459 0.0497 142.4953);
@@ -1082,6 +1090,7 @@ function getThemeCSS(tailwindMajor, mode) {
1082
1090
  parts.push('@import "tailwindcss";');
1083
1091
  }
1084
1092
  parts.push(lightVars, darkVars);
1093
+ const preservedLines = preservedThemeVars.length > 0 ? "\n" + preservedThemeVars.map((v) => ` ${v}`).join("\n") : "";
1085
1094
  parts.push(`@theme inline {
1086
1095
  --color-background: var(--background);
1087
1096
  --color-foreground: var(--foreground);
@@ -1105,7 +1114,7 @@ function getThemeCSS(tailwindMajor, mode) {
1105
1114
  --radius-sm: calc(var(--radius) - 4px);
1106
1115
  --radius-md: calc(var(--radius) - 2px);
1107
1116
  --radius-lg: var(--radius);
1108
- --radius-xl: calc(var(--radius) + 4px);
1117
+ --radius-xl: calc(var(--radius) + 4px);${preservedLines}
1109
1118
  }`);
1110
1119
  parts.push(`@layer base {
1111
1120
  * {
@@ -1148,9 +1157,10 @@ async function injectThemeCSS(cssFilePath, tailwindMajor) {
1148
1157
  if (existing.includes("--primary:")) {
1149
1158
  return { path: cssFilePath, skipped: true };
1150
1159
  }
1160
+ const preservedVars = tailwindMajor >= 4 ? extractPreservedThemeVars(existing) : [];
1151
1161
  const cleaned = stripConflictingCSS(existing);
1152
- const theme2 = getThemeCSS(tailwindMajor, "append");
1153
- const result = cleaned.length > 0 ? cleaned + "\n\n" + theme2 : getThemeCSS(tailwindMajor, "full");
1162
+ const theme2 = getThemeCSS(tailwindMajor, "append", preservedVars);
1163
+ const result = cleaned.length > 0 ? cleaned + "\n\n" + theme2 : getThemeCSS(tailwindMajor, "full", preservedVars);
1154
1164
  await fs4.writeFile(cssFilePath, result);
1155
1165
  return { path: cssFilePath, updated: true };
1156
1166
  }
@@ -1329,7 +1339,9 @@ async function ensureAtAlias(projectRoot, srcDir, isTypeScript) {
1329
1339
  }
1330
1340
  }
1331
1341
  const aliasTarget = srcDir === "." ? "*" : `${srcDir}/*`;
1332
- const alreadyConfigured = compilerOptions.baseUrl === "." && Array.isArray(paths["@/*"]) && paths["@/*"][0] === aliasTarget;
1342
+ const aliasTargetAlt = srcDir === "." ? "./*" : `./${srcDir}/*`;
1343
+ const existingAlias = paths["@/*"]?.[0];
1344
+ const alreadyConfigured = compilerOptions.baseUrl === "." && existingAlias === aliasTarget || existingAlias === aliasTargetAlt;
1333
1345
  if (alreadyConfigured) {
1334
1346
  return { path: configPath };
1335
1347
  }
@@ -1364,7 +1376,7 @@ var initCommand = new Command2("init").description("Initialize Dinachi UI in you
1364
1376
  const projectConfig = detectProjectType();
1365
1377
  console.log(chalk2.gray(`Detected ${projectConfig.framework} project`));
1366
1378
  console.log();
1367
- const response = await prompts([
1379
+ const response = await prompts2([
1368
1380
  {
1369
1381
  type: "text",
1370
1382
  name: "componentsPath",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dinachi/cli",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "CLI for adding Dinachi UI components to your project",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -39,11 +39,13 @@ export interface ButtonProps
39
39
  }
40
40
 
41
41
  const Button = React.forwardRef<HTMLElement, ButtonProps>(
42
- ({ className, variant, size, ...props }, ref) => {
42
+ ({ className, variant, size, render, nativeButton, ...props }, ref) => {
43
43
  return (
44
44
  <BaseButton
45
45
  className={cn(buttonVariants({ variant, size, className }))}
46
46
  ref={ref}
47
+ render={render}
48
+ nativeButton={render ? (nativeButton ?? false) : nativeButton}
47
49
  {...props}
48
50
  />
49
51
  )
@@ -30,8 +30,8 @@ const MenuTrigger = React.forwardRef<
30
30
  "inline-flex h-10 items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium",
31
31
  "hover:bg-accent hover:text-accent-foreground",
32
32
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
33
- "data-[popup-open]:bg-accent data-[popup-open]:text-accent-foreground",
34
- "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
33
+ "data-popup-open:bg-accent data-popup-open:text-accent-foreground",
34
+ "data-disabled:pointer-events-none data-disabled:opacity-50",
35
35
  className
36
36
  )}
37
37
  {...props}
@@ -75,10 +75,10 @@ const MenuContent = React.forwardRef<
75
75
  <MenuPrimitive.Popup
76
76
  ref={ref}
77
77
  className={cn(
78
- "z-50 min-w-[10rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
79
- "origin-[var(--transform-origin)] outline-none",
80
- "data-[starting-style]:animate-in data-[starting-style]:fade-in-0 data-[starting-style]:zoom-in-95",
81
- "data-[ending-style]:animate-out data-[ending-style]:fade-out-0 data-[ending-style]:zoom-out-95",
78
+ "z-50 min-w-40 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
79
+ "origin-(--transform-origin) outline-none",
80
+ "data-starting-style:animate-in data-starting-style:fade-in-0 data-starting-style:zoom-in-95",
81
+ "data-ending-style:animate-out data-ending-style:fade-out-0 data-ending-style:zoom-out-95",
82
82
  className
83
83
  )}
84
84
  {...props}
@@ -99,8 +99,8 @@ const MenuItem = React.forwardRef<
99
99
  className={cn(
100
100
  "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
101
101
  "focus:bg-accent focus:text-accent-foreground",
102
- "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
103
- "data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground",
102
+ "data-disabled:pointer-events-none data-disabled:opacity-50",
103
+ "data-highlighted:bg-accent data-highlighted:text-accent-foreground",
104
104
  inset && "pl-8",
105
105
  className
106
106
  )}
@@ -118,8 +118,8 @@ const MenuCheckboxItem = React.forwardRef<
118
118
  className={cn(
119
119
  "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
120
120
  "focus:bg-accent focus:text-accent-foreground",
121
- "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
122
- "data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground",
121
+ "data-disabled:pointer-events-none data-disabled:opacity-50",
122
+ "data-highlighted:bg-accent data-highlighted:text-accent-foreground",
123
123
  className
124
124
  )}
125
125
  checked={checked}
@@ -150,14 +150,14 @@ const MenuRadioItem = React.forwardRef<
150
150
  className={cn(
151
151
  "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
152
152
  "focus:bg-accent focus:text-accent-foreground",
153
- "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
154
- "data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground",
153
+ "data-disabled:pointer-events-none data-disabled:opacity-50",
154
+ "data-highlighted:bg-accent data-highlighted:text-accent-foreground",
155
155
  className
156
156
  )}
157
157
  {...props}
158
158
  >
159
159
  <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
160
- <Circle className="h-2 w-2 fill-current data-[checked]:block data-[unchecked]:hidden" />
160
+ <Circle className="h-2 w-2 fill-current data-checked:block data-unchecked:hidden" />
161
161
  </span>
162
162
  {children}
163
163
  </MenuPrimitive.RadioItem>
@@ -226,9 +226,9 @@ const MenuSubTrigger = React.forwardRef<
226
226
  className={cn(
227
227
  "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
228
228
  "focus:bg-accent focus:text-accent-foreground",
229
- "data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground",
230
- "data-[popup-open]:bg-accent data-[popup-open]:text-accent-foreground",
231
- "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
229
+ "data-highlighted:bg-accent data-highlighted:text-accent-foreground",
230
+ "data-popup-open:bg-accent data-popup-open:text-accent-foreground",
231
+ "data-disabled:pointer-events-none data-disabled:opacity-50",
232
232
  inset && "pl-8",
233
233
  className
234
234
  )}
@@ -249,10 +249,10 @@ const MenuSubContent = React.forwardRef<
249
249
  <MenuPrimitive.Popup
250
250
  ref={ref}
251
251
  className={cn(
252
- "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
253
- "origin-[var(--transform-origin)] outline-none",
254
- "data-[starting-style]:animate-in data-[starting-style]:fade-in-0 data-[starting-style]:zoom-in-95",
255
- "data-[ending-style]:animate-out data-[ending-style]:fade-out-0 data-[ending-style]:zoom-out-95",
252
+ "z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
253
+ "origin-(--transform-origin) outline-none",
254
+ "data-starting-style:animate-in data-starting-style:fade-in-0 data-starting-style:zoom-in-95",
255
+ "data-ending-style:animate-out data-ending-style:fade-out-0 data-ending-style:zoom-out-95",
256
256
  className
257
257
  )}
258
258
  {...props}