juxscript 1.1.369 → 1.1.371
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/bin/cli.js +111 -82
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -12,6 +12,117 @@ const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
|
12
12
|
|
|
13
13
|
const [, , command, ...args] = process.argv;
|
|
14
14
|
|
|
15
|
+
// ═══════════════════════════════════════════════════════════════
|
|
16
|
+
// UTILITIES (must be defined before commands that use them)
|
|
17
|
+
// ═══════════════════════════════════════════════════════════════
|
|
18
|
+
function copyDirRecursive(src, dest) {
|
|
19
|
+
if (!fs.existsSync(dest)) {
|
|
20
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
24
|
+
if (entry.name.startsWith('.')) continue;
|
|
25
|
+
|
|
26
|
+
const srcPath = path.join(src, entry.name);
|
|
27
|
+
const destPath = path.join(dest, entry.name);
|
|
28
|
+
|
|
29
|
+
if (entry.isDirectory()) {
|
|
30
|
+
copyDirRecursive(srcPath, destPath);
|
|
31
|
+
} else {
|
|
32
|
+
fs.copyFileSync(srcPath, destPath);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resolveProjectSrcDir() {
|
|
38
|
+
const projectRoot = process.cwd();
|
|
39
|
+
const configPath = path.join(projectRoot, 'juxconfig.js');
|
|
40
|
+
|
|
41
|
+
if (fs.existsSync(configPath)) {
|
|
42
|
+
try {
|
|
43
|
+
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
44
|
+
const srcDirMatch = configContent.match(/srcDir\s*:\s*['"]([^'"]+)['"]/);
|
|
45
|
+
if (srcDirMatch) {
|
|
46
|
+
return path.resolve(projectRoot, srcDirMatch[1]);
|
|
47
|
+
}
|
|
48
|
+
} catch (_) { }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return path.resolve(projectRoot, 'jux');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function resolveDestFolderName(parentDir, baseName) {
|
|
55
|
+
const first = path.join(parentDir, baseName);
|
|
56
|
+
if (!fs.existsSync(first)) return baseName;
|
|
57
|
+
|
|
58
|
+
const juxName = baseName + '-jux';
|
|
59
|
+
const second = path.join(parentDir, juxName);
|
|
60
|
+
if (!fs.existsSync(second)) return juxName;
|
|
61
|
+
|
|
62
|
+
for (let i = 1; i <= 99; i++) {
|
|
63
|
+
const numbered = `${baseName}-${i}`;
|
|
64
|
+
if (!fs.existsSync(path.join(parentDir, numbered))) return numbered;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return `${baseName}-${Date.now()}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function promptPresetSelection(presets) {
|
|
71
|
+
return new Promise((resolve) => {
|
|
72
|
+
console.log('\n📦 Available component presets:\n');
|
|
73
|
+
presets.forEach((p, i) => {
|
|
74
|
+
const presetDir = path.join(PACKAGE_ROOT, 'presets', p);
|
|
75
|
+
const files = fs.readdirSync(presetDir).filter(f => !f.startsWith('.'));
|
|
76
|
+
console.log(` ${i + 1}) ${p} (${files.join(', ')})`);
|
|
77
|
+
});
|
|
78
|
+
console.log(` 0) Cancel\n`);
|
|
79
|
+
|
|
80
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
81
|
+
rl.question('Select a preset (number or name): ', (answer) => {
|
|
82
|
+
rl.close();
|
|
83
|
+
const trimmed = answer.trim();
|
|
84
|
+
|
|
85
|
+
const num = parseInt(trimmed, 10);
|
|
86
|
+
if (num === 0) return resolve(null);
|
|
87
|
+
if (num >= 1 && num <= presets.length) return resolve(presets[num - 1]);
|
|
88
|
+
|
|
89
|
+
if (presets.includes(trimmed)) return resolve(trimmed);
|
|
90
|
+
|
|
91
|
+
console.error(`❌ Invalid selection: "${trimmed}"`);
|
|
92
|
+
resolve(null);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function promptYesNo(question) {
|
|
98
|
+
return new Promise((resolve) => {
|
|
99
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
100
|
+
rl.question(`${question} (y/N): `, (answer) => {
|
|
101
|
+
rl.close();
|
|
102
|
+
resolve(answer.trim().toLowerCase() === 'y');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function showHelp() {
|
|
108
|
+
console.log(`
|
|
109
|
+
JUX CLI
|
|
110
|
+
|
|
111
|
+
Usage:
|
|
112
|
+
jux create <name> Create a new JUX project
|
|
113
|
+
jux init Initialize JUX in current directory
|
|
114
|
+
jux build Build for production
|
|
115
|
+
jux serve Start production server
|
|
116
|
+
jux serve --hot Start dev server with hot reload
|
|
117
|
+
jux comp [name] Add a component preset to your project
|
|
118
|
+
jux comp [name] -f Force overwrite (backs up existing files)
|
|
119
|
+
|
|
120
|
+
Options:
|
|
121
|
+
--help, -h Show this help message
|
|
122
|
+
--force, -f Overwrite existing files (creates .bak backups)
|
|
123
|
+
`);
|
|
124
|
+
}
|
|
125
|
+
|
|
15
126
|
// ═══════════════════════════════════════════════════════════════
|
|
16
127
|
// COMMAND: create <project-name>
|
|
17
128
|
// Creates a new JUX project from the template
|
|
@@ -256,47 +367,6 @@ function runServe() {
|
|
|
256
367
|
child.on('close', (code) => process.exit(code || 0));
|
|
257
368
|
}
|
|
258
369
|
|
|
259
|
-
// ═══════════════════════════════════════════════════════════════
|
|
260
|
-
// UTILITIES
|
|
261
|
-
// ═══════════════════════════════════════════════════════════════
|
|
262
|
-
function copyDirRecursive(src, dest) {
|
|
263
|
-
if (!fs.existsSync(dest)) {
|
|
264
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
268
|
-
if (entry.name.startsWith('.')) continue; // Skip hidden files
|
|
269
|
-
|
|
270
|
-
const srcPath = path.join(src, entry.name);
|
|
271
|
-
const destPath = path.join(dest, entry.name);
|
|
272
|
-
|
|
273
|
-
if (entry.isDirectory()) {
|
|
274
|
-
copyDirRecursive(srcPath, destPath);
|
|
275
|
-
} else {
|
|
276
|
-
fs.copyFileSync(srcPath, destPath);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function showHelp() {
|
|
282
|
-
console.log(`
|
|
283
|
-
JUX CLI
|
|
284
|
-
|
|
285
|
-
Usage:
|
|
286
|
-
jux create <name> Create a new JUX project
|
|
287
|
-
jux init Initialize JUX in current directory
|
|
288
|
-
jux build Build for production
|
|
289
|
-
jux serve Start production server
|
|
290
|
-
jux serve --hot Start dev server with hot reload
|
|
291
|
-
jux comp [name] Add a component preset to your project
|
|
292
|
-
jux comp [name] -f Force overwrite (backs up existing files)
|
|
293
|
-
|
|
294
|
-
Options:
|
|
295
|
-
--help, -h Show this help message
|
|
296
|
-
--force, -f Overwrite existing files (creates .bak backups)
|
|
297
|
-
`);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
370
|
// ═══════════════════════════════════════════════════════════════
|
|
301
371
|
// COMMAND: comp [name]
|
|
302
372
|
// Copies a preset component into the project's jux directory
|
|
@@ -409,47 +479,6 @@ async function runComp(compName) {
|
|
|
409
479
|
console.log('');
|
|
410
480
|
}
|
|
411
481
|
|
|
412
|
-
/**
|
|
413
|
-
* Resolve a safe folder name: compName -> compName-jux -> compName-1, compName-2, ...
|
|
414
|
-
*/
|
|
415
|
-
function resolveDestFolderName(parentDir, baseName) {
|
|
416
|
-
const first = path.join(parentDir, baseName);
|
|
417
|
-
if (!fs.existsSync(first)) return baseName;
|
|
418
|
-
|
|
419
|
-
const juxName = baseName + '-jux';
|
|
420
|
-
const second = path.join(parentDir, juxName);
|
|
421
|
-
if (!fs.existsSync(second)) return juxName;
|
|
422
|
-
|
|
423
|
-
for (let i = 1; i <= 99; i++) {
|
|
424
|
-
const numbered = `${baseName}-${i}`;
|
|
425
|
-
if (!fs.existsSync(path.join(parentDir, numbered))) return numbered;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// Last resort — timestamp
|
|
429
|
-
return `${baseName}-${Date.now()}`;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/**
|
|
433
|
-
* List all files in a directory recursively, returning relative paths
|
|
434
|
-
*/
|
|
435
|
-
function listFilesRecursive(dir, base = '') {
|
|
436
|
-
const results = [];
|
|
437
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
438
|
-
|
|
439
|
-
for (const entry of entries) {
|
|
440
|
-
if (entry.name.startsWith('.')) continue;
|
|
441
|
-
const rel = base ? path.join(base, entry.name) : entry.name;
|
|
442
|
-
|
|
443
|
-
if (entry.isDirectory()) {
|
|
444
|
-
results.push(...listFilesRecursive(path.join(dir, entry.name), rel));
|
|
445
|
-
} else {
|
|
446
|
-
results.push(rel);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
return results;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
482
|
// ═══════════════════════════════════════════════════════════════
|
|
454
483
|
// MAIN ROUTER
|
|
455
484
|
// ═══════════════════════════════════════════════════════════════
|