@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 +66 -54
- package/package.json +1 -1
- package/templates/button/button.tsx +3 -1
- package/templates/menu/menu.tsx +20 -20
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
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
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
|
-
|
|
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
|
|
781
|
-
const
|
|
782
|
-
for (const
|
|
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
|
-
|
|
776
|
+
const normalized = block.endsWith(";") ? block : `${block};`;
|
|
777
|
+
blocksToAppend.push(normalized);
|
|
793
778
|
}
|
|
794
|
-
if (
|
|
779
|
+
if (blocksToAppend.length === 0) {
|
|
795
780
|
return;
|
|
796
781
|
}
|
|
797
782
|
const updatedContent = `${existingContent.trimEnd()}
|
|
798
|
-
${
|
|
783
|
+
${blocksToAppend.join("\n")}
|
|
799
784
|
`;
|
|
800
785
|
await fs3.writeFile(targetPath, updatedContent);
|
|
801
|
-
|
|
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.
|
|
905
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
1379
|
+
const response = await prompts2([
|
|
1368
1380
|
{
|
|
1369
1381
|
type: "text",
|
|
1370
1382
|
name: "componentsPath",
|
package/package.json
CHANGED
|
@@ -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
|
)
|
package/templates/menu/menu.tsx
CHANGED
|
@@ -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-
|
|
34
|
-
"data-
|
|
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-
|
|
79
|
-
"origin-
|
|
80
|
-
"data-
|
|
81
|
-
"data-
|
|
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-
|
|
103
|
-
"data-
|
|
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-
|
|
122
|
-
"data-
|
|
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-
|
|
154
|
-
"data-
|
|
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-
|
|
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-
|
|
230
|
-
"data-
|
|
231
|
-
"data-
|
|
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-
|
|
253
|
-
"origin-
|
|
254
|
-
"data-
|
|
255
|
-
"data-
|
|
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}
|