@dinachi/cli 0.1.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.
- package/README.md +131 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +643 -0
- package/package.json +56 -0
- package/templates/accordion/accordion.tsx +85 -0
- package/templates/accordion/index.ts +1 -0
- package/templates/alert-dialog/alert-dialog.tsx +162 -0
- package/templates/alert-dialog/index.ts +13 -0
- package/templates/avatar/avatar.tsx +64 -0
- package/templates/avatar/index.ts +1 -0
- package/templates/button/button.tsx +54 -0
- package/templates/button/index.ts +2 -0
- package/templates/checkbox/checkbox.tsx +29 -0
- package/templates/checkbox/index.ts +1 -0
- package/templates/checkbox-group/checkbox-group.tsx +19 -0
- package/templates/checkbox-group/index.ts +1 -0
- package/templates/collapsible/collapsible.tsx +65 -0
- package/templates/collapsible/index.ts +1 -0
- package/templates/context-menu/context-menu.tsx +278 -0
- package/templates/context-menu/index.ts +17 -0
- package/templates/dialog/dialog.tsx +158 -0
- package/templates/dialog/index.ts +1 -0
- package/templates/field/field.tsx +119 -0
- package/templates/field/index.ts +1 -0
- package/templates/form/form.tsx +71 -0
- package/templates/form/index.ts +1 -0
- package/templates/input/index.ts +2 -0
- package/templates/input/input.tsx +17 -0
- package/templates/menubar/index.ts +18 -0
- package/templates/menubar/menubar.tsx +303 -0
- package/templates/navigation-menu/index.ts +13 -0
- package/templates/navigation-menu/navigation-menu.tsx +205 -0
- package/templates/preview-card/index.ts +1 -0
- package/templates/preview-card/preview-card.tsx +142 -0
- package/templates/select/index.ts +1 -0
- package/templates/select/select.tsx +208 -0
- package/templates/slider/index.ts +9 -0
- package/templates/slider/slider.tsx +121 -0
- package/templates/tabs/index.ts +7 -0
- package/templates/tabs/tabs.tsx +89 -0
- package/templates/toast/index.ts +1 -0
- package/templates/toast/toast.tsx +276 -0
- package/templates/toggle/index.ts +1 -0
- package/templates/toggle/toggle.tsx +83 -0
- package/templates/toolbar/index.ts +1 -0
- package/templates/toolbar/toolbar.tsx +124 -0
- package/templates/tooltip/index.ts +1 -0
- package/templates/tooltip/tooltip.tsx +217 -0
- package/templates/utils/utils.ts +7 -0
- package/templates/utils/variants.ts +7 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command as Command3 } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/commands/add.ts
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
import { execSync } from "child_process";
|
|
9
|
+
import fs2 from "fs-extra";
|
|
10
|
+
import path2 from "path";
|
|
11
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
12
|
+
import ora from "ora";
|
|
13
|
+
import chalk from "chalk";
|
|
14
|
+
|
|
15
|
+
// src/utils/registry.ts
|
|
16
|
+
import fs from "fs-extra";
|
|
17
|
+
import path from "path";
|
|
18
|
+
import { fileURLToPath } from "url";
|
|
19
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
var __dirname = path.dirname(__filename);
|
|
21
|
+
function getUtilityRegistry() {
|
|
22
|
+
return {
|
|
23
|
+
cn: {
|
|
24
|
+
name: "utils",
|
|
25
|
+
dependencies: [
|
|
26
|
+
"clsx",
|
|
27
|
+
"tailwind-merge"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
variants: {
|
|
31
|
+
name: "variants",
|
|
32
|
+
dependencies: [
|
|
33
|
+
"class-variance-authority"
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function getComponentRegistry() {
|
|
39
|
+
return {
|
|
40
|
+
button: {
|
|
41
|
+
name: "button",
|
|
42
|
+
description: "A customizable button component with multiple variants",
|
|
43
|
+
files: [
|
|
44
|
+
{ name: "button.tsx" },
|
|
45
|
+
{ name: "index.ts" }
|
|
46
|
+
],
|
|
47
|
+
dependencies: [
|
|
48
|
+
"@base-ui-components/react",
|
|
49
|
+
"class-variance-authority"
|
|
50
|
+
],
|
|
51
|
+
componentDependencies: ["core"],
|
|
52
|
+
utilityDependencies: ["cn", "variants"]
|
|
53
|
+
},
|
|
54
|
+
input: {
|
|
55
|
+
name: "input",
|
|
56
|
+
description: "A customizable input field component with variants and sizes",
|
|
57
|
+
files: [
|
|
58
|
+
{ name: "input.tsx" },
|
|
59
|
+
{ name: "index.ts" }
|
|
60
|
+
],
|
|
61
|
+
dependencies: [
|
|
62
|
+
"@base-ui-components/react"
|
|
63
|
+
],
|
|
64
|
+
utilityDependencies: ["cn"]
|
|
65
|
+
},
|
|
66
|
+
field: {
|
|
67
|
+
name: "field",
|
|
68
|
+
description: "A component for building accessible forms with custom styling and validation.",
|
|
69
|
+
files: [
|
|
70
|
+
{ name: "field.tsx" },
|
|
71
|
+
{ name: "index.ts" }
|
|
72
|
+
],
|
|
73
|
+
dependencies: [
|
|
74
|
+
"@base-ui-components/react"
|
|
75
|
+
],
|
|
76
|
+
utilityDependencies: ["cn"]
|
|
77
|
+
},
|
|
78
|
+
form: {
|
|
79
|
+
name: "form",
|
|
80
|
+
description: "A native form element with consolidated error handling, built on Base UI foundation.",
|
|
81
|
+
files: [
|
|
82
|
+
{ name: "form.tsx" },
|
|
83
|
+
{ name: "index.ts" }
|
|
84
|
+
],
|
|
85
|
+
dependencies: [
|
|
86
|
+
"@base-ui-components/react"
|
|
87
|
+
],
|
|
88
|
+
utilityDependencies: ["cn"]
|
|
89
|
+
},
|
|
90
|
+
"alert-dialog": {
|
|
91
|
+
name: "alert-dialog",
|
|
92
|
+
description: "A modal dialog that interrupts the user with important content and expects a response.",
|
|
93
|
+
files: [
|
|
94
|
+
{ name: "alert-dialog.tsx" },
|
|
95
|
+
{ name: "index.ts" }
|
|
96
|
+
],
|
|
97
|
+
dependencies: [
|
|
98
|
+
"@base-ui-components/react/alert-dialog",
|
|
99
|
+
"lucide-react"
|
|
100
|
+
],
|
|
101
|
+
utilityDependencies: ["cn"]
|
|
102
|
+
},
|
|
103
|
+
accordion: {
|
|
104
|
+
name: "accordion",
|
|
105
|
+
description: "A vertically stacked set of interactive headings that each reveal a section of content.",
|
|
106
|
+
files: [
|
|
107
|
+
{ name: "accordion.tsx" },
|
|
108
|
+
{ name: "index.ts" }
|
|
109
|
+
],
|
|
110
|
+
dependencies: [
|
|
111
|
+
"@base-ui-components/react",
|
|
112
|
+
"lucide-react",
|
|
113
|
+
"tailwindcss-animate"
|
|
114
|
+
],
|
|
115
|
+
utilityDependencies: ["cn"]
|
|
116
|
+
},
|
|
117
|
+
tabs: {
|
|
118
|
+
name: "tabs",
|
|
119
|
+
description: "A component for toggling between related panels on the same page.",
|
|
120
|
+
files: [
|
|
121
|
+
{ name: "tabs.tsx" },
|
|
122
|
+
{ name: "index.ts" }
|
|
123
|
+
],
|
|
124
|
+
dependencies: [
|
|
125
|
+
"@base-ui-components/react"
|
|
126
|
+
],
|
|
127
|
+
utilityDependencies: ["cn"]
|
|
128
|
+
},
|
|
129
|
+
slider: {
|
|
130
|
+
name: "slider",
|
|
131
|
+
description: "An input where the user selects a value from within a given range.",
|
|
132
|
+
files: [
|
|
133
|
+
{ name: "slider.tsx" },
|
|
134
|
+
{ name: "index.ts" }
|
|
135
|
+
],
|
|
136
|
+
dependencies: [
|
|
137
|
+
"@base-ui-components/react"
|
|
138
|
+
],
|
|
139
|
+
utilityDependencies: ["cn"]
|
|
140
|
+
},
|
|
141
|
+
avatar: {
|
|
142
|
+
name: "avatar",
|
|
143
|
+
description: "An image element with a fallback for representing a user.",
|
|
144
|
+
files: [
|
|
145
|
+
{ name: "avatar.tsx" },
|
|
146
|
+
{ name: "index.ts" }
|
|
147
|
+
],
|
|
148
|
+
dependencies: [
|
|
149
|
+
"@base-ui-components/react",
|
|
150
|
+
"class-variance-authority"
|
|
151
|
+
],
|
|
152
|
+
utilityDependencies: ["cn"]
|
|
153
|
+
},
|
|
154
|
+
checkbox: {
|
|
155
|
+
name: "checkbox",
|
|
156
|
+
description: "A control that allows the user to select one or more options from a set.",
|
|
157
|
+
files: [
|
|
158
|
+
{ name: "checkbox.tsx" },
|
|
159
|
+
{ name: "index.ts" }
|
|
160
|
+
],
|
|
161
|
+
dependencies: [
|
|
162
|
+
"@base-ui-components/react",
|
|
163
|
+
"lucide-react"
|
|
164
|
+
],
|
|
165
|
+
utilityDependencies: ["cn"]
|
|
166
|
+
},
|
|
167
|
+
"checkbox-group": {
|
|
168
|
+
name: "checkbox-group",
|
|
169
|
+
description: "A group of checkboxes that share a common state.",
|
|
170
|
+
files: [
|
|
171
|
+
{ name: "checkbox-group.tsx" },
|
|
172
|
+
{ name: "index.ts" }
|
|
173
|
+
],
|
|
174
|
+
dependencies: [
|
|
175
|
+
"@base-ui-components/react"
|
|
176
|
+
],
|
|
177
|
+
componentDependencies: ["checkbox"],
|
|
178
|
+
utilityDependencies: ["cn"]
|
|
179
|
+
},
|
|
180
|
+
"collapsible": {
|
|
181
|
+
name: "collapsible",
|
|
182
|
+
description: "A collapsible panel controlled by a button.",
|
|
183
|
+
files: [
|
|
184
|
+
{ name: "collapsible.tsx" },
|
|
185
|
+
{ name: "index.ts" }
|
|
186
|
+
],
|
|
187
|
+
dependencies: [
|
|
188
|
+
"@base-ui-components/react",
|
|
189
|
+
"tailwindcss-animate"
|
|
190
|
+
],
|
|
191
|
+
utilityDependencies: ["cn"]
|
|
192
|
+
},
|
|
193
|
+
dialog: {
|
|
194
|
+
name: "dialog",
|
|
195
|
+
description: "A popup that opens on top of the entire page, providing a modal interface for user interactions.",
|
|
196
|
+
files: [
|
|
197
|
+
{ name: "dialog.tsx" },
|
|
198
|
+
{ name: "index.ts" }
|
|
199
|
+
],
|
|
200
|
+
dependencies: [
|
|
201
|
+
"@base-ui-components/react"
|
|
202
|
+
],
|
|
203
|
+
utilityDependencies: ["cn"]
|
|
204
|
+
},
|
|
205
|
+
toast: {
|
|
206
|
+
name: "toast",
|
|
207
|
+
description: "Generates toast notifications with support for different types, promises, actions, and global management.",
|
|
208
|
+
files: [
|
|
209
|
+
{ name: "toast.tsx" },
|
|
210
|
+
{ name: "index.ts" }
|
|
211
|
+
],
|
|
212
|
+
dependencies: [
|
|
213
|
+
"@base-ui-components/react",
|
|
214
|
+
"class-variance-authority"
|
|
215
|
+
],
|
|
216
|
+
utilityDependencies: ["cn"]
|
|
217
|
+
},
|
|
218
|
+
select: {
|
|
219
|
+
name: "select",
|
|
220
|
+
description: "A common form component for choosing a predefined value in a dropdown menu.",
|
|
221
|
+
files: [
|
|
222
|
+
{ name: "select.tsx" },
|
|
223
|
+
{ name: "index.ts" }
|
|
224
|
+
],
|
|
225
|
+
dependencies: [
|
|
226
|
+
"@base-ui-components/react",
|
|
227
|
+
"lucide-react"
|
|
228
|
+
],
|
|
229
|
+
utilityDependencies: ["cn"]
|
|
230
|
+
},
|
|
231
|
+
"context-menu": {
|
|
232
|
+
name: "context-menu",
|
|
233
|
+
description: "A menu that appears at the pointer on right click or long press.",
|
|
234
|
+
files: [
|
|
235
|
+
{ name: "context-menu.tsx" },
|
|
236
|
+
{ name: "index.ts" }
|
|
237
|
+
],
|
|
238
|
+
dependencies: [
|
|
239
|
+
"@base-ui-components/react",
|
|
240
|
+
"lucide-react"
|
|
241
|
+
],
|
|
242
|
+
utilityDependencies: ["cn"]
|
|
243
|
+
},
|
|
244
|
+
menubar: {
|
|
245
|
+
name: "menubar",
|
|
246
|
+
description: "A visually persistent menu common in desktop applications that provides access to a consistent set of commands.",
|
|
247
|
+
files: [
|
|
248
|
+
{ name: "menubar.tsx" },
|
|
249
|
+
{ name: "index.ts" }
|
|
250
|
+
],
|
|
251
|
+
dependencies: [
|
|
252
|
+
"@base-ui-components/react",
|
|
253
|
+
"lucide-react"
|
|
254
|
+
],
|
|
255
|
+
utilityDependencies: ["cn"]
|
|
256
|
+
},
|
|
257
|
+
"navigation-menu": {
|
|
258
|
+
name: "navigation-menu",
|
|
259
|
+
description: "A collection of links and menus for website navigation.",
|
|
260
|
+
files: [
|
|
261
|
+
{ name: "navigation-menu.tsx" },
|
|
262
|
+
{ name: "index.ts" }
|
|
263
|
+
],
|
|
264
|
+
dependencies: [
|
|
265
|
+
"@base-ui-components/react",
|
|
266
|
+
"lucide-react"
|
|
267
|
+
],
|
|
268
|
+
utilityDependencies: ["cn"]
|
|
269
|
+
},
|
|
270
|
+
"preview-card": {
|
|
271
|
+
name: "preview-card",
|
|
272
|
+
description: "A popup that appears when a link is hovered, showing a preview for sighted users.",
|
|
273
|
+
files: [
|
|
274
|
+
{ name: "preview-card.tsx" },
|
|
275
|
+
{ name: "index.ts" }
|
|
276
|
+
],
|
|
277
|
+
dependencies: [
|
|
278
|
+
"@base-ui-components/react"
|
|
279
|
+
],
|
|
280
|
+
utilityDependencies: ["cn"]
|
|
281
|
+
},
|
|
282
|
+
toggle: {
|
|
283
|
+
name: "toggle",
|
|
284
|
+
description: "A two-state button that can be on or off.",
|
|
285
|
+
files: [
|
|
286
|
+
{ name: "toggle.tsx" },
|
|
287
|
+
{ name: "index.ts" }
|
|
288
|
+
],
|
|
289
|
+
dependencies: [
|
|
290
|
+
"@base-ui-components/react",
|
|
291
|
+
"class-variance-authority"
|
|
292
|
+
],
|
|
293
|
+
utilityDependencies: ["cn"]
|
|
294
|
+
},
|
|
295
|
+
"toolbar": {
|
|
296
|
+
"name": "toolbar",
|
|
297
|
+
"description": "A container for grouping a set of controls, such as buttons, toggle groups, or dropdown menus.",
|
|
298
|
+
"files": [
|
|
299
|
+
{
|
|
300
|
+
"name": "toolbar.tsx"
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
"name": "index.ts"
|
|
304
|
+
}
|
|
305
|
+
],
|
|
306
|
+
"dependencies": [
|
|
307
|
+
"@base-ui-components/react"
|
|
308
|
+
],
|
|
309
|
+
"utilityDependencies": [
|
|
310
|
+
"cn"
|
|
311
|
+
]
|
|
312
|
+
},
|
|
313
|
+
"tooltip": {
|
|
314
|
+
"name": "tooltip",
|
|
315
|
+
"description": "A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.",
|
|
316
|
+
"files": [
|
|
317
|
+
{
|
|
318
|
+
"name": "tooltip.tsx"
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"name": "index.ts"
|
|
322
|
+
}
|
|
323
|
+
],
|
|
324
|
+
"dependencies": [
|
|
325
|
+
"@base-ui-components/react"
|
|
326
|
+
],
|
|
327
|
+
"utilityDependencies": [
|
|
328
|
+
"cn"
|
|
329
|
+
]
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
async function getConfig() {
|
|
334
|
+
const configPath = path.join(process.cwd(), "components.json");
|
|
335
|
+
if (!fs.existsSync(configPath)) {
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
try {
|
|
339
|
+
const configContent = await fs.readFile(configPath, "utf-8");
|
|
340
|
+
return JSON.parse(configContent);
|
|
341
|
+
} catch (error) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/commands/add.ts
|
|
347
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
348
|
+
var __dirname2 = path2.dirname(__filename2);
|
|
349
|
+
function getComponentDependencies(componentName) {
|
|
350
|
+
const registry = getComponentRegistry();
|
|
351
|
+
const component = registry[componentName];
|
|
352
|
+
if (!component) return [];
|
|
353
|
+
let dependencies = component.componentDependencies || [];
|
|
354
|
+
for (const dep of component.componentDependencies || []) {
|
|
355
|
+
dependencies = [...dependencies, ...getComponentDependencies(dep)];
|
|
356
|
+
}
|
|
357
|
+
return [...new Set(dependencies)];
|
|
358
|
+
}
|
|
359
|
+
async function ensureTailwindConfig(deps) {
|
|
360
|
+
const needsAnimatePlugin = deps.includes("tailwindcss-animate");
|
|
361
|
+
if (!needsAnimatePlugin) return;
|
|
362
|
+
const configPath = path2.join(process.cwd(), "tailwind.config.js");
|
|
363
|
+
if (!fs2.existsSync(configPath)) {
|
|
364
|
+
const configContent = `/** @type {import('tailwindcss').Config} */
|
|
365
|
+
export default {
|
|
366
|
+
content: [
|
|
367
|
+
"./src/**/*.{js,ts,jsx,tsx}",
|
|
368
|
+
"./components/**/*.{js,ts,jsx,tsx}",
|
|
369
|
+
"./pages/**/*.{js,ts,jsx,tsx}",
|
|
370
|
+
],
|
|
371
|
+
theme: {
|
|
372
|
+
extend: {},
|
|
373
|
+
},
|
|
374
|
+
plugins: [
|
|
375
|
+
require("tailwindcss-animate"),
|
|
376
|
+
],
|
|
377
|
+
};`;
|
|
378
|
+
await fs2.writeFile(configPath, configContent);
|
|
379
|
+
return { created: true };
|
|
380
|
+
} else {
|
|
381
|
+
const configContent = await fs2.readFile(configPath, "utf-8");
|
|
382
|
+
if (!configContent.includes("tailwindcss-animate")) {
|
|
383
|
+
let updatedContent = configContent;
|
|
384
|
+
if (configContent.includes("plugins:")) {
|
|
385
|
+
updatedContent = configContent.replace(
|
|
386
|
+
/plugins:\s*\[([\s\S]*?)\]/,
|
|
387
|
+
(match, pluginsContent) => {
|
|
388
|
+
const cleanPlugins = pluginsContent.trim();
|
|
389
|
+
const newPlugin = 'require("tailwindcss-animate")';
|
|
390
|
+
if (cleanPlugins === "") {
|
|
391
|
+
return `plugins: [
|
|
392
|
+
${newPlugin},
|
|
393
|
+
]`;
|
|
394
|
+
} else {
|
|
395
|
+
return `plugins: [
|
|
396
|
+
${pluginsContent},
|
|
397
|
+
${newPlugin},
|
|
398
|
+
]`;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
);
|
|
402
|
+
} else {
|
|
403
|
+
updatedContent = configContent.replace(
|
|
404
|
+
/}\s*;?\s*$/,
|
|
405
|
+
' plugins: [\n require("tailwindcss-animate"),\n ],\n};'
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
await fs2.writeFile(configPath, updatedContent);
|
|
409
|
+
return { updated: true };
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return { exists: true };
|
|
413
|
+
}
|
|
414
|
+
var addCommand = new Command("add").description("Add a component to your project").argument("<component>", "Name of the component").option("-y, --yes", "Skip confirmation prompts").option("-o, --overwrite", "Overwrite existing files").action(async (componentName, options) => {
|
|
415
|
+
const spinner = ora("Adding component...").start();
|
|
416
|
+
try {
|
|
417
|
+
const config = await getConfig();
|
|
418
|
+
if (!config) {
|
|
419
|
+
spinner.fail("\u274C No components.json found. Run `dinachi init` first.");
|
|
420
|
+
process.exit(1);
|
|
421
|
+
}
|
|
422
|
+
const registry = getComponentRegistry();
|
|
423
|
+
const component = registry[componentName];
|
|
424
|
+
if (!component) {
|
|
425
|
+
spinner.fail(`\u274C Component "${componentName}" not found.`);
|
|
426
|
+
console.log("Available components:");
|
|
427
|
+
Object.keys(registry).forEach((name) => {
|
|
428
|
+
console.log(` ${chalk.cyan(name)}`);
|
|
429
|
+
});
|
|
430
|
+
process.exit(1);
|
|
431
|
+
}
|
|
432
|
+
const componentsToInstall = [componentName, ...getComponentDependencies(componentName)];
|
|
433
|
+
spinner.text = `Installing ${componentsToInstall.join(", ")}...`;
|
|
434
|
+
const componentDir = path2.join(process.cwd(), config.aliases.components);
|
|
435
|
+
await fs2.ensureDir(componentDir);
|
|
436
|
+
let allFilesAdded = [];
|
|
437
|
+
let allDepsInstalled = [];
|
|
438
|
+
let allUtilityDeps = [];
|
|
439
|
+
for (const name of componentsToInstall) {
|
|
440
|
+
const comp = registry[name];
|
|
441
|
+
if (!comp) continue;
|
|
442
|
+
if (comp.utilityDependencies?.length) {
|
|
443
|
+
allUtilityDeps.push(...comp.utilityDependencies);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const utilityRegistry = getUtilityRegistry();
|
|
447
|
+
const uniqueUtilityDeps = [...new Set(allUtilityDeps)];
|
|
448
|
+
const utilsDir = path2.join(process.cwd(), config.aliases.utils);
|
|
449
|
+
if (uniqueUtilityDeps.length > 0) {
|
|
450
|
+
await fs2.ensureDir(utilsDir);
|
|
451
|
+
for (const utilityName of uniqueUtilityDeps) {
|
|
452
|
+
const utility = utilityRegistry[utilityName];
|
|
453
|
+
if (!utility) continue;
|
|
454
|
+
const utilityFilename = `${utility.name}.ts`;
|
|
455
|
+
const sourcePath = path2.join(__dirname2, "../templates/utils", utilityFilename);
|
|
456
|
+
const targetPath = path2.join(utilsDir, utilityFilename);
|
|
457
|
+
if (!fs2.existsSync(targetPath)) {
|
|
458
|
+
let content = await fs2.readFile(sourcePath, "utf-8");
|
|
459
|
+
content = content.replace(/^\/\/ @ts-nocheck\s*\n/m, "");
|
|
460
|
+
await fs2.writeFile(targetPath, content);
|
|
461
|
+
allFilesAdded.push({
|
|
462
|
+
name: utilityFilename,
|
|
463
|
+
path: path2.join(config.aliases.utils, utilityFilename)
|
|
464
|
+
});
|
|
465
|
+
if (utility.dependencies?.length) {
|
|
466
|
+
allDepsInstalled.push(...utility.dependencies);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
for (const name of componentsToInstall) {
|
|
472
|
+
const comp = registry[name];
|
|
473
|
+
if (!comp) continue;
|
|
474
|
+
for (const file of comp.files) {
|
|
475
|
+
const sourcePath = path2.join(__dirname2, "../templates", name, file.name);
|
|
476
|
+
const targetPath = path2.join(componentDir, file.name);
|
|
477
|
+
if (fs2.existsSync(targetPath) && !options.overwrite) {
|
|
478
|
+
spinner.warn(`\u26A0\uFE0F ${file.name} already exists. Use --overwrite to replace it.`);
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
let content = await fs2.readFile(sourcePath, "utf-8");
|
|
482
|
+
content = content.replace(/^\/\/ @ts-nocheck\s*\n/m, "");
|
|
483
|
+
await fs2.writeFile(targetPath, content);
|
|
484
|
+
allFilesAdded.push({ name: file.name, path: path2.join(config.aliases.components, file.name) });
|
|
485
|
+
}
|
|
486
|
+
if (comp.dependencies?.length) {
|
|
487
|
+
allDepsInstalled.push(...comp.dependencies);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
let tailwindConfigInfo = null;
|
|
491
|
+
if (allDepsInstalled.includes("tailwindcss-animate")) {
|
|
492
|
+
spinner.text = "Updating Tailwind configuration...";
|
|
493
|
+
tailwindConfigInfo = await ensureTailwindConfig(allDepsInstalled);
|
|
494
|
+
}
|
|
495
|
+
if (allDepsInstalled.length > 0) {
|
|
496
|
+
spinner.text = "Installing dependencies...";
|
|
497
|
+
const packageJsonPath = path2.join(process.cwd(), "package.json");
|
|
498
|
+
const packageJson = JSON.parse(await fs2.readFile(packageJsonPath, "utf-8"));
|
|
499
|
+
const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
500
|
+
const missingDeps = [...new Set(allDepsInstalled)].filter((dep) => !allDeps[dep]);
|
|
501
|
+
if (missingDeps.length > 0) {
|
|
502
|
+
try {
|
|
503
|
+
const packageManager = getPackageManager();
|
|
504
|
+
const installCmd = `${packageManager} ${packageManager === "npm" ? "install" : "add"} ${missingDeps.join(" ")}`;
|
|
505
|
+
execSync(installCmd, { stdio: "inherit" });
|
|
506
|
+
} catch (error) {
|
|
507
|
+
spinner.warn(`\u26A0\uFE0F Failed to install dependencies. Please install manually: ${missingDeps.join(" ")}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
spinner.succeed(`\u2705 Added ${componentsToInstall.join(", ")} component(s)!`);
|
|
512
|
+
console.log();
|
|
513
|
+
console.log("Files added:");
|
|
514
|
+
allFilesAdded.forEach((file) => {
|
|
515
|
+
console.log(` ${chalk.green("+")} ${file.path}`);
|
|
516
|
+
});
|
|
517
|
+
if (tailwindConfigInfo) {
|
|
518
|
+
console.log();
|
|
519
|
+
if (tailwindConfigInfo.created) {
|
|
520
|
+
console.log(` ${chalk.green("+")} tailwind.config.js (created with tailwindcss-animate plugin)`);
|
|
521
|
+
} else if (tailwindConfigInfo.updated) {
|
|
522
|
+
console.log(` ${chalk.blue("~")} tailwind.config.js (updated with tailwindcss-animate plugin)`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
if (allDepsInstalled.length > 0) {
|
|
526
|
+
console.log();
|
|
527
|
+
console.log("Dependencies installed:");
|
|
528
|
+
[...new Set(allDepsInstalled)].forEach((dep) => {
|
|
529
|
+
console.log(` ${chalk.green("\u2713")} ${dep}`);
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
} catch (error) {
|
|
533
|
+
spinner.fail(`\u274C Failed to add component: ${error instanceof Error ? error.message : error}`);
|
|
534
|
+
process.exit(1);
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
function getPackageManager() {
|
|
538
|
+
if (fs2.existsSync("pnpm-lock.yaml")) return "pnpm";
|
|
539
|
+
if (fs2.existsSync("yarn.lock")) return "yarn";
|
|
540
|
+
return "npm";
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// src/commands/init.ts
|
|
544
|
+
import { Command as Command2 } from "commander";
|
|
545
|
+
import { execSync as execSync2 } from "child_process";
|
|
546
|
+
import fs3 from "fs-extra";
|
|
547
|
+
import path3 from "path";
|
|
548
|
+
import prompts from "prompts";
|
|
549
|
+
import chalk2 from "chalk";
|
|
550
|
+
import ora2 from "ora";
|
|
551
|
+
var initCommand = new Command2("init").description("Initialize Dinachi UI in your project").action(async () => {
|
|
552
|
+
console.log(chalk2.bold.cyan("\u{1F3A8} Welcome to Dinachi UI!"));
|
|
553
|
+
console.log();
|
|
554
|
+
const packageJsonPath = path3.join(process.cwd(), "package.json");
|
|
555
|
+
if (!fs3.existsSync(packageJsonPath)) {
|
|
556
|
+
console.log(chalk2.red("\u274C No package.json found. Please run this command in a valid project."));
|
|
557
|
+
process.exit(1);
|
|
558
|
+
}
|
|
559
|
+
const response = await prompts([
|
|
560
|
+
{
|
|
561
|
+
type: "text",
|
|
562
|
+
name: "componentsPath",
|
|
563
|
+
message: "Where would you like to install components?",
|
|
564
|
+
initial: "./src/components/ui"
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
type: "text",
|
|
568
|
+
name: "utilsPath",
|
|
569
|
+
message: "Where would you like to install utilities?",
|
|
570
|
+
initial: "./src/lib/utils.ts"
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
type: "confirm",
|
|
574
|
+
name: "installDeps",
|
|
575
|
+
message: "Install required dependencies?",
|
|
576
|
+
initial: true
|
|
577
|
+
}
|
|
578
|
+
]);
|
|
579
|
+
if (!response.componentsPath || !response.utilsPath) {
|
|
580
|
+
console.log(chalk2.red("\u274C Setup cancelled."));
|
|
581
|
+
process.exit(1);
|
|
582
|
+
}
|
|
583
|
+
const spinner = ora2("Setting up Dinachi UI...").start();
|
|
584
|
+
try {
|
|
585
|
+
await fs3.ensureDir(path3.dirname(response.componentsPath));
|
|
586
|
+
await fs3.ensureDir(path3.dirname(response.utilsPath));
|
|
587
|
+
const utilsContent = `import { type ClassValue, clsx } from "clsx"
|
|
588
|
+
import { twMerge } from "tailwind-merge"
|
|
589
|
+
|
|
590
|
+
export function cn(...inputs: ClassValue[]) {
|
|
591
|
+
return twMerge(clsx(inputs))
|
|
592
|
+
}
|
|
593
|
+
`;
|
|
594
|
+
await fs3.writeFile(response.utilsPath, utilsContent);
|
|
595
|
+
if (response.installDeps) {
|
|
596
|
+
spinner.text = "Installing dependencies...";
|
|
597
|
+
const deps = [
|
|
598
|
+
"class-variance-authority",
|
|
599
|
+
"clsx",
|
|
600
|
+
"tailwind-merge"
|
|
601
|
+
];
|
|
602
|
+
const packageManager = getPackageManager2();
|
|
603
|
+
const installCmd = `${packageManager} ${packageManager === "npm" ? "install" : "add"} ${deps.join(" ")}`;
|
|
604
|
+
execSync2(installCmd, { stdio: "inherit" });
|
|
605
|
+
}
|
|
606
|
+
const configContent = `{
|
|
607
|
+
"style": "default",
|
|
608
|
+
"rsc": false,
|
|
609
|
+
"tsx": true,
|
|
610
|
+
"tailwind": {
|
|
611
|
+
"config": "tailwind.config.js",
|
|
612
|
+
"css": "src/index.css",
|
|
613
|
+
"baseColor": "slate",
|
|
614
|
+
"cssVariables": true
|
|
615
|
+
},
|
|
616
|
+
"aliases": {
|
|
617
|
+
"components": "${response.componentsPath}",
|
|
618
|
+
"utils": "${response.utilsPath.replace(".ts", "")}"
|
|
619
|
+
}
|
|
620
|
+
}`;
|
|
621
|
+
await fs3.writeFile("components.json", configContent);
|
|
622
|
+
spinner.succeed("\u2705 Dinachi UI setup complete!");
|
|
623
|
+
console.log();
|
|
624
|
+
console.log("Next steps:");
|
|
625
|
+
console.log(` 1. Add a component: ${chalk2.cyan("npx dinachi add button")}`);
|
|
626
|
+
console.log(` 2. Components will be installed to: ${chalk2.cyan(response.componentsPath)}`);
|
|
627
|
+
console.log(` 3. Utils available at: ${chalk2.cyan(response.utilsPath)}`);
|
|
628
|
+
} catch (error) {
|
|
629
|
+
spinner.fail(`\u274C Setup failed: ${error.message}`);
|
|
630
|
+
process.exit(1);
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
function getPackageManager2() {
|
|
634
|
+
if (fs3.existsSync("pnpm-lock.yaml")) return "pnpm";
|
|
635
|
+
if (fs3.existsSync("yarn.lock")) return "yarn";
|
|
636
|
+
return "npm";
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// src/index.ts
|
|
640
|
+
var program = new Command3();
|
|
641
|
+
program.name("dinachi").description("Add Dinachi UI components to your project").version("0.1.0");
|
|
642
|
+
program.addCommand(addCommand).addCommand(initCommand);
|
|
643
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dinachi/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for adding Dinachi UI components to your project",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"ui",
|
|
8
|
+
"components",
|
|
9
|
+
"cli",
|
|
10
|
+
"dinachi",
|
|
11
|
+
"react",
|
|
12
|
+
"tailwind"
|
|
13
|
+
],
|
|
14
|
+
"author": "Dinachi Team",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/dinachi/ui"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://dinachi.dev",
|
|
21
|
+
"bin": {
|
|
22
|
+
"dinachi": "./dist/index.js",
|
|
23
|
+
"dinachi-ui": "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"templates"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"commander": "^11.1.0",
|
|
31
|
+
"chalk": "^5.3.0",
|
|
32
|
+
"ora": "^7.0.1",
|
|
33
|
+
"prompts": "^2.4.2",
|
|
34
|
+
"fs-extra": "^11.2.0",
|
|
35
|
+
"node-fetch": "^3.3.2",
|
|
36
|
+
"execa": "^8.0.1",
|
|
37
|
+
"ts-morph": "^20.0.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/fs-extra": "^11.0.4",
|
|
41
|
+
"@types/prompts": "^2.4.9",
|
|
42
|
+
"tsup": "^8.0.1",
|
|
43
|
+
"typescript": "^5.5.3"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"typescript": ">=4.5.0"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
53
|
+
"dev": "tsup src/index.ts --format esm --dts --watch",
|
|
54
|
+
"type-check": "tsc --noEmit"
|
|
55
|
+
}
|
|
56
|
+
}
|