pejay-ui 1.3.4 → 1.3.5

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 CHANGED
@@ -11,7 +11,36 @@ npx pejay-ui init
11
11
 
12
12
  ### 2. Add Component
13
13
  ```bash
14
- npx pejay-ui add <component-name>
14
+ npx pejay-ui add <component-name-or-category> [options]
15
+ ```
16
+
17
+ **Options:**
18
+ - `--all`: Install all components in the specified category.
19
+ - `--select`: Interactively select which components to install from the specified category.
20
+
21
+ **Examples:**
22
+ - Install a single component directly:
23
+ ```bash
24
+ npx pejay-ui add form/input
25
+ ```
26
+ - Install all components in a category (e.g., `form`):
27
+ ```bash
28
+ npx pejay-ui add form --all
29
+ ```
30
+ - Select specific components to install from a category (e.g., `form`):
31
+ ```bash
32
+ npx pejay-ui add form --select
33
+ ```
34
+ *(Note: Running `npx pejay-ui add <category>` without options will default to the interactive selection prompt).*
35
+
36
+ **Automatic Exports (Auto-Indexing):**
37
+ Installing components automatically generates or updates `index.ts` (or `index.js`) files at:
38
+ 1. The category level (e.g., `src/pejay-ui/components/form/index.ts`)
39
+ 2. The global components level (`src/pejay-ui/components/index.ts`)
40
+
41
+ This allows you to easily import multiple components:
42
+ ```typescript
43
+ import { Input, Checkbox, AmountInput } from "@/pejay-ui/components";
15
44
  ```
16
45
 
17
46
  ### 3. Remove Component
@@ -19,6 +48,15 @@ npx pejay-ui add <component-name>
19
48
  npx pejay-ui remove <component-name>
20
49
  ```
21
50
 
51
+ ### 4. Check Components Status
52
+ ```bash
53
+ npx pejay-ui status
54
+ ```
55
+
56
+ Lists all available components categorized, showing their local installation status:
57
+ - Installed components are marked with a green `[āœ”]`.
58
+ - Uninstalled components are marked with `[ ]`.
59
+
22
60
  ---
23
61
 
24
62
  ## Available Components & Scaffolds
package/bin/cli.js CHANGED
@@ -74,7 +74,9 @@ program
74
74
  program
75
75
  .command("add <component>")
76
76
  .description("Add a component to your project")
77
- .action(async (component) => {
77
+ .option("--all", "Add all components in the category")
78
+ .option("--select", "Select specific components from the category to add")
79
+ .action(async (component, options) => {
78
80
  try {
79
81
  const cwd = process.cwd();
80
82
  const configPath = path.join(cwd, "pejay-ui.json");
@@ -95,6 +97,46 @@ program
95
97
  const registry = await fs.readJSON(registryPath);
96
98
  const isTsProject = await fs.pathExists(path.join(cwd, "tsconfig.json"));
97
99
 
100
+ // Determine which components to install
101
+ let selectedComponents = [];
102
+
103
+ // Check if it's a category
104
+ const categoryComponents = Object.keys(registry).filter(
105
+ (key) => registry[key].category === component
106
+ );
107
+
108
+ if (categoryComponents.length > 0) {
109
+ if (options.all) {
110
+ selectedComponents = categoryComponents;
111
+ } else {
112
+ // Dynamic prompt using inquirer checkbox for --select or when no flag is specified for category
113
+ const answers = await prompt([
114
+ {
115
+ type: "checkbox",
116
+ name: "components",
117
+ message: `Select components from category "${component}" to add:`,
118
+ choices: categoryComponents.map((key) => ({
119
+ name: `${registry[key].name} (${key})`,
120
+ value: key,
121
+ })),
122
+ },
123
+ ]);
124
+ selectedComponents = answers.components;
125
+ if (selectedComponents.length === 0) {
126
+ console.log("No components selected. Exiting.");
127
+ process.exit(0);
128
+ }
129
+ }
130
+ } else {
131
+ // Not a category, treat as a single component key
132
+ if (!registry[component]) {
133
+ console.error(`Error: Component or Category '${component}' not found in registry.`);
134
+ console.log(`Available categories/components: ${Array.from(new Set(Object.values(registry).map(c => c.category).filter(Boolean))).join(", ")} or ${Object.keys(registry).join(", ")}`);
135
+ process.exit(1);
136
+ }
137
+ selectedComponents = [component];
138
+ }
139
+
98
140
  // Track all components to install (including dependencies) in topological/order of dependencies
99
141
  const installQueue = [];
100
142
  const visited = new Set();
@@ -106,7 +148,6 @@ program
106
148
  const compData = registry[compName];
107
149
  if (!compData) {
108
150
  console.error(`Error: Component '${compName}' not found in registry.`);
109
- console.log(`Available components: ${Object.keys(registry).join(", ")}`);
110
151
  process.exit(1);
111
152
  }
112
153
 
@@ -120,13 +161,15 @@ program
120
161
  installQueue.push(compName);
121
162
  };
122
163
 
123
- resolveDependencies(component);
164
+ for (const comp of selectedComponents) {
165
+ resolveDependencies(comp);
166
+ }
124
167
 
125
168
  console.log("\nšŸš€ Starting installation...\n");
126
169
 
127
170
  for (const compToInstall of installQueue) {
128
- // Skip if already marked as installed in config (unless it is the main component requested)
129
- if (config.installed?.[compToInstall] && compToInstall !== component) {
171
+ // Skip if already marked as installed in config (unless it is one of the explicitly requested components)
172
+ if (config.installed?.[compToInstall] && !selectedComponents.includes(compToInstall)) {
130
173
  console.log(`Component '${compToInstall}' is already installed. Skipping dependency installation.`);
131
174
  continue;
132
175
  }
@@ -293,6 +336,56 @@ program
293
336
  }
294
337
  }
295
338
 
339
+ // 3.5 Automatically generate/update category-level and global index.ts/index.js files
340
+ if (componentData.category && !["scaffold", "scaffolds", "script", "scripts"].includes(componentData.category.toLowerCase())) {
341
+ const indexExt = isTsProject ? "ts" : "js";
342
+ const filesInDir = await fs.readdir(targetDir);
343
+ const exportableFiles = [];
344
+
345
+ for (const file of filesInDir) {
346
+ const filePath = path.join(targetDir, file);
347
+ const stat = await fs.stat(filePath);
348
+ if (stat.isFile()) {
349
+ const ext = path.extname(file);
350
+ const name = path.basename(file, ext);
351
+ if ((ext === ".tsx" || ext === ".ts" || ext === ".jsx" || ext === ".js") && name !== "index") {
352
+ exportableFiles.push(name);
353
+ }
354
+ }
355
+ }
356
+
357
+ if (exportableFiles.length > 0) {
358
+ exportableFiles.sort();
359
+ const indexFilePath = path.join(targetDir, `index.${indexExt}`);
360
+ const exportLines = exportableFiles.map(name => `export * from "./${name}";`);
361
+ await fs.writeFile(indexFilePath, exportLines.join("\n") + "\n", "utf-8");
362
+ console.log(`āœ… Updated index.${indexExt} in ${path.relative(cwd, targetDir)}`);
363
+
364
+ // Also update the global components index file
365
+ const componentsDir = path.dirname(targetDir); // src/pejay-ui/components
366
+ if (await fs.pathExists(componentsDir)) {
367
+ const categories = await fs.readdir(componentsDir);
368
+ const validCategories = [];
369
+ for (const cat of categories) {
370
+ const catDir = path.join(componentsDir, cat);
371
+ const catStat = await fs.stat(catDir);
372
+ if (catStat.isDirectory()) {
373
+ if (await fs.pathExists(path.join(catDir, `index.ts`)) || await fs.pathExists(path.join(catDir, `index.js`))) {
374
+ validCategories.push(cat);
375
+ }
376
+ }
377
+ }
378
+ if (validCategories.length > 0) {
379
+ validCategories.sort();
380
+ const globalIndexFile = path.join(componentsDir, `index.${indexExt}`);
381
+ const globalExportLines = validCategories.map(cat => `export * from "./${cat}";`);
382
+ await fs.writeFile(globalIndexFile, globalExportLines.join("\n") + "\n", "utf-8");
383
+ console.log(`āœ… Updated global index.${indexExt} in ${path.relative(cwd, componentsDir)}`);
384
+ }
385
+ }
386
+ }
387
+ }
388
+
296
389
  // 4. Update State tracking in config
297
390
  config.installed = config.installed || {};
298
391
  config.installed[compToInstall] = {
@@ -358,6 +451,73 @@ program
358
451
  }
359
452
  }
360
453
 
454
+ // 1.5 Update index files
455
+ if (componentData.category && !["scaffold", "scaffolds", "script", "scripts"].includes(componentData.category.toLowerCase())) {
456
+ const isTsProject = await fs.pathExists(path.join(cwd, "tsconfig.json"));
457
+ const targetDir = path.join(cwd, config.baseDir, "components", componentData.category);
458
+ const indexExt = isTsProject ? "ts" : "js";
459
+
460
+ if (await fs.pathExists(targetDir)) {
461
+ const filesInDir = await fs.readdir(targetDir);
462
+ const exportableFiles = [];
463
+
464
+ for (const file of filesInDir) {
465
+ const filePath = path.join(targetDir, file);
466
+ const stat = await fs.stat(filePath);
467
+ if (stat.isFile()) {
468
+ const ext = path.extname(file);
469
+ const name = path.basename(file, ext);
470
+ if ((ext === ".tsx" || ext === ".ts" || ext === ".jsx" || ext === ".js") && name !== "index") {
471
+ exportableFiles.push(name);
472
+ }
473
+ }
474
+ }
475
+
476
+ const indexFilePath = path.join(targetDir, `index.${indexExt}`);
477
+ if (exportableFiles.length > 0) {
478
+ exportableFiles.sort();
479
+ const exportLines = exportableFiles.map(name => `export * from "./${name}";`);
480
+ await fs.writeFile(indexFilePath, exportLines.join("\n") + "\n", "utf-8");
481
+ console.log(`āœ… Updated index.${indexExt} in ${path.relative(cwd, targetDir)}`);
482
+ } else {
483
+ // No files left, delete the category index file
484
+ if (await fs.pathExists(indexFilePath)) {
485
+ await fs.remove(indexFilePath);
486
+ console.log(`šŸ—‘ļø Removed empty index.${indexExt} in ${path.relative(cwd, targetDir)}`);
487
+ }
488
+ }
489
+
490
+ // Also update the global components index file
491
+ const componentsDir = path.dirname(targetDir);
492
+ if (await fs.pathExists(componentsDir)) {
493
+ const categories = await fs.readdir(componentsDir);
494
+ const validCategories = [];
495
+ for (const cat of categories) {
496
+ const catDir = path.join(componentsDir, cat);
497
+ const catStat = await fs.stat(catDir);
498
+ if (catStat.isDirectory()) {
499
+ if (await fs.pathExists(path.join(catDir, `index.ts`)) || await fs.pathExists(path.join(catDir, `index.js`))) {
500
+ validCategories.push(cat);
501
+ }
502
+ }
503
+ }
504
+ const globalIndexFile = path.join(componentsDir, `index.${indexExt}`);
505
+ if (validCategories.length > 0) {
506
+ validCategories.sort();
507
+ const globalExportLines = validCategories.map(cat => `export * from "./${cat}";`);
508
+ await fs.writeFile(globalIndexFile, globalExportLines.join("\n") + "\n", "utf-8");
509
+ console.log(`āœ… Updated global index.${indexExt} in ${path.relative(cwd, componentsDir)}`);
510
+ } else {
511
+ // No categories left with index files, remove the global index
512
+ if (await fs.pathExists(globalIndexFile)) {
513
+ await fs.remove(globalIndexFile);
514
+ console.log(`šŸ—‘ļø Removed empty global index.${indexExt} in ${path.relative(cwd, componentsDir)}`);
515
+ }
516
+ }
517
+ }
518
+ }
519
+ }
520
+
361
521
  // 2. Build Utility Usage Map
362
522
  const utilityUsage = {};
363
523
  for (const [compName, compInfo] of Object.entries(config.installed)) {
@@ -452,4 +612,86 @@ program
452
612
  }
453
613
  });
454
614
 
615
+ /* =============================
616
+ STATUS COMMAND
617
+ ============================= */
618
+ program
619
+ .command("status")
620
+ .description("List all available components and check their installation status")
621
+ .action(async () => {
622
+ try {
623
+ const cwd = process.cwd();
624
+ const configPath = path.join(cwd, "pejay-ui.json");
625
+
626
+ let config = { installed: {} };
627
+ let hasConfig = true;
628
+
629
+ if (!await fs.pathExists(configPath)) {
630
+ hasConfig = false;
631
+ } else {
632
+ try {
633
+ config = await fs.readJSON(configPath);
634
+ } catch (e) {
635
+ hasConfig = false;
636
+ }
637
+ }
638
+
639
+ const registryPath = path.join(packageRoot, "registry.json");
640
+ if (!await fs.pathExists(registryPath)) {
641
+ console.error("Error: Registry configuration not found.");
642
+ process.exit(1);
643
+ }
644
+
645
+ const registry = await fs.readJSON(registryPath);
646
+
647
+ // Terminal styling colors
648
+ const GREEN = "\x1b[32m";
649
+ const CYAN = "\x1b[36m";
650
+ const DIM = "\x1b[2m";
651
+ const RESET = "\x1b[0m";
652
+ const YELLOW = "\x1b[33m";
653
+
654
+ console.log(`\nšŸ” ${CYAN}pejay-ui Components Status:${RESET}\n`);
655
+
656
+ if (!hasConfig) {
657
+ console.log(`${YELLOW}Note: pejay-ui.json not found. Initialize first via 'npx pejay-ui init'.${RESET}`);
658
+ console.log(`${YELLOW}Showing all components as uninstalled.${RESET}\n`);
659
+ }
660
+
661
+ // Group registry items by category
662
+ const categories = {};
663
+ for (const [key, compData] of Object.entries(registry)) {
664
+ const category = compData.category || "other";
665
+ if (!categories[category]) {
666
+ categories[category] = [];
667
+ }
668
+ categories[category].push({
669
+ key,
670
+ name: compData.name,
671
+ installed: !!config.installed?.[key]
672
+ });
673
+ }
674
+
675
+ // Print categories and components sorted alphabetically
676
+ const sortedCategoryNames = Object.keys(categories).sort();
677
+ for (const cat of sortedCategoryNames) {
678
+ console.log(`${CYAN}Category: ${cat}${RESET}`);
679
+ const comps = categories[cat];
680
+ comps.sort((a, b) => a.name.localeCompare(b.name));
681
+
682
+ for (const comp of comps) {
683
+ if (comp.installed) {
684
+ console.log(` ${GREEN}[āœ”] ${comp.name}${RESET} ${DIM}(${comp.key})${RESET}`);
685
+ } else {
686
+ console.log(` [ ] ${comp.name} ${DIM}(${comp.key})${RESET}`);
687
+ }
688
+ }
689
+ console.log(); // blank line between categories
690
+ }
691
+
692
+ } catch (err) {
693
+ console.error("\nāŒ Status display failed\n", err);
694
+ }
695
+ });
696
+
455
697
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pejay-ui",
3
- "version": "1.3.4",
3
+ "version": "1.3.5",
4
4
  "type": "module",
5
5
  "description": "react ui components",
6
6
  "bin": {
package/registry.json CHANGED
@@ -46,7 +46,7 @@
46
46
  "lucide-react",
47
47
  "@floating-ui/react"
48
48
  ],
49
- "dependencies": ["select-dropdown/select-input"]
49
+ "dependencies": ["dropdown/select-input"]
50
50
  },
51
51
  "form/date-range-picker": {
52
52
  "name": "DateRangePicker",
@@ -59,7 +59,7 @@
59
59
  "lucide-react",
60
60
  "@floating-ui/react"
61
61
  ],
62
- "dependencies": ["select-dropdown/select-input"]
62
+ "dependencies": ["dropdown/select-input"]
63
63
  },
64
64
  "form/email-input": {
65
65
  "name": "EmailInput",
@@ -143,7 +143,7 @@
143
143
  "lucide-react",
144
144
  "@floating-ui/react"
145
145
  ],
146
- "dependencies": ["select-dropdown/select-input"]
146
+ "dependencies": ["dropdown/select-input"]
147
147
  },
148
148
  "form/time-range-picker": {
149
149
  "name": "TimeRangePicker",
@@ -156,7 +156,7 @@
156
156
  "lucide-react",
157
157
  "@floating-ui/react"
158
158
  ],
159
- "dependencies": ["select-dropdown/select-input"]
159
+ "dependencies": ["dropdown/select-input"]
160
160
  },
161
161
  "form/url-input": {
162
162
  "name": "UrlInput",