juxscript 1.0.96 → 1.0.98
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 +90 -119
- package/package.json +1 -1
package/machinery/compiler3.js
CHANGED
|
@@ -215,16 +215,97 @@ export class JuxCompiler {
|
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
generateEntryPoint(views, dataModules, sharedModules) {
|
|
218
|
+
let entry = `// Auto-generated JUX entry point\n\n`;
|
|
218
219
|
const allIssues = [];
|
|
219
220
|
const sourceSnapshot = {};
|
|
220
221
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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`);
|
|
226
245
|
|
|
227
|
-
|
|
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 ---
|
|
228
309
|
var __juxSources = null;
|
|
229
310
|
async function __juxLoadSources() {
|
|
230
311
|
if (__juxSources) return __juxSources;
|
|
@@ -237,6 +318,7 @@ async function __juxLoadSources() {
|
|
|
237
318
|
return __juxSources;
|
|
238
319
|
}
|
|
239
320
|
|
|
321
|
+
// Find source file from error stack
|
|
240
322
|
function __juxFindSource(stack) {
|
|
241
323
|
var match = stack.match(/render(\\w+)/);
|
|
242
324
|
if (match) {
|
|
@@ -247,12 +329,6 @@ function __juxFindSource(stack) {
|
|
|
247
329
|
}
|
|
248
330
|
}
|
|
249
331
|
}
|
|
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
|
-
}
|
|
256
332
|
return null;
|
|
257
333
|
}
|
|
258
334
|
|
|
@@ -416,6 +492,7 @@ var __juxErrorOverlay = {
|
|
|
416
492
|
|
|
417
493
|
document.body.appendChild(overlay);
|
|
418
494
|
|
|
495
|
+
// Trigger transition
|
|
419
496
|
requestAnimationFrame(function() {
|
|
420
497
|
overlay.classList.add('visible');
|
|
421
498
|
});
|
|
@@ -424,10 +501,7 @@ var __juxErrorOverlay = {
|
|
|
424
501
|
}
|
|
425
502
|
};
|
|
426
503
|
|
|
427
|
-
//
|
|
428
|
-
// INSTALL GLOBAL ERROR HANDLERS IMMEDIATELY (before imports!)
|
|
429
|
-
// This catches errors during module loading
|
|
430
|
-
// ═══════════════════════════════════════════════════════════════
|
|
504
|
+
// Global error handlers
|
|
431
505
|
window.addEventListener('error', function(e) {
|
|
432
506
|
__juxErrorOverlay.show(e.error || new Error(e.message), 'Uncaught Error');
|
|
433
507
|
}, true);
|
|
@@ -436,109 +510,6 @@ window.addEventListener('unhandledrejection', function(e) {
|
|
|
436
510
|
__juxErrorOverlay.show(e.reason || new Error('Promise rejected'), 'Unhandled Promise Rejection');
|
|
437
511
|
}, true);
|
|
438
512
|
|
|
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 `
|
|
542
513
|
// --- JUX ROUTER ---
|
|
543
514
|
const routes = {\n${routeMap}};
|
|
544
515
|
|