juxscript 1.0.95 → 1.0.96

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.
@@ -215,97 +215,16 @@ export class JuxCompiler {
215
215
  }
216
216
 
217
217
  generateEntryPoint(views, dataModules, sharedModules) {
218
- let entry = `// Auto-generated JUX entry point\n\n`;
219
218
  const allIssues = [];
220
219
  const sourceSnapshot = {};
221
220
 
222
- const juxImports = new Set();
223
- [...views, ...dataModules, ...sharedModules].forEach(m => {
224
- for (const match of m.content.matchAll(/import\s*\{\s*([^}]+)\s*\}\s*from\s*['"]juxscript['"]/g)) {
225
- match[1].split(',').map(s => s.trim()).forEach(imp => {
226
- if (imp) juxImports.add(imp);
227
- });
228
- }
229
- });
230
-
231
- if (juxImports.size > 0) {
232
- entry += `import { ${[...juxImports].sort().join(', ')} } from 'juxscript';\n\n`;
233
- }
234
-
235
- dataModules.forEach(m => {
236
- entry += `import * as ${this.sanitizeName(m.name)}Data from './jux/${m.file}';\n`;
237
- });
238
- sharedModules.forEach(m => {
239
- entry += `import * as ${this.sanitizeName(m.name)}Shared from './jux/${m.file}';\n`;
240
- });
241
-
242
- entry += `\n// Expose to window\n`;
243
- dataModules.forEach(m => entry += `Object.assign(window, ${this.sanitizeName(m.name)}Data);\n`);
244
- sharedModules.forEach(m => entry += `Object.assign(window, ${this.sanitizeName(m.name)}Shared);\n`);
221
+ // ═══════════════════════════════════════════════════════════════
222
+ // STEP 1: ERROR OVERLAY FIRST (before any imports!)
223
+ // This ensures module-level errors are caught
224
+ // ═══════════════════════════════════════════════════════════════
225
+ let entry = `// Auto-generated JUX entry point
245
226
 
246
- if (juxImports.size > 0) {
247
- entry += `\nObject.assign(window, { ${[...juxImports].join(', ')} });\n`;
248
- }
249
-
250
- entry += `\n// --- VIEW FUNCTIONS ---\n`;
251
-
252
- views.forEach(v => {
253
- const capitalized = v.name.charAt(0).toUpperCase() + v.name.slice(1);
254
- allIssues.push(...this.validateViewCode(v.name, v.content));
255
-
256
- sourceSnapshot[v.file] = {
257
- name: v.name,
258
- file: v.file,
259
- content: v.content,
260
- lines: v.content.split('\n')
261
- };
262
-
263
- let viewCode = this.removeImports(v.content).replace(/^\s*export\s+default\s+.*$/gm, '');
264
- const asyncPrefix = viewCode.includes('await ') ? 'async ' : '';
265
-
266
- entry += `\n${asyncPrefix}function render${capitalized}() {\n${viewCode}\n}\n`;
267
- });
268
-
269
- dataModules.forEach(m => {
270
- sourceSnapshot[m.file] = { name: m.name, file: m.file, content: m.content, lines: m.content.split('\n') };
271
- });
272
- sharedModules.forEach(m => {
273
- sourceSnapshot[m.file] = { name: m.name, file: m.file, content: m.content, lines: m.content.split('\n') };
274
- });
275
-
276
- this._sourceSnapshot = sourceSnapshot;
277
- this._validationIssues = allIssues;
278
- entry += this._generateRouter(views);
279
- return entry;
280
- }
281
-
282
- reportValidationIssues() {
283
- const issues = this._validationIssues || [];
284
- const errors = issues.filter(i => i.type === 'error');
285
- const warnings = issues.filter(i => i.type === 'warning');
286
-
287
- if (issues.length > 0) {
288
- console.log('\n⚠️ Validation Issues:\n');
289
- issues.forEach(issue => {
290
- const icon = issue.type === 'error' ? '❌' : '⚠️';
291
- console.log(`${icon} [${issue.view}:${issue.line}] ${issue.message}`);
292
- });
293
- console.log('');
294
- }
295
-
296
- return { isValid: errors.length === 0, errors, warnings };
297
- }
298
-
299
- _generateRouter(views) {
300
- let routeMap = '';
301
- views.forEach(v => {
302
- const cap = v.name.charAt(0).toUpperCase() + v.name.slice(1);
303
- if (v.name.toLowerCase() === 'index') routeMap += ` '/': render${cap},\n`;
304
- routeMap += ` '/${v.name.toLowerCase()}': render${cap},\n`;
305
- });
306
-
307
- return `
308
- // --- JUX SOURCE LOADER ---
227
+ // --- JUX SOURCE LOADER (must be first) ---
309
228
  var __juxSources = null;
310
229
  async function __juxLoadSources() {
311
230
  if (__juxSources) return __juxSources;
@@ -318,7 +237,6 @@ async function __juxLoadSources() {
318
237
  return __juxSources;
319
238
  }
320
239
 
321
- // Find source file from error stack
322
240
  function __juxFindSource(stack) {
323
241
  var match = stack.match(/render(\\w+)/);
324
242
  if (match) {
@@ -329,6 +247,12 @@ function __juxFindSource(stack) {
329
247
  }
330
248
  }
331
249
  }
250
+ // Also check for module names in stack
251
+ for (var file in __juxSources || {}) {
252
+ if (stack.indexOf(file) > -1 || stack.indexOf(__juxSources[file].name) > -1) {
253
+ return { file: file, source: __juxSources[file] };
254
+ }
255
+ }
332
256
  return null;
333
257
  }
334
258
 
@@ -492,7 +416,6 @@ var __juxErrorOverlay = {
492
416
 
493
417
  document.body.appendChild(overlay);
494
418
 
495
- // Trigger transition
496
419
  requestAnimationFrame(function() {
497
420
  overlay.classList.add('visible');
498
421
  });
@@ -501,7 +424,10 @@ var __juxErrorOverlay = {
501
424
  }
502
425
  };
503
426
 
504
- // Global error handlers
427
+ // ═══════════════════════════════════════════════════════════════
428
+ // INSTALL GLOBAL ERROR HANDLERS IMMEDIATELY (before imports!)
429
+ // This catches errors during module loading
430
+ // ═══════════════════════════════════════════════════════════════
505
431
  window.addEventListener('error', function(e) {
506
432
  __juxErrorOverlay.show(e.error || new Error(e.message), 'Uncaught Error');
507
433
  }, true);
@@ -510,6 +436,109 @@ window.addEventListener('unhandledrejection', function(e) {
510
436
  __juxErrorOverlay.show(e.reason || new Error('Promise rejected'), 'Unhandled Promise Rejection');
511
437
  }, true);
512
438
 
439
+ // ═══════════════════════════════════════════════════════════════
440
+ // NOW SAFE TO IMPORT MODULES
441
+ // ═══════════════════════════════════════════════════════════════
442
+
443
+ `;
444
+
445
+ // ═══════════════════════════════════════════════════════════════
446
+ // STEP 2: Collect and add imports
447
+ // ═══════════════════════════════════════════════════════════════
448
+ const juxImports = new Set();
449
+ [...views, ...dataModules, ...sharedModules].forEach(m => {
450
+ for (const match of m.content.matchAll(/import\s*\{\s*([^}]+)\s*\}\s*from\s*['"]juxscript['"]/g)) {
451
+ match[1].split(',').map(s => s.trim()).forEach(imp => {
452
+ if (imp) juxImports.add(imp);
453
+ });
454
+ }
455
+ });
456
+
457
+ if (juxImports.size > 0) {
458
+ entry += `import { ${[...juxImports].sort().join(', ')} } from 'juxscript';\n\n`;
459
+ }
460
+
461
+ dataModules.forEach(m => {
462
+ entry += `import * as ${this.sanitizeName(m.name)}Data from './jux/${m.file}';\n`;
463
+ });
464
+ sharedModules.forEach(m => {
465
+ entry += `import * as ${this.sanitizeName(m.name)}Shared from './jux/${m.file}';\n`;
466
+ });
467
+
468
+ entry += `\n// Expose to window\n`;
469
+ dataModules.forEach(m => entry += `Object.assign(window, ${this.sanitizeName(m.name)}Data);\n`);
470
+ sharedModules.forEach(m => entry += `Object.assign(window, ${this.sanitizeName(m.name)}Shared);\n`);
471
+
472
+ if (juxImports.size > 0) {
473
+ entry += `\nObject.assign(window, { ${[...juxImports].join(', ')} });\n`;
474
+ }
475
+
476
+ // ═══════════════════════════════════════════════════════════════
477
+ // STEP 3: View functions
478
+ // ═══════════════════════════════════════════════════════════════
479
+ entry += `\n// --- VIEW FUNCTIONS ---\n`;
480
+
481
+ views.forEach(v => {
482
+ const capitalized = v.name.charAt(0).toUpperCase() + v.name.slice(1);
483
+ allIssues.push(...this.validateViewCode(v.name, v.content));
484
+
485
+ sourceSnapshot[v.file] = {
486
+ name: v.name,
487
+ file: v.file,
488
+ content: v.content,
489
+ lines: v.content.split('\n')
490
+ };
491
+
492
+ let viewCode = this.removeImports(v.content).replace(/^\s*export\s+default\s+.*$/gm, '');
493
+ const asyncPrefix = viewCode.includes('await ') ? 'async ' : '';
494
+
495
+ entry += `\n${asyncPrefix}function render${capitalized}() {\n${viewCode}\n}\n`;
496
+ });
497
+
498
+ dataModules.forEach(m => {
499
+ sourceSnapshot[m.file] = { name: m.name, file: m.file, content: m.content, lines: m.content.split('\n') };
500
+ });
501
+ sharedModules.forEach(m => {
502
+ sourceSnapshot[m.file] = { name: m.name, file: m.file, content: m.content, lines: m.content.split('\n') };
503
+ });
504
+
505
+ this._sourceSnapshot = sourceSnapshot;
506
+ this._validationIssues = allIssues;
507
+
508
+ // ═══════════════════════════════════════════════════════════════
509
+ // STEP 4: Router (no error overlay code - it's already at top)
510
+ // ═══════════════════════════════════════════════════════════════
511
+ entry += this._generateRouter(views);
512
+ return entry;
513
+ }
514
+
515
+ reportValidationIssues() {
516
+ const issues = this._validationIssues || [];
517
+ const errors = issues.filter(i => i.type === 'error');
518
+ const warnings = issues.filter(i => i.type === 'warning');
519
+
520
+ if (issues.length > 0) {
521
+ console.log('\n⚠️ Validation Issues:\n');
522
+ issues.forEach(issue => {
523
+ const icon = issue.type === 'error' ? '❌' : '⚠️';
524
+ console.log(`${icon} [${issue.view}:${issue.line}] ${issue.message}`);
525
+ });
526
+ console.log('');
527
+ }
528
+
529
+ return { isValid: errors.length === 0, errors, warnings };
530
+ }
531
+
532
+ _generateRouter(views) {
533
+ let routeMap = '';
534
+ views.forEach(v => {
535
+ const cap = v.name.charAt(0).toUpperCase() + v.name.slice(1);
536
+ if (v.name.toLowerCase() === 'index') routeMap += ` '/': render${cap},\n`;
537
+ routeMap += ` '/${v.name.toLowerCase()}': render${cap},\n`;
538
+ });
539
+
540
+ // Router ONLY - error handlers are now at the TOP of the bundle
541
+ return `
513
542
  // --- JUX ROUTER ---
514
543
  const routes = {\n${routeMap}};
515
544
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.0.95",
3
+ "version": "1.0.96",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "./index.js",