juxscript 1.1.154 → 1.1.155
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/compiler4.js +6 -0
- package/machinery/errors.js +184 -21
- package/package.json +1 -1
package/machinery/compiler4.js
CHANGED
|
@@ -508,6 +508,12 @@ export class JuxCompiler {
|
|
|
508
508
|
fs.writeFileSync(snapshotPath, JSON.stringify(this._sourceSnapshot, null, 2));
|
|
509
509
|
console.log(`📸 Source snapshot written`);
|
|
510
510
|
|
|
511
|
+
// ✅ Also write to dist root so the error overlay can fetch it via /__jux_sources.json
|
|
512
|
+
const distSnapshotPath = path.join(this.config.distDir, '__jux_sources.json');
|
|
513
|
+
if (distSnapshotPath !== snapshotPath) {
|
|
514
|
+
fs.writeFileSync(distSnapshotPath, JSON.stringify(this._sourceSnapshot, null, 2));
|
|
515
|
+
}
|
|
516
|
+
|
|
511
517
|
const validation = this.reportValidationIssues();
|
|
512
518
|
if (!validation.isValid) {
|
|
513
519
|
console.log('🛑 BUILD FAILED\n');
|
package/machinery/errors.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generates an injectable client-side error collector script.
|
|
3
3
|
* Full-screen overlay similar to Vite's error screen.
|
|
4
|
+
* Resolves bundle line numbers back to original .jux source using __jux_sources.json.
|
|
4
5
|
*
|
|
5
6
|
* @param {Object} options
|
|
6
7
|
* @param {boolean} [options.enabled=true]
|
|
@@ -22,6 +23,114 @@ export function generateErrorCollector(options = {}) {
|
|
|
22
23
|
var __maxErrors = ${maxErrors};
|
|
23
24
|
var __overlay = null;
|
|
24
25
|
var __list = null;
|
|
26
|
+
var __sources = null;
|
|
27
|
+
var __sourcesFetched = false;
|
|
28
|
+
var __bundleLines = null;
|
|
29
|
+
|
|
30
|
+
// Fetch source map + bundle on first error
|
|
31
|
+
function loadSources(cb) {
|
|
32
|
+
if (__sourcesFetched) return cb();
|
|
33
|
+
__sourcesFetched = true;
|
|
34
|
+
|
|
35
|
+
var done = 0;
|
|
36
|
+
function check() { if (++done >= 2) cb(); }
|
|
37
|
+
|
|
38
|
+
// Fetch source snapshot
|
|
39
|
+
var xhr1 = new XMLHttpRequest();
|
|
40
|
+
xhr1.open('GET', '/__jux_sources.json', true);
|
|
41
|
+
xhr1.onload = function() {
|
|
42
|
+
try { __sources = JSON.parse(xhr1.responseText); } catch(e) {}
|
|
43
|
+
check();
|
|
44
|
+
};
|
|
45
|
+
xhr1.onerror = check;
|
|
46
|
+
xhr1.send();
|
|
47
|
+
|
|
48
|
+
// Fetch bundle to split into lines
|
|
49
|
+
var xhr2 = new XMLHttpRequest();
|
|
50
|
+
xhr2.open('GET', '/bundle.js', true);
|
|
51
|
+
xhr2.onload = function() {
|
|
52
|
+
try { __bundleLines = xhr2.responseText.split('\\n'); } catch(e) {}
|
|
53
|
+
check();
|
|
54
|
+
};
|
|
55
|
+
xhr2.onerror = check;
|
|
56
|
+
xhr2.send();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Given a bundle line number, find which view function it belongs to
|
|
60
|
+
// and map back to the original .jux source line
|
|
61
|
+
function resolveSourceLocation(bundleLine) {
|
|
62
|
+
if (!__sources || !__bundleLines || !bundleLine) return null;
|
|
63
|
+
|
|
64
|
+
// Find the renderJuxN function that contains this line
|
|
65
|
+
// by scanning backwards from bundleLine for "async function renderJux"
|
|
66
|
+
var funcName = null;
|
|
67
|
+
var funcStartLine = 0;
|
|
68
|
+
for (var i = bundleLine - 1; i >= 0; i--) {
|
|
69
|
+
var line = __bundleLines[i];
|
|
70
|
+
if (!line) continue;
|
|
71
|
+
var match = line.match(/async\\s+function\\s+(renderJux\\d+)/);
|
|
72
|
+
if (match) {
|
|
73
|
+
funcName = match[1];
|
|
74
|
+
funcStartLine = i + 1; // 1-based
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!funcName) return null;
|
|
80
|
+
|
|
81
|
+
// Find the source entry that maps to this function
|
|
82
|
+
var sourceEntry = null;
|
|
83
|
+
for (var key in __sources) {
|
|
84
|
+
if (__sources[key].functionName === funcName) {
|
|
85
|
+
sourceEntry = __sources[key];
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!sourceEntry) return null;
|
|
91
|
+
|
|
92
|
+
// Calculate approximate original line
|
|
93
|
+
// bundleLine is inside the function body, funcStartLine is the function declaration
|
|
94
|
+
// +1 because function body starts after the declaration line
|
|
95
|
+
var offsetInFunc = bundleLine - funcStartLine - 1;
|
|
96
|
+
var originalLines = sourceEntry.lines || [];
|
|
97
|
+
var approxLine = Math.max(0, Math.min(offsetInFunc, originalLines.length - 1));
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
file: sourceEntry.file,
|
|
101
|
+
name: sourceEntry.name,
|
|
102
|
+
functionName: funcName,
|
|
103
|
+
originalLine: approxLine + 1,
|
|
104
|
+
originalCode: originalLines[approxLine] || '',
|
|
105
|
+
// Provide surrounding context (3 lines before/after)
|
|
106
|
+
context: originalLines.slice(
|
|
107
|
+
Math.max(0, approxLine - 3),
|
|
108
|
+
Math.min(originalLines.length, approxLine + 4)
|
|
109
|
+
),
|
|
110
|
+
contextStartLine: Math.max(0, approxLine - 3) + 1,
|
|
111
|
+
highlightLine: approxLine + 1
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Parse stack trace and extract bundle line numbers
|
|
116
|
+
function parseStack(stack) {
|
|
117
|
+
if (!stack) return [];
|
|
118
|
+
var frames = [];
|
|
119
|
+
var lines = stack.split('\\n');
|
|
120
|
+
for (var i = 0; i < lines.length; i++) {
|
|
121
|
+
// Match patterns like "at funcName (url:line:col)" or "at url:line:col"
|
|
122
|
+
var m = lines[i].match(/(?:at\\s+(.+?)\\s+\\()?(?:https?:\\/\\/[^:]+):([\\d]+):([\\d]+)\\)?/);
|
|
123
|
+
if (m) {
|
|
124
|
+
frames.push({
|
|
125
|
+
raw: lines[i].trim(),
|
|
126
|
+
func: m[1] || '(anonymous)',
|
|
127
|
+
line: parseInt(m[2], 10),
|
|
128
|
+
col: parseInt(m[3], 10)
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return frames;
|
|
133
|
+
}
|
|
25
134
|
|
|
26
135
|
function createOverlay() {
|
|
27
136
|
__overlay = document.createElement('div');
|
|
@@ -53,7 +162,6 @@ export function generateErrorCollector(options = {}) {
|
|
|
53
162
|
'background:#1a1a1a'
|
|
54
163
|
].join(';'));
|
|
55
164
|
|
|
56
|
-
// Title bar
|
|
57
165
|
var header = document.createElement('div');
|
|
58
166
|
header.setAttribute('style', [
|
|
59
167
|
'display:flex',
|
|
@@ -120,6 +228,38 @@ export function generateErrorCollector(options = {}) {
|
|
|
120
228
|
if (__overlay) __overlay.style.display = 'none';
|
|
121
229
|
}
|
|
122
230
|
|
|
231
|
+
function renderSourceContext(resolved) {
|
|
232
|
+
if (!resolved) return '';
|
|
233
|
+
|
|
234
|
+
var lines = resolved.context || [];
|
|
235
|
+
var startLine = resolved.contextStartLine || 1;
|
|
236
|
+
var highlight = resolved.highlightLine || -1;
|
|
237
|
+
|
|
238
|
+
var html = '<div style="margin-top:8px;padding:8px;background:#111;border:1px solid #333;border-radius:4px;">';
|
|
239
|
+
html += '<div style="color:#ff5555;font-size:12px;margin-bottom:6px;font-weight:bold;">' +
|
|
240
|
+
escapeHtml(resolved.file) + ':' + resolved.originalLine +
|
|
241
|
+
'</div>';
|
|
242
|
+
|
|
243
|
+
for (var i = 0; i < lines.length; i++) {
|
|
244
|
+
var lineNum = startLine + i;
|
|
245
|
+
var isHighlight = lineNum === highlight;
|
|
246
|
+
var bg = isHighlight ? 'background:#3a1a1a;' : '';
|
|
247
|
+
var color = isHighlight ? 'color:#ff8888;' : 'color:#888;';
|
|
248
|
+
var numColor = isHighlight ? 'color:#ff5555;' : 'color:#555;';
|
|
249
|
+
var marker = isHighlight ? '>' : ' ';
|
|
250
|
+
|
|
251
|
+
html += '<div style="' + bg + 'padding:1px 8px;white-space:pre;">' +
|
|
252
|
+
'<span style="' + numColor + 'display:inline-block;width:40px;text-align:right;margin-right:12px;user-select:none;">' +
|
|
253
|
+
marker + ' ' + lineNum +
|
|
254
|
+
'</span>' +
|
|
255
|
+
'<span style="' + color + '">' + escapeHtml(lines[i]) + '</span>' +
|
|
256
|
+
'</div>';
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
html += '</div>';
|
|
260
|
+
return html;
|
|
261
|
+
}
|
|
262
|
+
|
|
123
263
|
function addError(type, message, source, line, col, stack) {
|
|
124
264
|
if (__errors.length >= __maxErrors) __errors.shift();
|
|
125
265
|
|
|
@@ -148,34 +288,57 @@ export function generateErrorCollector(options = {}) {
|
|
|
148
288
|
|
|
149
289
|
var typeLabel = type === 'error' ? 'Error' : type === 'rejection' ? 'Unhandled Rejection' : 'console.error';
|
|
150
290
|
|
|
151
|
-
var meta = entry.source ? entry.source + ':' + entry.line + ':' + entry.col : '';
|
|
152
|
-
|
|
153
291
|
item.innerHTML =
|
|
154
292
|
'<div style="color:#888;font-size:12px;margin-bottom:4px;">' +
|
|
155
|
-
typeLabel +
|
|
293
|
+
typeLabel +
|
|
156
294
|
'<span style="float:right;">' + entry.time + '</span>' +
|
|
157
295
|
'</div>' +
|
|
158
|
-
'<div style="color:#ff8888;white-space:pre-wrap;word-break:break-word;">' +
|
|
296
|
+
'<div style="color:#ff8888;font-size:14px;white-space:pre-wrap;word-break:break-word;font-weight:bold;">' +
|
|
159
297
|
escapeHtml(entry.message) +
|
|
160
|
-
'</div>'
|
|
161
|
-
(entry.stack ?
|
|
162
|
-
'<pre style="margin:8px 0 0;padding:8px;font-size:12px;color:#888;' +
|
|
163
|
-
'white-space:pre-wrap;background:#1a1a1a;border-radius:4px;' +
|
|
164
|
-
'border:1px solid #333;max-height:200px;overflow-y:auto;">' +
|
|
165
|
-
escapeHtml(cleanStack(entry.stack)) +
|
|
166
|
-
'</pre>'
|
|
167
|
-
: '');
|
|
298
|
+
'</div>';
|
|
168
299
|
|
|
169
300
|
__list.appendChild(item);
|
|
170
|
-
}
|
|
171
301
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
302
|
+
// Resolve source location asynchronously
|
|
303
|
+
loadSources(function() {
|
|
304
|
+
var frames = parseStack(entry.stack);
|
|
305
|
+
var resolved = null;
|
|
306
|
+
|
|
307
|
+
// Try each frame until we find one that maps to a .jux source
|
|
308
|
+
for (var i = 0; i < frames.length; i++) {
|
|
309
|
+
var r = resolveSourceLocation(frames[i].line);
|
|
310
|
+
if (r) {
|
|
311
|
+
resolved = r;
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Also try the direct line/col from the error itself
|
|
317
|
+
if (!resolved && entry.line) {
|
|
318
|
+
resolved = resolveSourceLocation(entry.line);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (resolved) {
|
|
322
|
+
// Add source context block
|
|
323
|
+
var sourceBlock = document.createElement('div');
|
|
324
|
+
sourceBlock.innerHTML = renderSourceContext(resolved);
|
|
325
|
+
item.appendChild(sourceBlock);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Add raw stack (collapsed) if present
|
|
329
|
+
if (entry.stack) {
|
|
330
|
+
var stackBlock = document.createElement('details');
|
|
331
|
+
stackBlock.setAttribute('style', 'margin-top:8px;');
|
|
332
|
+
stackBlock.innerHTML =
|
|
333
|
+
'<summary style="cursor:pointer;color:#555;font-size:12px;user-select:none;">Raw stack trace</summary>' +
|
|
334
|
+
'<pre style="margin:4px 0 0;padding:8px;font-size:12px;color:#666;' +
|
|
335
|
+
'white-space:pre-wrap;background:#111;border-radius:4px;' +
|
|
336
|
+
'border:1px solid #333;max-height:200px;overflow-y:auto;">' +
|
|
337
|
+
escapeHtml(entry.stack) +
|
|
338
|
+
'</pre>';
|
|
339
|
+
item.appendChild(stackBlock);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
179
342
|
}
|
|
180
343
|
|
|
181
344
|
function escapeHtml(str) {
|