juxscript 1.1.365 → 1.1.367

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 CHANGED
@@ -4,6 +4,7 @@ import path from 'path';
4
4
  import fs from 'fs';
5
5
  import { fileURLToPath } from 'url';
6
6
  import { spawn } from 'child_process';
7
+ import { createInterface } from 'readline';
7
8
 
8
9
  const __filename = fileURLToPath(import.meta.url);
9
10
  const __dirname = path.dirname(__filename);
@@ -287,23 +288,154 @@ Usage:
287
288
  jux build Build for production
288
289
  jux serve Start production server
289
290
  jux serve --hot Start dev server with hot reload
291
+ jux comp [name] Add a component preset to your project
290
292
 
291
293
  Options:
292
294
  --help, -h Show this help message
293
295
  `);
294
296
  }
295
297
 
298
+ // ═══════════════════════════════════════════════════════════════
299
+ // COMMAND: comp [name]
300
+ // Copies a preset component into the project's jux directory
301
+ // ═══════════════════════════════════════════════════════════════
302
+ async function runComp(compName) {
303
+ const presetsDir = path.join(PACKAGE_ROOT, 'presets');
304
+
305
+ if (!fs.existsSync(presetsDir)) {
306
+ console.error('❌ No presets directory found in juxscript package.');
307
+ process.exit(1);
308
+ }
309
+
310
+ // Discover available presets (each subfolder is a preset)
311
+ const available = fs.readdirSync(presetsDir, { withFileTypes: true })
312
+ .filter(d => d.isDirectory())
313
+ .map(d => d.name);
314
+
315
+ if (available.length === 0) {
316
+ console.error('❌ No presets available.');
317
+ process.exit(1);
318
+ }
319
+
320
+ // If no name given, show interactive list
321
+ if (!compName) {
322
+ compName = await promptPresetSelection(available);
323
+ if (!compName) {
324
+ console.log('Cancelled.');
325
+ process.exit(0);
326
+ }
327
+ }
328
+
329
+ // Validate preset exists
330
+ const presetDir = path.join(presetsDir, compName);
331
+ if (!fs.existsSync(presetDir)) {
332
+ console.error(`❌ Preset "${compName}" not found.`);
333
+ console.log(`\nAvailable presets:`);
334
+ available.forEach(p => console.log(` • ${p}`));
335
+ process.exit(1);
336
+ }
337
+
338
+ // Resolve target directory from juxconfig.js or default 'jux'
339
+ const targetSrcDir = resolveProjectSrcDir();
340
+
341
+ if (!fs.existsSync(targetSrcDir)) {
342
+ fs.mkdirSync(targetSrcDir, { recursive: true });
343
+ }
344
+
345
+ // Copy all files from preset to project src dir
346
+ const presetFiles = fs.readdirSync(presetDir, { withFileTypes: true });
347
+ let copied = 0;
348
+
349
+ for (const entry of presetFiles) {
350
+ if (entry.isFile()) {
351
+ const srcPath = path.join(presetDir, entry.name);
352
+ const destPath = path.join(targetSrcDir, entry.name);
353
+
354
+ if (fs.existsSync(destPath)) {
355
+ const overwrite = await promptYesNo(` ⚠️ "${entry.name}" already exists. Overwrite?`);
356
+ if (!overwrite) {
357
+ console.log(` ⏭️ Skipped ${entry.name}`);
358
+ continue;
359
+ }
360
+ }
361
+
362
+ fs.copyFileSync(srcPath, destPath);
363
+ console.log(` ✅ ${entry.name}`);
364
+ copied++;
365
+ }
366
+ }
367
+
368
+ console.log(`\n✅ Copied ${copied} file(s) from preset "${compName}" to ${path.relative(process.cwd(), targetSrcDir)}/\n`);
369
+ }
370
+
371
+ function resolveProjectSrcDir() {
372
+ const projectRoot = process.cwd();
373
+ const configPath = path.join(projectRoot, 'juxconfig.js');
374
+
375
+ if (fs.existsSync(configPath)) {
376
+ try {
377
+ const configContent = fs.readFileSync(configPath, 'utf8');
378
+ const srcDirMatch = configContent.match(/srcDir\s*:\s*['"]([^'"]+)['"]/);
379
+ if (srcDirMatch) {
380
+ return path.resolve(projectRoot, srcDirMatch[1]);
381
+ }
382
+ } catch (_) { }
383
+ }
384
+
385
+ return path.resolve(projectRoot, 'jux');
386
+ }
387
+
388
+ function promptPresetSelection(presets) {
389
+ return new Promise((resolve) => {
390
+ console.log('\n📦 Available component presets:\n');
391
+ presets.forEach((p, i) => {
392
+ // Read files in preset to show what's included
393
+ const presetDir = path.join(PACKAGE_ROOT, 'presets', p);
394
+ const files = fs.readdirSync(presetDir).filter(f => !f.startsWith('.'));
395
+ console.log(` ${i + 1}) ${p} (${files.join(', ')})`);
396
+ });
397
+ console.log(` 0) Cancel\n`);
398
+
399
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
400
+ rl.question('Select a preset (number or name): ', (answer) => {
401
+ rl.close();
402
+ const trimmed = answer.trim();
403
+
404
+ // By number
405
+ const num = parseInt(trimmed, 10);
406
+ if (num === 0) return resolve(null);
407
+ if (num >= 1 && num <= presets.length) return resolve(presets[num - 1]);
408
+
409
+ // By name
410
+ if (presets.includes(trimmed)) return resolve(trimmed);
411
+
412
+ console.error(`❌ Invalid selection: "${trimmed}"`);
413
+ resolve(null);
414
+ });
415
+ });
416
+ }
417
+
418
+ function promptYesNo(question) {
419
+ return new Promise((resolve) => {
420
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
421
+ rl.question(`${question} (y/N): `, (answer) => {
422
+ rl.close();
423
+ resolve(answer.trim().toLowerCase() === 'y');
424
+ });
425
+ });
426
+ }
427
+
296
428
  // ═══════════════════════════════════════════════════════════════
297
429
  // MAIN ROUTER
298
430
  // ═══════════════════════════════════════════════════════════════
299
431
  switch (command) {
300
432
  case 'create':
301
- createProject(args[0]); // ✅ Already async, no await needed at top level
433
+ createProject(args[0]);
302
434
  break;
303
435
 
304
436
  case 'init':
305
437
  case 'install':
306
- initProject(); // ✅ Already async, no await needed at top level
438
+ initProject();
307
439
  break;
308
440
 
309
441
  case 'build':
@@ -315,6 +447,12 @@ switch (command) {
315
447
  runServe();
316
448
  break;
317
449
 
450
+ case 'comp':
451
+ case 'component':
452
+ case 'preset':
453
+ runComp(args[0]);
454
+ break;
455
+
318
456
  case '--help':
319
457
  case '-h':
320
458
  case undefined:
@@ -4,17 +4,11 @@ export interface RouteInfo {
4
4
  file: string;
5
5
  }
6
6
  export declare const routes: {
7
- /** All .jux file routes from the source directory */
8
7
  all(): RouteInfo[];
9
- /** Just the route paths */
10
8
  paths(): string[];
11
- /** Current browser path */
12
9
  current(): string;
13
- /** Is this path active? */
14
10
  isActive(path: string): boolean;
15
- /** SPA navigate */
16
11
  navigate(path: string): void;
17
- /** Listen for route changes */
18
12
  onNavigate(callback: (path: string) => void): () => void;
19
13
  };
20
14
  export default routes;
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../lib/components/routes.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAChB;AAcD,eAAO,MAAM,MAAM;IACf,qDAAqD;WAC9C,SAAS,EAAE;IAIlB,2BAA2B;aAClB,MAAM,EAAE;IAIjB,2BAA2B;eAChB,MAAM;IAIjB,2BAA2B;mBACZ,MAAM,GAAG,OAAO;IAI/B,mBAAmB;mBACJ,MAAM,GAAG,IAAI;IAK5B,+BAA+B;yBACV,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;CAM3D,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../lib/components/routes.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAChB;AAQD,eAAO,MAAM,MAAM;WACR,SAAS,EAAE;aAGT,MAAM,EAAE;eAGN,MAAM;mBAGF,MAAM,GAAG,OAAO;mBAGhB,MAAM,GAAG,IAAI;yBAIP,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;CAM3D,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -1,9 +1,3 @@
1
- function _nameFromPath(p) {
2
- if (p === '/')
3
- return 'Home';
4
- const seg = p.split('/').filter(Boolean).pop() || 'Home';
5
- return seg.split('-').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(' ');
6
- }
7
1
  function _getData() {
8
2
  const w = typeof window !== 'undefined' ? window : null;
9
3
  if (!w?.__juxRouteIndex)
@@ -11,29 +5,23 @@ function _getData() {
11
5
  return w.__juxRouteIndex;
12
6
  }
13
7
  export const routes = {
14
- /** All .jux file routes from the source directory */
15
8
  all() {
16
9
  return _getData();
17
10
  },
18
- /** Just the route paths */
19
11
  paths() {
20
12
  return _getData().map(r => r.path);
21
13
  },
22
- /** Current browser path */
23
14
  current() {
24
15
  return typeof window !== 'undefined' ? window.location.pathname : '/';
25
16
  },
26
- /** Is this path active? */
27
17
  isActive(path) {
28
18
  return routes.current() === path;
29
19
  },
30
- /** SPA navigate */
31
20
  navigate(path) {
32
21
  const w = typeof window !== 'undefined' ? window : null;
33
22
  if (w?.navigateTo)
34
23
  w.navigateTo(path);
35
24
  },
36
- /** Listen for route changes */
37
25
  onNavigate(callback) {
38
26
  if (typeof window === 'undefined')
39
27
  return () => { };
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../lib/components/routes.ts"],"names":[],"mappings":"AAMA,SAAS,aAAa,CAAC,CAAS;IAC5B,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,MAAM,CAAC;IAC7B,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,IAAI,MAAM,CAAC;IACzD,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACrF,CAAC;AAED,SAAS,QAAQ;IACb,MAAM,CAAC,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAE,MAAc,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,IAAI,CAAC,CAAC,EAAE,eAAe;QAAE,OAAO,EAAE,CAAC;IACnC,OAAO,CAAC,CAAC,eAAe,CAAC;AAC7B,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IAClB,qDAAqD;IACrD,GAAG;QACC,OAAO,QAAQ,EAAE,CAAC;IACtB,CAAC;IAED,2BAA2B;IAC3B,KAAK;QACD,OAAO,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,2BAA2B;IAC3B,OAAO;QACH,OAAO,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;IAC1E,CAAC;IAED,2BAA2B;IAC3B,QAAQ,CAAC,IAAY;QACjB,OAAO,MAAM,CAAC,OAAO,EAAE,KAAK,IAAI,CAAC;IACrC,CAAC;IAED,mBAAmB;IACnB,QAAQ,CAAC,IAAY;QACjB,MAAM,CAAC,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAE,MAAc,CAAC,CAAC,CAAC,IAAI,CAAC;QACjE,IAAI,CAAC,EAAE,UAAU;YAAE,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,+BAA+B;IAC/B,UAAU,CAAC,QAAgC;QACvC,IAAI,OAAO,MAAM,KAAK,WAAW;YAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzD,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC7C,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACjE,CAAC;CACJ,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../lib/components/routes.ts"],"names":[],"mappings":"AAMA,SAAS,QAAQ;IACb,MAAM,CAAC,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAE,MAAc,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,IAAI,CAAC,CAAC,EAAE,eAAe;QAAE,OAAO,EAAE,CAAC;IACnC,OAAO,CAAC,CAAC,eAAe,CAAC;AAC7B,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IAClB,GAAG;QACC,OAAO,QAAQ,EAAE,CAAC;IACtB,CAAC;IACD,KAAK;QACD,OAAO,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IACD,OAAO;QACH,OAAO,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;IAC1E,CAAC;IACD,QAAQ,CAAC,IAAY;QACjB,OAAO,MAAM,CAAC,OAAO,EAAE,KAAK,IAAI,CAAC;IACrC,CAAC;IACD,QAAQ,CAAC,IAAY;QACjB,MAAM,CAAC,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAE,MAAc,CAAC,CAAC,CAAC,IAAI,CAAC;QACjE,IAAI,CAAC,EAAE,UAAU;YAAE,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IACD,UAAU,CAAC,QAAgC;QACvC,IAAI,OAAO,MAAM,KAAK,WAAW;YAAE,OAAO,GAAG,EAAE,GAAG,CAAC,CAAC;QACpD,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzD,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC7C,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACjE,CAAC;CACJ,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.365",
3
+ "version": "1.1.367",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "./dist/lib/index.js",