juxscript 1.1.92 → 1.1.94

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "totalComponents": 63,
3
- "generatedAt": "2026-02-12T19:55:15.526Z",
3
+ "generatedAt": "2026-02-12T20:03:25.609Z",
4
4
  "components": [
5
5
  {
6
6
  "file": "alert.js",
@@ -202,180 +202,6 @@ export class JuxCompiler {
202
202
  return issues;
203
203
  }
204
204
 
205
- /**
206
- * ✅ Generate routes based on folder structure
207
- */
208
- _generateRouter(views) {
209
- let routeMap = '';
210
-
211
- views.forEach(v => {
212
- // ✅ Generate route from folder structure
213
- // abc/juxabc.jux -> /abc/juxabc
214
- // index.jux -> /
215
- // abc/index.jux -> /abc
216
-
217
- const routePath = this._generateRoutePath(v.file);
218
- const functionName = this._generateFunctionName(v.name);
219
-
220
- routeMap += ` '${routePath}': render${functionName},\n`;
221
- });
222
-
223
- return `
224
- // --- JUX SOURCE LOADER ---
225
- var __juxSources = null;
226
- async function __juxLoadSources() {
227
- if (__juxSources) return __juxSources;
228
- try {
229
- var res = await fetch('/__jux_sources.json');
230
- __juxSources = await res.json();
231
- } catch (e) {
232
- __juxSources = {};
233
- }
234
- return __juxSources;
235
- }
236
-
237
- function __juxFindSource(stack) {
238
- var match = stack.match(/render([A-Z][a-zA-Z0-9_]*)/);
239
- if (match) {
240
- var funcName = match[1];
241
- for (var file in __juxSources || {}) {
242
- var normalized = __juxSources[file].name
243
- .split('_')
244
- .map(s => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase())
245
- .join('');
246
-
247
- if (normalized === funcName) {
248
- return { file: file, source: __juxSources[file], viewName: funcName };
249
- }
250
- }
251
- }
252
- return null;
253
- }
254
-
255
- // --- JUX RUNTIME ERROR OVERLAY ---
256
- var __juxErrorOverlay = {
257
- styles: \`
258
- #__jux-error-overlay {
259
- position: fixed; inset: 0; z-index: 99999;
260
- background: rgba(0, 0, 0, 0.4);
261
- display: flex; align-items: center; justify-content: center;
262
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
263
- opacity: 0; transition: opacity 0.2s ease-out; backdrop-filter: blur(2px);
264
- }
265
- #__jux-error-overlay.visible { opacity: 1; }
266
- #__jux-error-overlay * { box-sizing: border-box; }
267
- .__jux-modal {
268
- background: #f8f9fa; border-radius: 4px;
269
- box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.4);
270
- max-width: 80vw; width: 90%; max-height: 90vh;
271
- overflow: hidden; display: flex; flex-direction: column;
272
- transform: translateY(10px); transition: transform 0.2s ease-out;
273
- }
274
- #__jux-error-overlay.visible .__jux-modal { transform: translateY(0); }
275
- .__jux-header { background: #fff; padding: 20px 24px; border-bottom: 1px solid #e5e7eb; }
276
- .__jux-header h3 { margin: 0 0 6px; font-weight: 600; font-size: 11px; color: #9ca3af; text-transform: uppercase; }
277
- .__jux-header h1 { margin: 0 0 8px; font-size: 18px; font-weight: 600; color: #dc2626; line-height: 1.3; }
278
- .__jux-header .file-info { color: #6b7280; font-size: 13px; }
279
- .__jux-header .file-info strong { color: #dc2626; font-weight: 600; }
280
- .__jux-source { padding: 16px 24px; overflow: auto; flex: 1; background: #f8f9fa; }
281
- .__jux-code { background: #fff; border-radius: 8px; overflow: hidden; border: 1px solid #e5e7eb; }
282
- .__jux-code-line { display: flex; font-size: 13px; line-height: 1.7; }
283
- .__jux-code-line.error { background: #fef2f2; }
284
- .__jux-code-line.error .__jux-line-code { color: #dc2626; font-weight: 500; }
285
- .__jux-code-line.context { background: #fefce8; }
286
- .__jux-line-num { min-width: 44px; padding: 4px 12px; text-align: right; color: #9ca3af; background: #f9fafb; border-right: 1px solid #e5e7eb; font-size: 12px; }
287
- .__jux-code-line.error .__jux-line-num { background: #fef2f2; color: #dc2626; }
288
- .__jux-line-code { flex: 1; padding: 4px 16px; color: #374151; white-space: pre; overflow-x: auto; }
289
- .__jux-footer { padding: 16px 24px; background: #fff; border-top: 1px solid #e5e7eb; display: flex; justify-content: space-between; align-items: center; }
290
- .__jux-tip { color: #6b7280; font-size: 12px; }
291
- .__jux-dismiss { background: #f3f4f6; color: #374151; border: 1px solid #d1d5db; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; }
292
- .__jux-dismiss:hover { background: #e5e7eb; }
293
- .__jux-no-source { color: #6b7280; padding: 24px; text-align: center; }
294
- .__jux-no-source pre { background: #fff; padding: 16px; border-radius: 8px; margin-top: 16px; font-size: 11px; color: #6b7280; overflow-x: auto; text-align: left; border: 1px solid #e5e7eb; }
295
- \`,
296
-
297
- show: async function(error, title) {
298
- title = title || 'Runtime Error';
299
- var existing = document.getElementById('__jux-error-overlay');
300
- if (existing) existing.remove();
301
- await __juxLoadSources();
302
-
303
- var overlay = document.createElement('div');
304
- overlay.id = '__jux-error-overlay';
305
- var stack = error && error.stack ? error.stack : '';
306
- var msg = error && error.message ? error.message : String(error);
307
- var found = __juxFindSource(stack);
308
- var sourceHtml = '', fileInfo = '';
309
-
310
- if (found && found.source && found.source.lines) {
311
- var lines = found.source.lines;
312
- fileInfo = '<span class="file-info">in <strong>' + found.file + '</strong></span>';
313
- var errorLineIndex = -1;
314
- var errorMethod = msg.match(/\\.([a-zA-Z]+)\\s*is not a function/);
315
- if (errorMethod) {
316
- for (var i = 0; i < lines.length; i++) {
317
- if (lines[i].indexOf('.' + errorMethod[1]) > -1) { errorLineIndex = i; break; }
318
- }
319
- }
320
- if (errorLineIndex === -1) {
321
- for (var i = 0; i < lines.length; i++) {
322
- if (lines[i].indexOf('throw ') > -1) { errorLineIndex = i; break; }
323
- }
324
- }
325
- var contextStart = Math.max(0, errorLineIndex - 3);
326
- var contextEnd = Math.min(lines.length - 1, errorLineIndex + 5);
327
- if (errorLineIndex === -1) { contextStart = 0; contextEnd = Math.min(14, lines.length - 1); }
328
-
329
- for (var i = contextStart; i <= contextEnd; i++) {
330
- var isError = (i === errorLineIndex);
331
- var isContext = !isError && errorLineIndex > -1 && Math.abs(i - errorLineIndex) <= 2;
332
- var lineClass = isError ? ' error' : (isContext ? ' context' : '');
333
- var lineCode = lines[i].replace(/</g, '&lt;').replace(/>/g, '&gt;') || ' ';
334
- sourceHtml += '<div class="__jux-code-line' + lineClass + '"><span class="__jux-line-num">' + (i + 1) + '</span><span class="__jux-line-code">' + lineCode + '</span></div>';
335
- }
336
- } else {
337
- sourceHtml = '<div class="__jux-no-source"><p>Could not locate source file.</p><pre>' + stack + '</pre></div>';
338
- }
339
-
340
- overlay.innerHTML = '<style>' + this.styles + '</style><div class="__jux-modal"><div class="__jux-header"><h3>' + title + '</h3><h1>' + msg + '</h1>' + fileInfo + '</div><div class="__jux-source"><div class="__jux-code">' + sourceHtml + '</div></div><div class="__jux-footer"><span class="__jux-tip">💡 Fix the error and save to reload</span><button class="__jux-dismiss" onclick="document.getElementById(\\'__jux-error-overlay\\').remove()">Dismiss</button></div></div>';
341
- document.body.appendChild(overlay);
342
- requestAnimationFrame(function() { overlay.classList.add('visible'); });
343
- console.error(title + ':', error);
344
- }
345
- };
346
-
347
- window.addEventListener('error', function(e) { __juxErrorOverlay.show(e.error || new Error(e.message), 'Uncaught Error'); }, true);
348
- window.addEventListener('unhandledrejection', function(e) { __juxErrorOverlay.show(e.reason || new Error('Promise rejected'), 'Unhandled Promise Rejection'); }, true);
349
-
350
- // --- JUX ROUTER ---
351
- const routes = {\n${routeMap}};
352
-
353
- async function navigate(path) {
354
- const view = routes[path];
355
- if (!view) {
356
- document.getElementById('app').innerHTML = '<h1 style="padding:40px;">404 - Not Found</h1>';
357
- return;
358
- }
359
- document.getElementById('app').innerHTML = '';
360
- var overlay = document.getElementById('__jux-error-overlay');
361
- if (overlay) overlay.remove();
362
- try { await view(); } catch (err) { __juxErrorOverlay.show(err, 'Jux Render Error'); }
363
- }
364
-
365
- document.addEventListener('click', e => {
366
- const a = e.target.closest('a');
367
- if (!a || a.dataset.router === 'false') return;
368
- try { if (new URL(a.href, location.origin).origin !== location.origin) return; } catch { return; }
369
- e.preventDefault();
370
- history.pushState({}, '', a.href);
371
- navigate(new URL(a.href, location.origin).pathname);
372
- });
373
-
374
- window.addEventListener('popstate', () => navigate(location.pathname));
375
- navigate(location.pathname);
376
- `;
377
- }
378
-
379
205
  /**
380
206
  * ✅ Generate route path from file path
381
207
  * Examples:
@@ -483,32 +309,14 @@ navigate(location.pathname);
483
309
  return entry;
484
310
  }
485
311
 
486
- reportValidationIssues() {
487
- const issues = this._validationIssues || [];
488
- const errors = issues.filter(i => i.type === 'error');
489
- const warnings = issues.filter(i => i.type === 'warning');
490
-
491
- if (issues.length > 0) {
492
- console.log('\n⚠️ Validation Issues:\n');
493
- issues.forEach(issue => {
494
- const icon = issue.type === 'error' ? '❌' : '⚠️';
495
- console.log(`${icon} [${issue.view}:${issue.line}] ${issue.message}`);
496
- });
497
- console.log('');
498
- }
499
-
500
- return { isValid: errors.length === 0, errors, warnings };
501
- }
502
-
312
+ /**
313
+ * Generate routes based on folder structure
314
+ */
503
315
  _generateRouter(views) {
504
316
  let routeMap = '';
505
317
 
506
318
  views.forEach(v => {
507
319
  // ✅ Generate route from folder structure
508
- // abc/juxabc.jux -> /abc/juxabc
509
- // index.jux -> /
510
- // abc/index.jux -> /abc
511
-
512
320
  const routePath = this._generateRoutePath(v.file);
513
321
  const functionName = this._generateFunctionName(v.name);
514
322
 
@@ -643,7 +451,8 @@ window.addEventListener('error', function(e) { __juxErrorOverlay.show(e.error ||
643
451
  window.addEventListener('unhandledrejection', function(e) { __juxErrorOverlay.show(e.reason || new Error('Promise rejected'), 'Unhandled Promise Rejection'); }, true);
644
452
 
645
453
  // --- JUX ROUTER ---
646
- const routes = {\n${routeMap}};
454
+ const routes = {
455
+ ${routeMap}};
647
456
 
648
457
  async function navigate(path) {
649
458
  const view = routes[path];
@@ -671,6 +480,93 @@ navigate(location.pathname);
671
480
  `;
672
481
  }
673
482
 
483
+ /**
484
+ * ✅ Build method - INSIDE the class
485
+ */
486
+ async build() {
487
+ console.log('🔨 Building JUX application...\n');
488
+
489
+ try {
490
+ const startTime = Date.now();
491
+
492
+ // Scan files
493
+ const { views, dataModules, sharedModules } = this.scanFiles();
494
+
495
+ console.log(`📂 Found ${views.length} views, ${dataModules.length} data modules, ${sharedModules.length} shared modules\n`);
496
+
497
+ // Load juxscript exports for validation
498
+ await this.loadJuxscriptExports();
499
+
500
+ // Ensure dist directory exists
501
+ if (!fs.existsSync(this.distDir)) {
502
+ fs.mkdirSync(this.distDir, { recursive: true });
503
+ }
504
+
505
+ // Generate entry point
506
+ const entryCode = this.generateEntryPoint(views, dataModules, sharedModules);
507
+ const entryPath = path.join(this.distDir, 'entry.js');
508
+ fs.writeFileSync(entryPath, entryCode);
509
+
510
+ // Bundle with esbuild
511
+ console.log('📦 Bundling with esbuild...');
512
+ await esbuild.build({
513
+ entryPoints: [entryPath],
514
+ bundle: true,
515
+ format: 'esm',
516
+ outfile: path.join(this.distDir, 'bundle.js'),
517
+ platform: 'browser',
518
+ target: 'es2020',
519
+ sourcemap: true,
520
+ external: []
521
+ });
522
+
523
+ // Generate index.html
524
+ console.log('📄 Generating index.html...');
525
+ const indexHtml = `<!DOCTYPE html>
526
+ <html lang="en">
527
+ <head>
528
+ <meta charset="UTF-8">
529
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
530
+ <title>JUX App</title>
531
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/juxscript@latest/lib/layouts/default.css">
532
+ </head>
533
+ <body>
534
+ <div id="app"></div>
535
+ <script type="module" src="/bundle.js"></script>
536
+ </body>
537
+ </html>`;
538
+
539
+ fs.writeFileSync(path.join(this.distDir, 'index.html'), indexHtml);
540
+
541
+ // Copy public folder
542
+ this.copyPublicFolder();
543
+
544
+ // Write source snapshot for error overlay
545
+ const snapshotPath = path.join(this.distDir, '__jux_sources.json');
546
+ fs.writeFileSync(snapshotPath, JSON.stringify(this._sourceSnapshot, null, 2));
547
+
548
+ const endTime = Date.now();
549
+ console.log(`\n✅ Build complete in ${endTime - startTime}ms`);
550
+ console.log(` Output: ${this.distDir}`);
551
+
552
+ this.reportValidationIssues();
553
+
554
+ return {
555
+ success: true,
556
+ errors: this._validationIssues || [],
557
+ distDir: this.distDir
558
+ };
559
+
560
+ } catch (err) {
561
+ console.error('❌ Build failed:', err.message);
562
+ console.error(err.stack);
563
+ return {
564
+ success: false,
565
+ errors: [err.message]
566
+ };
567
+ }
568
+ }
569
+
674
570
  /**
675
571
  * Copy public folder contents to dist
676
572
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.92",
3
+ "version": "1.1.94",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",