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 +39 -1
- package/bin/cli.js +247 -5
- package/package.json +1 -1
- package/registry.json +4 -4
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
|
-
.
|
|
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
|
-
|
|
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
|
|
129
|
-
if (config.installed?.[compToInstall] && compToInstall
|
|
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
package/registry.json
CHANGED
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"lucide-react",
|
|
47
47
|
"@floating-ui/react"
|
|
48
48
|
],
|
|
49
|
-
"dependencies": ["
|
|
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": ["
|
|
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": ["
|
|
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": ["
|
|
159
|
+
"dependencies": ["dropdown/select-input"]
|
|
160
160
|
},
|
|
161
161
|
"form/url-input": {
|
|
162
162
|
"name": "UrlInput",
|