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.
- package/machinery/compiler3.js +119 -90
- package/package.json +1 -1
package/machinery/compiler3.js
CHANGED
|
@@ -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
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
|