@hemia/lume 0.0.1

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 ADDED
@@ -0,0 +1,109 @@
1
+ # @hemia/lume
2
+
3
+ CLI for adding Lume UI components to your project. Inspired by shadcn/ui.
4
+
5
+ ## Quick start
6
+
7
+ ```bash
8
+ bunx @hemia/lume@latest init
9
+ bunx @hemia/lume@latest add button
10
+ ```
11
+
12
+ ## Commands
13
+
14
+ ### `init`
15
+
16
+ Initialize Lume in your project:
17
+
18
+ ```bash
19
+ bunx @hemia/lume@latest init
20
+
21
+ # With template
22
+ bunx @hemia/lume@latest init -t nuxt
23
+
24
+ # With preset
25
+ bunx @hemia/lume@latest init --preset dashboard
26
+ ```
27
+
28
+ Creates `lume.config.json` and sets up Tailwind configuration.
29
+
30
+ ### `add`
31
+
32
+ Add components to your project:
33
+
34
+ ```bash
35
+ # Single component
36
+ bunx @hemia/lume@latest add button
37
+
38
+ # Multiple components
39
+ bunx @hemia/lume@latest add button card badge
40
+
41
+ # All components
42
+ bunx @hemia/lume@latest add --all
43
+
44
+ # Skip confirmation
45
+ bunx @hemia/lume@latest add button -y
46
+ ```
47
+
48
+ Components are **copied to your project** (not installed from npm).
49
+
50
+ ### `list`
51
+
52
+ List available components:
53
+
54
+ ```bash
55
+ bunx @hemia/lume@latest list
56
+
57
+ # For specific framework
58
+ bunx @hemia/lume@latest list --framework react
59
+ ```
60
+
61
+ ## How it works
62
+
63
+ 1. Components are stored in `@hemia/lume-registry`
64
+ 2. CLI detects your framework from `lume.config.json`
65
+ 3. Copies component files to `src/components/ui/`
66
+ 4. Installs required dependencies automatically
67
+
68
+ ## Framework support
69
+
70
+ - ✅ **Vue 3** — Full support
71
+ - 🚧 **React** — Coming soon
72
+ - 🚧 **Svelte** — Coming soon
73
+ - 🚧 **Astro** — Coming soon
74
+
75
+ ## Configuration
76
+
77
+ The `lume.config.json` file:
78
+
79
+ ```json
80
+ {
81
+ "framework": "vue",
82
+ "style": "default",
83
+ "template": "vite-vue",
84
+ "tailwind": {
85
+ "config": "tailwind.config.ts",
86
+ "css": "src/assets/globals.css"
87
+ },
88
+ "aliases": {
89
+ "components": "@/components",
90
+ "utils": "@/lib/utils"
91
+ }
92
+ }
93
+ ```
94
+
95
+ ## Templates
96
+
97
+ - `vite-vue` — Vite + Vue 3
98
+ - `nuxt` — Nuxt 3
99
+ - `next` — Next.js (coming soon)
100
+
101
+ ## Presets
102
+
103
+ - `dashboard` — Button, Card, Input, Select, Table, Chart
104
+ - `auth` — Button, Input, Card, Form
105
+ - `landing` — Button, Card, Hero, Features, CTA
106
+
107
+ ## License
108
+
109
+ MIT
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,603 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+
6
+ // src/commands/add.ts
7
+ import path3 from "path";
8
+ import fs3 from "fs-extra";
9
+ import pc2 from "picocolors";
10
+ import prompts from "prompts";
11
+ import { createRequire } from "module";
12
+
13
+ // src/utils/package-manager.ts
14
+ import fs from "fs-extra";
15
+ import path from "path";
16
+ import { execSync } from "child_process";
17
+ import pc from "picocolors";
18
+ function detectPackageManager(cwd = process.cwd()) {
19
+ if (fs.existsSync(path.join(cwd, "bun.lockb"))) return "bun";
20
+ if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
21
+ if (fs.existsSync(path.join(cwd, "yarn.lock"))) return "yarn";
22
+ return "npm";
23
+ }
24
+ function getInstallCommand(packageManager) {
25
+ const commands = {
26
+ bun: "bun add",
27
+ pnpm: "pnpm add",
28
+ yarn: "yarn add",
29
+ npm: "npm install"
30
+ };
31
+ return commands[packageManager];
32
+ }
33
+ function installDependencies(dependencies, options = {}) {
34
+ if (dependencies.length === 0) return;
35
+ const { dev = false, cwd = process.cwd() } = options;
36
+ const packageManager = detectPackageManager(cwd);
37
+ const installCmd = getInstallCommand(packageManager);
38
+ const devFlag = dev ? packageManager === "npm" ? "--save-dev" : "-D" : "";
39
+ const command = `${installCmd} ${dependencies.join(" ")} ${devFlag}`.trim();
40
+ console.log(pc.cyan(`\u{1F4E6} Installing dependencies with ${packageManager}...`));
41
+ console.log(pc.dim(` ${command}`));
42
+ try {
43
+ execSync(command, { cwd, stdio: "inherit" });
44
+ console.log(pc.green("\u2705 Dependencies installed successfully"));
45
+ } catch (error) {
46
+ console.log(pc.yellow("\u26A0\uFE0F Failed to install dependencies. Please install manually:"));
47
+ console.log(pc.dim(` ${command}`));
48
+ }
49
+ }
50
+
51
+ // src/utils/registry.ts
52
+ import path2 from "path";
53
+ import fs2 from "fs-extra";
54
+ async function readComponentMeta(componentPath) {
55
+ const metaPath = path2.join(componentPath, "meta.json");
56
+ if (!await fs2.pathExists(metaPath)) {
57
+ return null;
58
+ }
59
+ try {
60
+ return await fs2.readJson(metaPath);
61
+ } catch {
62
+ return null;
63
+ }
64
+ }
65
+ async function getAllDependencies(componentName, registryPath, visited = /* @__PURE__ */ new Set()) {
66
+ if (visited.has(componentName)) {
67
+ return { components: [], dependencies: [], devDependencies: [] };
68
+ }
69
+ visited.add(componentName);
70
+ const componentPath = path2.join(registryPath, componentName);
71
+ const meta = await readComponentMeta(componentPath);
72
+ if (!meta) {
73
+ return { components: [], dependencies: [], devDependencies: [] };
74
+ }
75
+ const components = [componentName];
76
+ const dependencies = [...meta.dependencies || [], ...meta.peerDependencies || []];
77
+ const devDependencies = [...meta.devDependencies || []];
78
+ if (meta.registryDependencies && meta.registryDependencies.length > 0) {
79
+ for (const depName of meta.registryDependencies) {
80
+ const depResult = await getAllDependencies(depName, registryPath, visited);
81
+ components.push(...depResult.components);
82
+ dependencies.push(...depResult.dependencies);
83
+ devDependencies.push(...depResult.devDependencies);
84
+ }
85
+ }
86
+ return {
87
+ components: [...new Set(components)],
88
+ dependencies: [...new Set(dependencies)],
89
+ devDependencies: [...new Set(devDependencies)]
90
+ };
91
+ }
92
+
93
+ // src/commands/add.ts
94
+ var require2 = createRequire(import.meta.url);
95
+ function resolveRegistryPath(framework) {
96
+ const registryRoot = path3.dirname(
97
+ require2.resolve("@hemia/lume-registry/package.json")
98
+ );
99
+ return path3.join(registryRoot, "registry", framework);
100
+ }
101
+ function getFrameworkFromConfig(cwd) {
102
+ try {
103
+ const config = fs3.readJsonSync(path3.resolve(cwd, "lume.config.json"));
104
+ return config.framework ?? "vue";
105
+ } catch {
106
+ return "vue";
107
+ }
108
+ }
109
+ function log(message, options) {
110
+ if (!options.silent) {
111
+ console.log(message);
112
+ }
113
+ }
114
+ function warn(message, options) {
115
+ if (!options.silent) {
116
+ console.log(pc2.yellow(`\u26A0\uFE0F ${message}`));
117
+ }
118
+ }
119
+ async function add(components = [], options = {}) {
120
+ if (options.dryRun) {
121
+ warn("Dry-run option is not implemented yet. Coming soon!", options);
122
+ }
123
+ if (options.diff) {
124
+ warn("Diff option is not implemented yet. Coming soon!", options);
125
+ }
126
+ if (options.view) {
127
+ warn("View option is not implemented yet. Coming soon!", options);
128
+ }
129
+ const cwd = options.cwd ? path3.resolve(options.cwd) : process.cwd();
130
+ const targetBase = options.path ? path3.resolve(cwd, options.path) : path3.resolve(cwd, "src/components/ui");
131
+ const componentNames = options.all ? ["button", "card", "badge", "alert", "field", "icon", "textfield", "alert-dialog"] : Array.isArray(components) ? components : components ? [components] : [];
132
+ if (componentNames.length === 0 && !options.all) {
133
+ log("No components specified. Use --all to add all components or specify component names.", options);
134
+ process.exit(1);
135
+ }
136
+ const framework = options.framework ?? getFrameworkFromConfig(cwd);
137
+ const frameworkRegistry = resolveRegistryPath(framework);
138
+ if (!options.silent) {
139
+ log(pc2.cyan(`
140
+ \u26A1 Adding component(s) to ${targetBase}/
141
+ `), options);
142
+ }
143
+ const allComponents = /* @__PURE__ */ new Set();
144
+ const allDependencies = /* @__PURE__ */ new Set();
145
+ const allDevDependencies = /* @__PURE__ */ new Set();
146
+ for (const name of componentNames) {
147
+ const componentPath = path3.join(frameworkRegistry, name);
148
+ if (!await fs3.pathExists(componentPath)) {
149
+ log(pc2.red(`\u274C Component "${name}" not found in registry for framework "${framework}"`), options);
150
+ process.exit(1);
151
+ }
152
+ const result = await getAllDependencies(name, frameworkRegistry);
153
+ result.components.forEach((c) => allComponents.add(c));
154
+ result.dependencies.forEach((d) => allDependencies.add(d));
155
+ result.devDependencies.forEach((d) => allDevDependencies.add(d));
156
+ }
157
+ const componentsArray = Array.from(allComponents);
158
+ log(pc2.cyan(`\u{1F4E6} Components to install (${componentsArray.length}):`), options);
159
+ log(pc2.dim(` ${componentsArray.join(", ")}
160
+ `), options);
161
+ await fs3.ensureDir(targetBase);
162
+ let copiedCount = 0;
163
+ for (const componentName of componentsArray) {
164
+ const source = path3.join(frameworkRegistry, componentName);
165
+ const target = path3.join(targetBase, `${componentName}.vue`);
166
+ const targetExists = await fs3.pathExists(target);
167
+ if (targetExists && !options.overwrite && !options.yes) {
168
+ const { overwrite } = await prompts({
169
+ type: "confirm",
170
+ name: "overwrite",
171
+ message: pc2.yellow(`Component "${componentName}" already exists. Overwrite?`),
172
+ initial: false
173
+ });
174
+ if (!overwrite) {
175
+ log(pc2.dim(` Skipped ${componentName}`), options);
176
+ continue;
177
+ }
178
+ }
179
+ if (options.overwrite || options.yes || !targetExists) {
180
+ await fs3.copy(source, target, { overwrite: true });
181
+ copiedCount++;
182
+ log(pc2.green(` \u2713 ${componentName}`), options);
183
+ }
184
+ }
185
+ const deps = Array.from(allDependencies);
186
+ const devDeps = Array.from(allDevDependencies);
187
+ if (deps.length > 0) {
188
+ log("", options);
189
+ installDependencies(deps);
190
+ }
191
+ if (devDeps.length > 0) {
192
+ log("", options);
193
+ installDependencies(devDeps, { dev: true });
194
+ }
195
+ log(pc2.green(`
196
+ \u2705 Added ${copiedCount} component(s) to ${targetBase}/`), options);
197
+ }
198
+
199
+ // src/commands/init.ts
200
+ import fs4 from "fs-extra";
201
+ import path4 from "path";
202
+ import pc3 from "picocolors";
203
+ import prompts2 from "prompts";
204
+
205
+ // src/utils/templates.ts
206
+ var GLOBALS_CSS_TEMPLATE = `@tailwind base;
207
+ @tailwind components;
208
+ @tailwind utilities;
209
+
210
+ @layer base {
211
+ :root {
212
+ /* Color palette using HSL for easy theming */
213
+ --primary: 222.2 47.4% 11.2%;
214
+ --primary-foreground: 210 40% 98%;
215
+
216
+ --secondary: 210 40% 96.1%;
217
+ --secondary-foreground: 222.2 47.4% 11.2%;
218
+
219
+ --destructive: 0 84.2% 60.2%;
220
+ --destructive-foreground: 210 40% 98%;
221
+
222
+ --muted: 210 40% 96.1%;
223
+ --muted-foreground: 215.4 16.3% 46.9%;
224
+
225
+ --accent: 210 40% 96.1%;
226
+ --accent-foreground: 222.2 47.4% 11.2%;
227
+
228
+ --border: 214.3 31.8% 91.4%;
229
+ --input: 214.3 31.8% 91.4%;
230
+ --ring: 210 5% 80% / 30%;
231
+
232
+ --background: 0 0% 100%;
233
+ --foreground: 222.2 47.4% 11.2%;
234
+
235
+ /* Border radius */
236
+ --radius-sm: 0.375rem;
237
+ --radius-md: 0.5rem;
238
+ --radius-lg: 0.75rem;
239
+ --radius: 0.625rem;
240
+ }
241
+
242
+ .dark {
243
+ --primary: 210 40% 98%;
244
+ --primary-foreground: 222.2 47.4% 11.2%;
245
+
246
+ --secondary: 217.2 32.6% 17.5%;
247
+ --secondary-foreground: 210 40% 98%;
248
+
249
+ --destructive: 0 62.8% 30.6%;
250
+ --destructive-foreground: 210 40% 98%;
251
+
252
+ --muted: 217.2 32.6% 17.5%;
253
+ --muted-foreground: 215 20.2% 65.1%;
254
+
255
+ --accent: 217.2 32.6% 17.5%;
256
+ --accent-foreground: 210 40% 98%;
257
+
258
+ --border: 217.2 32.6% 17.5%;
259
+ --input: 217.2 32.6% 17.5%;
260
+ --ring: 217.2 32.6% 50% / 30%;
261
+
262
+ --background: 222.2 84% 4.9%;
263
+ --foreground: 210 40% 98%;
264
+ --radius: 0.625rem;
265
+ }
266
+ }
267
+
268
+ @layer base {
269
+ * {
270
+ @apply border-border;
271
+ }
272
+ body {
273
+ @apply bg-background text-foreground;
274
+ }
275
+ }
276
+ `;
277
+ var TAILWIND_CONFIG_TEMPLATE = `import type { Config } from 'tailwindcss'
278
+
279
+ export default {
280
+ darkMode: ['class'],
281
+ content: [
282
+ './index.html',
283
+ './src/**/*.{vue,js,ts,jsx,tsx}',
284
+ ],
285
+ theme: {
286
+ extend: {
287
+ colors: {
288
+ border: 'hsl(var(--border))',
289
+ input: 'hsl(var(--input))',
290
+ ring: 'hsl(var(--ring))',
291
+ background: 'hsl(var(--background))',
292
+ foreground: 'hsl(var(--foreground))',
293
+ primary: {
294
+ DEFAULT: 'hsl(var(--primary))',
295
+ foreground: 'hsl(var(--primary-foreground))',
296
+ },
297
+ secondary: {
298
+ DEFAULT: 'hsl(var(--secondary))',
299
+ foreground: 'hsl(var(--secondary-foreground))',
300
+ },
301
+ destructive: {
302
+ DEFAULT: 'hsl(var(--destructive))',
303
+ foreground: 'hsl(var(--destructive-foreground))',
304
+ },
305
+ muted: {
306
+ DEFAULT: 'hsl(var(--muted))',
307
+ foreground: 'hsl(var(--muted-foreground))',
308
+ },
309
+ accent: {
310
+ DEFAULT: 'hsl(var(--accent))',
311
+ foreground: 'hsl(var(--accent-foreground))',
312
+ },
313
+ },
314
+ borderRadius: {
315
+ sm: 'var(--radius-sm)',
316
+ md: 'var(--radius-md)',
317
+ lg: 'var(--radius-lg)',
318
+ DEFAULT: 'var(--radius)',
319
+ },
320
+ },
321
+ },
322
+ plugins: [],
323
+ } satisfies Config
324
+ `;
325
+ function getTemplateConfig(framework, template) {
326
+ const configs = {
327
+ "vite-vue": {
328
+ tailwindConfigPath: "tailwind.config.ts",
329
+ globalsCssPath: "src/assets/globals.css",
330
+ componentsPath: "src/components",
331
+ utilsPath: "src/lib/utils"
332
+ },
333
+ nuxt: {
334
+ tailwindConfigPath: "tailwind.config.ts",
335
+ globalsCssPath: "assets/css/globals.css",
336
+ componentsPath: "components",
337
+ utilsPath: "utils"
338
+ },
339
+ next: {
340
+ tailwindConfigPath: "tailwind.config.ts",
341
+ globalsCssPath: "src/app/globals.css",
342
+ componentsPath: "src/components",
343
+ utilsPath: "src/lib/utils"
344
+ }
345
+ };
346
+ const templateKey = template || `vite-${framework}`;
347
+ const config = configs[templateKey] || configs["vite-vue"];
348
+ return {
349
+ framework,
350
+ template: templateKey,
351
+ tailwindConfigPath: config.tailwindConfigPath,
352
+ globalsCssPath: config.globalsCssPath,
353
+ componentsPath: config.componentsPath,
354
+ utilsPath: config.utilsPath
355
+ };
356
+ }
357
+
358
+ // src/commands/init.ts
359
+ var SUPPORTED_FRAMEWORKS = ["vue", "react", "svelte", "astro"];
360
+ function detectFramework() {
361
+ try {
362
+ const pkg = fs4.readJsonSync(path4.resolve(process.cwd(), "package.json"));
363
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
364
+ if (deps["vue"]) return "vue";
365
+ if (deps["react"]) return "react";
366
+ if (deps["svelte"]) return "svelte";
367
+ if (deps["astro"]) return "astro";
368
+ return null;
369
+ } catch {
370
+ return null;
371
+ }
372
+ }
373
+ function log2(message, options) {
374
+ if (!options.silent) {
375
+ console.log(message);
376
+ }
377
+ }
378
+ function warn2(message, options) {
379
+ if (!options.silent) {
380
+ console.log(pc3.yellow(`\u26A0\uFE0F ${message}`));
381
+ }
382
+ }
383
+ function info(message, options) {
384
+ if (!options.silent) {
385
+ console.log(pc3.cyan(message));
386
+ }
387
+ }
388
+ async function init(options = {}) {
389
+ if (options.preset) {
390
+ warn2("Preset option is not implemented yet. Coming soon!", options);
391
+ }
392
+ if (options.defaults) {
393
+ warn2("Defaults option is not implemented yet. Coming soon!", options);
394
+ }
395
+ if (options.monorepo) {
396
+ warn2("Monorepo option is not implemented yet. Coming soon!", options);
397
+ }
398
+ if (options.reinstall) {
399
+ warn2("Reinstall option is not implemented yet. Coming soon!", options);
400
+ }
401
+ if (options.monorepo === false) {
402
+ }
403
+ if (options.reinstall === false) {
404
+ }
405
+ const cwd = options.cwd ? path4.resolve(options.cwd) : process.cwd();
406
+ const validTemplates = ["next", "vite", "start", "react-router", "laravel", "astro"];
407
+ let template = options.template?.toLowerCase();
408
+ if (template && !validTemplates.includes(template)) {
409
+ warn2(`Invalid template "${template}". Using default.`, options);
410
+ template = void 0;
411
+ }
412
+ const configPath = path4.resolve(cwd, "lume.config.json");
413
+ const configExists = await fs4.pathExists(configPath);
414
+ if (configExists && !options.force) {
415
+ if (!options.yes) {
416
+ const { overwrite } = await prompts2({
417
+ type: "confirm",
418
+ name: "overwrite",
419
+ message: "lume.config.json already exists. Overwrite?",
420
+ initial: false
421
+ });
422
+ if (!overwrite) {
423
+ log2("Cancelled.", options);
424
+ return;
425
+ }
426
+ }
427
+ }
428
+ const detected = detectFramework();
429
+ let framework;
430
+ if (detected) {
431
+ framework = detected;
432
+ info(`Detected framework: ${detected}`, options);
433
+ } else if (!options.yes) {
434
+ const { framework: selectedFramework } = await prompts2({
435
+ type: "select",
436
+ name: "framework",
437
+ message: "Which framework are you using?",
438
+ choices: SUPPORTED_FRAMEWORKS.map((f) => ({ title: f, value: f }))
439
+ });
440
+ framework = selectedFramework;
441
+ } else {
442
+ framework = "vue";
443
+ }
444
+ if (!framework) {
445
+ log2(pc3.red("\u274C Framework selection required"), options);
446
+ process.exit(1);
447
+ }
448
+ const templateConfig = getTemplateConfig(framework, template);
449
+ const useCssVariables = options.cssVariables !== false;
450
+ const config = {
451
+ framework,
452
+ style: useCssVariables ? "css-variables" : "default",
453
+ template: templateConfig.template,
454
+ tailwind: {
455
+ config: templateConfig.tailwindConfigPath,
456
+ css: templateConfig.globalsCssPath,
457
+ prefix: ""
458
+ },
459
+ aliases: {
460
+ components: `@/${templateConfig.componentsPath}`,
461
+ utils: `@/${templateConfig.utilsPath}`
462
+ }
463
+ };
464
+ await fs4.writeJson(configPath, config, { spaces: 2 });
465
+ log2(pc3.green(`\u2705 Created lume.config.json`), options);
466
+ let writeFiles = options.yes;
467
+ if (!options.yes) {
468
+ const { confirm } = await prompts2({
469
+ type: "confirm",
470
+ name: "confirm",
471
+ message: "Write globals.css and tailwind.config.ts?",
472
+ initial: true
473
+ });
474
+ writeFiles = confirm;
475
+ }
476
+ if (writeFiles) {
477
+ const cssPath = path4.resolve(cwd, templateConfig.globalsCssPath);
478
+ await fs4.ensureDir(path4.dirname(cssPath));
479
+ if (await fs4.pathExists(cssPath)) {
480
+ if (options.force || !options.yes) {
481
+ const { overwrite } = await prompts2({
482
+ type: "confirm",
483
+ name: "overwrite",
484
+ message: `${templateConfig.globalsCssPath} already exists. Overwrite?`,
485
+ initial: false
486
+ });
487
+ if (overwrite) {
488
+ await fs4.writeFile(cssPath, GLOBALS_CSS_TEMPLATE);
489
+ log2(pc3.green(`\u2705 Updated ${templateConfig.globalsCssPath}`), options);
490
+ }
491
+ }
492
+ } else {
493
+ await fs4.writeFile(cssPath, GLOBALS_CSS_TEMPLATE);
494
+ log2(pc3.green(`\u2705 Created ${templateConfig.globalsCssPath}`), options);
495
+ }
496
+ const tailwindPath = path4.resolve(cwd, templateConfig.tailwindConfigPath);
497
+ if (await fs4.pathExists(tailwindPath)) {
498
+ if (options.force || !options.yes) {
499
+ const { overwrite } = await prompts2({
500
+ type: "confirm",
501
+ name: "overwrite",
502
+ message: `${templateConfig.tailwindConfigPath} already exists. Overwrite?`,
503
+ initial: false
504
+ });
505
+ if (overwrite) {
506
+ await fs4.writeFile(tailwindPath, TAILWIND_CONFIG_TEMPLATE);
507
+ log2(pc3.green(`\u2705 Updated ${templateConfig.tailwindConfigPath}`), options);
508
+ }
509
+ }
510
+ } else {
511
+ await fs4.writeFile(tailwindPath, TAILWIND_CONFIG_TEMPLATE);
512
+ log2(pc3.green(`\u2705 Created ${templateConfig.tailwindConfigPath}`), options);
513
+ }
514
+ }
515
+ let installDeps = options.yes;
516
+ if (!options.yes) {
517
+ const { confirm } = await prompts2({
518
+ type: "confirm",
519
+ name: "confirm",
520
+ message: "Install base dependencies (tailwindcss, autoprefixer, postcss)?",
521
+ initial: true
522
+ });
523
+ installDeps = confirm;
524
+ }
525
+ if (installDeps) {
526
+ log2("", options);
527
+ const baseDeps = ["tailwindcss", "autoprefixer", "postcss"];
528
+ installDependencies(baseDeps, { dev: true });
529
+ log2("", options);
530
+ const frameworkPkg = framework === "vue" ? "@hemia/lume-vue" : `@hemia/lume-${framework}`;
531
+ info(`\u{1F4E6} Installing ${frameworkPkg}...`, options);
532
+ log2(pc3.dim(` When published, run: npm install ${frameworkPkg}`), options);
533
+ }
534
+ log2("", options);
535
+ log2(pc3.green("\u{1F389} All done! Run the following to add components:"), options);
536
+ info(` lume add button`, options);
537
+ log2("", options);
538
+ }
539
+
540
+ // src/commands/list.ts
541
+ import fs5 from "fs-extra";
542
+ import path5 from "path";
543
+ import pc4 from "picocolors";
544
+ import { createRequire as createRequire2 } from "module";
545
+ var require3 = createRequire2(import.meta.url);
546
+ function getFrameworkFromConfig2() {
547
+ try {
548
+ const config = fs5.readJsonSync(
549
+ path5.resolve(process.cwd(), "hemia.config.json")
550
+ );
551
+ return config.framework ?? "vue";
552
+ } catch {
553
+ return "vue";
554
+ }
555
+ }
556
+ function resolveRegistryPath2(framework) {
557
+ const registryRoot = path5.dirname(
558
+ require3.resolve("@hemia/lume-registry/package.json")
559
+ );
560
+ return path5.join(registryRoot, "registry", framework);
561
+ }
562
+ async function list(options = {}) {
563
+ const framework = options.framework ?? getFrameworkFromConfig2();
564
+ const frameworkRegistry = resolveRegistryPath2(framework);
565
+ if (!await fs5.pathExists(frameworkRegistry)) {
566
+ console.log(pc4.red(`\u274C No components found for framework "${framework}"`));
567
+ process.exit(1);
568
+ }
569
+ const entries = await fs5.readdir(frameworkRegistry, { withFileTypes: true });
570
+ const components = entries.filter((entry) => entry.isDirectory());
571
+ if (components.length === 0) {
572
+ console.log(pc4.yellow(`\u26A0\uFE0F No components available for ${framework}`));
573
+ return;
574
+ }
575
+ console.log(pc4.cyan(`
576
+ \u{1F4E6} Available components for ${framework}:
577
+ `));
578
+ for (const component of components) {
579
+ const meta = await readComponentMeta(
580
+ path5.join(frameworkRegistry, component.name)
581
+ );
582
+ if (meta) {
583
+ const deps = meta.registryDependencies?.length ? pc4.dim(` (requires: ${meta.registryDependencies.join(", ")})`) : "";
584
+ console.log(` ${pc4.green("\u25CF")} ${component.name}${deps}`);
585
+ } else {
586
+ console.log(` ${pc4.green("\u25CF")} ${component.name}`);
587
+ }
588
+ }
589
+ console.log();
590
+ console.log(pc4.dim(`Total: ${components.length} component(s)`));
591
+ console.log();
592
+ console.log(pc4.cyan("Usage:"));
593
+ console.log(pc4.dim(` hemia add <component-name>`));
594
+ console.log();
595
+ }
596
+
597
+ // src/index.ts
598
+ var program = new Command();
599
+ program.name("lume").description("lume CLI \u2014 multi-framework component generator inspired by shadcn/ui").version("0.0.1");
600
+ program.command("init").description("Initialize your project and install dependencies").allowUnknownOption(true).argument("[components...]", "name, url or local path to component").option("-t, --template <template>", "the template to use. (next, vite, start, react-router, laravel, astro)").option("-p, --preset [name]", "use a preset configuration. (name, URL, or preset code)").option("-d, --defaults", "use default configuration. (default: false)").option("-y, --yes", "skip confirmation prompt. (default: true)").option("-f, --force", "force overwrite of existing configuration. (default: false)").option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.").option("-s, --silent", "mute output. (default: false)").option("--monorepo", "scaffold a monorepo project.").option("--no-monorepo", "skip the monorepo prompt.").option("--reinstall", "re-install existing UI components.").option("--no-reinstall", "do not re-install existing UI components.").option("--css-variables", "use css variables for theming. (default: true)").option("--no-css-variables", "do not use css variables for theming.").option("-h, --help", "display help for command").action(init);
601
+ program.command("add").description("add a component to your project").allowUnknownOption(true).argument("[components...]", "name, url or local path to component").option("-y, --yes", "skip confirmation prompt. (default: false)").option("-o, --overwrite", "overwrite existing files. (default: false)").option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.").option("-a, --all", "add all available components (default: false)").option("-p, --path <path>", "the path to add the component to.").option("-s, --silent", "mute output. (default: false)").option("--dry-run", "preview changes without writing files. (default: false)").option("--diff [path]", "show diff for a file.").option("--view [path]", "show file contents.").option("-h, --help", "display help for command").option("-f, --framework <framework>", "Target framework (vue, react, svelte, astro)").action(add);
602
+ program.command("list").description("List all available components").option("-f, --framework <framework>", "Target framework (vue, react, svelte, astro)").action(list);
603
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@hemia/lume",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "files": ["dist"],
6
+ "bin": {
7
+ "lume": "./dist/index.js"
8
+ },
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "scripts": {
13
+ "build": "tsup src/index.ts --format esm --dts",
14
+ "dev": "tsup src/index.ts --format esm --watch"
15
+ },
16
+ "dependencies": {
17
+ "@hemia/lume-core": "^0.0.1",
18
+ "@hemia/lume-registry": "^0.0.1",
19
+ "commander": "^12.0.0",
20
+ "fs-extra": "^11.0.0",
21
+ "picocolors": "^1.0.0",
22
+ "prompts": "^2.4.2"
23
+ },
24
+ "devDependencies": {
25
+ "tsup": "^8.0.0",
26
+ "typescript": "^5.4.0",
27
+ "@types/fs-extra": "^11.0.0",
28
+ "@types/prompts": "^2.4.0"
29
+ }
30
+ }