lilact 0.26.14 → 0.26.16
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/dist/lilact.development.js +528 -291
- package/dist/lilact.development.js.map +3 -3
- package/dist/lilact.development.min.js +59 -50
- package/dist/lilact.development.min.js.map +3 -3
- package/dist/lilact.production.min.js +59 -50
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/functions/errors.globalErrorHandler.html +2 -8
- package/docs/functions/errors.mapLocation.html +1 -0
- package/docs/functions/errors.scanBlockLabels.html +2 -0
- package/docs/functions/errors.traceError.html +3 -7
- package/docs/functions/run.lazy.html +2 -13
- package/docs/functions/run.require.html +2 -9
- package/docs/functions/run.run.html +2 -5
- package/docs/functions/run.runScripts.html +1 -7
- package/docs/functions/timers.timeoutPromise.html +2 -2
- package/docs/modules/errors.html +1 -1
- package/docs/static/lilact.development.js +528 -291
- package/docs/static/lilact.development.js.map +3 -3
- package/docs/static/lilact.development.min.js +59 -50
- package/docs/static/lilact.development.min.js.map +3 -3
- package/docs/static/lilact.production.min.js +59 -50
- package/docs/variables/errors.blocks_info.html +1 -0
- package/docs/variables/errors.error.html +1 -0
- package/examples/lilact.development.js +528 -291
- package/examples/lilact.development.js.map +3 -3
- package/examples/lilact.development.min.js +59 -50
- package/examples/lilact.development.min.js.map +3 -3
- package/examples/lilact.production.min.js +59 -50
- package/package.json +1 -1
- package/scripts/esbuild-preprocessor-plugin.cjs +1 -0
- package/src/errors.jsx +525 -237
- package/src/jsx.js +4 -2
- package/src/lilact.jsx +1 -1
- package/src/run.jsx +347 -263
package/src/errors.jsx
CHANGED
|
@@ -27,273 +27,561 @@
|
|
|
27
27
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
28
28
|
|
|
29
29
|
*/
|
|
30
|
-
|
|
30
|
+
/*
|
|
31
|
+
Lilact
|
|
32
|
+
Copyright (C) 2024-2026 Arash Kazemi
|
|
33
|
+
BSD-2-Clause
|
|
34
|
+
*/
|
|
31
35
|
|
|
32
|
-
import
|
|
33
|
-
import {
|
|
36
|
+
import Lilact from "./lilact.jsx";
|
|
37
|
+
import { required_scripts } from "./run.jsx";
|
|
34
38
|
import { css } from "@emotion/css";
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
function numberOrNull(value) {
|
|
41
|
+
const n = Number(value);
|
|
42
|
+
return Number.isFinite(n) ? n : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function firstNumber(...values) {
|
|
46
|
+
for (const value of values) {
|
|
47
|
+
const n = numberOrNull(value);
|
|
48
|
+
if (n !== null) return n;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function asError(value) {
|
|
54
|
+
if (value instanceof Error) return value;
|
|
55
|
+
|
|
56
|
+
if (value && typeof value === "object") {
|
|
57
|
+
if (value.error instanceof Error) return value.error;
|
|
58
|
+
|
|
59
|
+
const error = new Error(
|
|
60
|
+
value.message == null ? String(value) : String(value.message)
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (value.name) error.name = value.name;
|
|
64
|
+
if (value.stack) error.stack = value.stack;
|
|
65
|
+
|
|
66
|
+
for (const key of Object.keys(value)) {
|
|
67
|
+
if (!(key in error)) error[key] = value[key];
|
|
68
|
+
}
|
|
37
69
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if(err.lineno!==undefined || err.line!==undefined || err.lineNumber!==undefined) {
|
|
41
|
-
const l = err.lineNumber || err.lineno || err.line;
|
|
42
|
-
const c = err.columnNumber || err.colno || err.column;
|
|
70
|
+
return error;
|
|
71
|
+
}
|
|
43
72
|
|
|
44
|
-
|
|
45
|
-
|
|
73
|
+
return new Error(String(value));
|
|
74
|
+
}
|
|
46
75
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
76
|
+
function isParserError(error) {
|
|
77
|
+
return error?.name === "JSXParserError";
|
|
78
|
+
}
|
|
51
79
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
80
|
+
/*
|
|
81
|
+
Returns zero-based line and column numbers.
|
|
55
82
|
|
|
56
|
-
|
|
83
|
+
Parser errors produced by jsx.js already use zero-based positions.
|
|
84
|
+
Browser Error objects and ErrorEvents normally use one-based positions.
|
|
85
|
+
*/
|
|
86
|
+
function getErrorLocation(error, { zeroBased = false } = {}) {
|
|
87
|
+
const line = firstNumber(
|
|
88
|
+
error?.lineNumber,
|
|
89
|
+
error?.lineno,
|
|
90
|
+
error?.line
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const column = firstNumber(
|
|
94
|
+
error?.columnNumber,
|
|
95
|
+
error?.colno,
|
|
96
|
+
error?.column
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
if (line !== null || column !== null) {
|
|
100
|
+
return {
|
|
101
|
+
line:
|
|
102
|
+
line === null
|
|
103
|
+
? null
|
|
104
|
+
: zeroBased
|
|
105
|
+
? line
|
|
106
|
+
: Math.max(0, line - 1),
|
|
107
|
+
|
|
108
|
+
col:
|
|
109
|
+
column === null
|
|
110
|
+
? null
|
|
111
|
+
: zeroBased
|
|
112
|
+
? column
|
|
113
|
+
: Math.max(0, column - 1),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return null;
|
|
57
118
|
}
|
|
58
119
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
for (const
|
|
75
|
-
const
|
|
76
|
-
if (!
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
120
|
+
function sourceUrlFromStack(stack) {
|
|
121
|
+
const text = typeof stack === "string" ? stack : "";
|
|
122
|
+
|
|
123
|
+
/*
|
|
124
|
+
* The final two colon-separated numeric values are the line and column.
|
|
125
|
+
* The path is intentionally greedy so paths containing ':' still work.
|
|
126
|
+
*
|
|
127
|
+
* Examples:
|
|
128
|
+
*
|
|
129
|
+
* at fn (eval:/components/App.jsx:12:8)
|
|
130
|
+
* fn@eval:/components/App.jsx:12:8
|
|
131
|
+
* eval:/components/App.jsx:12:8
|
|
132
|
+
*/
|
|
133
|
+
const expression = /(?:^|[\s(@])((?:eval:\/)[\s\S]*):(\d+):(\d+)(?:\)?$)/;
|
|
134
|
+
|
|
135
|
+
for (const line of text.split(/\r?\n/)) {
|
|
136
|
+
const match = line.trim().match(expression);
|
|
137
|
+
if (!match) continue;
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
url: match[1],
|
|
141
|
+
line: Number(match[2]),
|
|
142
|
+
col: Number(match[3]),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function sourcePathFromEvalUrl(url) {
|
|
150
|
+
if (typeof url !== "string") return null;
|
|
151
|
+
|
|
152
|
+
if (url.startsWith("eval:/")) {
|
|
153
|
+
return url.slice("eval:/".length);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (url.startsWith("eval:")) {
|
|
157
|
+
return url.slice("eval:".length).replace(/^\/+/, "");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/*
|
|
164
|
+
Mappings generated by jsx.js have this shape:
|
|
165
|
+
|
|
166
|
+
[
|
|
167
|
+
generatedLine,
|
|
168
|
+
generatedColumn,
|
|
169
|
+
sourceLine,
|
|
170
|
+
sourceColumn,
|
|
171
|
+
node
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
All four coordinates are zero-based.
|
|
175
|
+
|
|
176
|
+
The correct mapping is the closest mapping at or before the generated
|
|
177
|
+
location. The old implementation searched in the opposite direction and
|
|
178
|
+
used the final mapping for errors occurring before the first mapping.
|
|
179
|
+
*/
|
|
180
|
+
export function mapLocation(mappings, generatedLine, generatedColumn) {
|
|
181
|
+
if (!Array.isArray(mappings) || mappings.length === 0) {
|
|
182
|
+
return {
|
|
183
|
+
line: generatedLine,
|
|
184
|
+
col: generatedColumn,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const line = Number(generatedLine);
|
|
189
|
+
const col = Number(generatedColumn);
|
|
190
|
+
|
|
191
|
+
if (!Number.isFinite(line) || !Number.isFinite(col)) {
|
|
192
|
+
return {
|
|
193
|
+
line: null,
|
|
194
|
+
col: null,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const validMappings = mappings
|
|
199
|
+
.filter(
|
|
200
|
+
(mapping) =>
|
|
201
|
+
Array.isArray(mapping) &&
|
|
202
|
+
mapping.length >= 4 &&
|
|
203
|
+
Number.isFinite(Number(mapping[0])) &&
|
|
204
|
+
Number.isFinite(Number(mapping[1])) &&
|
|
205
|
+
Number.isFinite(Number(mapping[2])) &&
|
|
206
|
+
Number.isFinite(Number(mapping[3]))
|
|
207
|
+
)
|
|
208
|
+
.slice()
|
|
209
|
+
.sort((a, b) => {
|
|
210
|
+
const lineDifference = Number(a[0]) - Number(b[0]);
|
|
211
|
+
|
|
212
|
+
if (lineDifference !== 0) {
|
|
213
|
+
return lineDifference;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return Number(a[1]) - Number(b[1]);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
if (validMappings.length === 0) {
|
|
220
|
+
return {
|
|
221
|
+
line,
|
|
222
|
+
col,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/*
|
|
227
|
+
* Find the last mapping whose generated position is before or equal
|
|
228
|
+
* to the runtime position.
|
|
229
|
+
*/
|
|
230
|
+
let mapping = validMappings[0];
|
|
231
|
+
|
|
232
|
+
for (const candidate of validMappings) {
|
|
233
|
+
const generatedMappingLine = Number(candidate[0]);
|
|
234
|
+
const generatedMappingColumn = Number(candidate[1]);
|
|
235
|
+
|
|
236
|
+
if (
|
|
237
|
+
generatedMappingLine < line ||
|
|
238
|
+
(
|
|
239
|
+
generatedMappingLine === line &&
|
|
240
|
+
generatedMappingColumn <= col
|
|
241
|
+
)
|
|
242
|
+
) {
|
|
243
|
+
mapping = candidate;
|
|
244
|
+
continue;
|
|
87
245
|
}
|
|
246
|
+
|
|
247
|
+
break;
|
|
88
248
|
}
|
|
89
249
|
|
|
90
|
-
|
|
250
|
+
const mappingGeneratedLine = Number(mapping[0]);
|
|
251
|
+
const mappingGeneratedColumn = Number(mapping[1]);
|
|
252
|
+
const mappingSourceLine = Number(mapping[2]);
|
|
253
|
+
const mappingSourceColumn = Number(mapping[3]);
|
|
254
|
+
|
|
255
|
+
/*
|
|
256
|
+
* A mapping points to the beginning of a generated region. Preserve
|
|
257
|
+
* the relative line offset inside that region.
|
|
258
|
+
*
|
|
259
|
+
* This is the important part. Returning mappingSourceLine directly
|
|
260
|
+
* causes errors inside a generated multi-line block to point to the
|
|
261
|
+
* first line of the block.
|
|
262
|
+
*/
|
|
263
|
+
const sourceLine =
|
|
264
|
+
mappingSourceLine + (line - mappingGeneratedLine);
|
|
265
|
+
|
|
266
|
+
/*
|
|
267
|
+
* Columns are only safely relative when the generated error is on the
|
|
268
|
+
* same generated line as the mapping. On later lines, use the source
|
|
269
|
+
* anchor column instead of carrying the generated column across lines.
|
|
270
|
+
*/
|
|
271
|
+
const sourceColumn =
|
|
272
|
+
line === mappingGeneratedLine
|
|
273
|
+
? Math.max(
|
|
274
|
+
0,
|
|
275
|
+
mappingSourceColumn +
|
|
276
|
+
(col - mappingGeneratedColumn)
|
|
277
|
+
)
|
|
278
|
+
: mappingSourceColumn;
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
line: sourceLine,
|
|
282
|
+
col: sourceColumn,
|
|
283
|
+
};
|
|
91
284
|
}
|
|
92
285
|
|
|
93
|
-
function
|
|
94
|
-
|
|
95
|
-
|
|
286
|
+
function getBlockTrace(error) {
|
|
287
|
+
const trace = error?.lilact_trace;
|
|
288
|
+
|
|
289
|
+
if (Array.isArray(trace)) {
|
|
290
|
+
return trace[0];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return trace;
|
|
96
294
|
}
|
|
97
295
|
|
|
98
|
-
function
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
if(!map) map = mps[mps.length-1];
|
|
110
|
-
return {line: r-map[0]+map[2], col: ((r-map[0]===0)?map[3]:0) };
|
|
296
|
+
function getBlockInfo(error) {
|
|
297
|
+
const trace = getBlockTrace(error);
|
|
298
|
+
|
|
299
|
+
if (trace === undefined || trace === null) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return Lilact.blocks_info?.labels?.[trace] ?? null;
|
|
111
304
|
}
|
|
112
305
|
|
|
306
|
+
function applyBlockFallback(error, result, currentPath) {
|
|
307
|
+
const block = getBlockInfo(error);
|
|
308
|
+
if (!block) return result;
|
|
113
309
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
{
|
|
117
|
-
|
|
310
|
+
if (block.path) {
|
|
311
|
+
result.fileName = block.path;
|
|
312
|
+
} else if (!result.fileName) {
|
|
313
|
+
result.fileName = currentPath;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/*
|
|
317
|
+
* scanBlockLabels() stores the original source position of the block.
|
|
318
|
+
* This is more reliable than using the location of the generated catch
|
|
319
|
+
* wrapper.
|
|
320
|
+
*/
|
|
321
|
+
if (
|
|
322
|
+
Number.isFinite(block.line) &&
|
|
323
|
+
Number.isFinite(block.col)
|
|
324
|
+
) {
|
|
325
|
+
result.lineNumber = block.line;
|
|
326
|
+
result.columnNumber = block.col;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (block.desc) {
|
|
330
|
+
result.label = block.desc;
|
|
331
|
+
}
|
|
118
332
|
|
|
119
|
-
|
|
120
|
-
ls.forEach(
|
|
121
|
-
(x) => {
|
|
122
|
-
Lilact.blocks_info.labels[x[1]] = {
|
|
123
|
-
path,
|
|
124
|
-
desc: x[4]
|
|
125
|
-
}
|
|
126
|
-
} );
|
|
333
|
+
return result;
|
|
127
334
|
}
|
|
128
335
|
|
|
336
|
+
function copyErrorMetadata(source, target) {
|
|
337
|
+
if (!source || typeof source !== "object") return;
|
|
338
|
+
|
|
339
|
+
for (const key of [
|
|
340
|
+
"fileName",
|
|
341
|
+
"lineNumber",
|
|
342
|
+
"columnNumber",
|
|
343
|
+
"lineno",
|
|
344
|
+
"colno",
|
|
345
|
+
"line",
|
|
346
|
+
"column",
|
|
347
|
+
"componentStackLog",
|
|
348
|
+
"lilact_trace",
|
|
349
|
+
]) {
|
|
350
|
+
if (source[key] !== undefined && target[key] === undefined) {
|
|
351
|
+
target[key] = source[key];
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
129
355
|
|
|
130
356
|
/**
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* by default for efficiency.
|
|
134
|
-
*
|
|
135
|
-
* @param error - The error object
|
|
136
|
-
*
|
|
137
|
-
* @returns A new object that includes `path`, `row`, `col`, `msg`, `name`, and optional `stack`.
|
|
138
|
-
* The exception itself is stored as `err`.
|
|
357
|
+
* Converts parser errors, runtime errors, ErrorEvents, and rejected
|
|
358
|
+
* promises into one consistent zero-based representation.
|
|
139
359
|
*/
|
|
140
|
-
export function traceError(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
360
|
+
export function traceError(value, runPath) {
|
|
361
|
+
if (value?.isTraced) return value;
|
|
362
|
+
|
|
363
|
+
const original =
|
|
364
|
+
value?.error instanceof Error ? value.error : asError(value);
|
|
365
|
+
|
|
366
|
+
const parserError = isParserError(original);
|
|
367
|
+
|
|
368
|
+
const stackLocation = parserError
|
|
369
|
+
? null
|
|
370
|
+
: sourceUrlFromStack(original.stack);
|
|
371
|
+
|
|
372
|
+
const eventLocation = parserError
|
|
373
|
+
? getErrorLocation(original, { zeroBased: true })
|
|
374
|
+
: getErrorLocation(original);
|
|
375
|
+
|
|
376
|
+
const result = {
|
|
377
|
+
fileName:
|
|
378
|
+
sourcePathFromEvalUrl(stackLocation?.url) ||
|
|
379
|
+
original.fileName ||
|
|
380
|
+
runPath ||
|
|
381
|
+
null,
|
|
382
|
+
|
|
383
|
+
lineNumber:
|
|
384
|
+
stackLocation?.line != null
|
|
385
|
+
? Math.max(0, stackLocation.line - 1)
|
|
386
|
+
: eventLocation?.line ?? null,
|
|
387
|
+
|
|
388
|
+
columnNumber:
|
|
389
|
+
stackLocation?.col != null
|
|
390
|
+
? Math.max(0, stackLocation.col - 1)
|
|
391
|
+
: eventLocation?.col ?? null,
|
|
392
|
+
|
|
393
|
+
message:
|
|
394
|
+
original.message == null
|
|
395
|
+
? String(original)
|
|
396
|
+
: String(original.message),
|
|
397
|
+
|
|
398
|
+
name: original.name || "Error",
|
|
399
|
+
stack: original.stack || null,
|
|
400
|
+
_error: original,
|
|
401
|
+
isTraced: true,
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
copyErrorMetadata(value, result);
|
|
405
|
+
copyErrorMetadata(original, result);
|
|
406
|
+
|
|
407
|
+
/*
|
|
408
|
+
* Parser errors already point into the original JSX source. Do not map
|
|
409
|
+
* them again.
|
|
410
|
+
*/
|
|
411
|
+
if (!parserError && stackLocation?.url) {
|
|
412
|
+
const module = required_scripts[result.fileName];
|
|
413
|
+
|
|
414
|
+
if (module?.mappings) {
|
|
415
|
+
const mapped = mapLocation(
|
|
416
|
+
module.mappings,
|
|
417
|
+
result.lineNumber,
|
|
418
|
+
result.columnNumber
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
result.lineNumber = mapped.line;
|
|
422
|
+
result.columnNumber = mapped.col;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/*
|
|
427
|
+
* When the browser does not preserve the eval sourceURL, the injected
|
|
428
|
+
* Lilact block label is the reliable fallback.
|
|
429
|
+
*/
|
|
430
|
+
if (!parserError && !stackLocation?.url) {
|
|
431
|
+
applyBlockFallback(original, result, runPath);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
Lilact.error = result;
|
|
435
|
+
return result;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function escapeHtml(value) {
|
|
439
|
+
return String(value ?? "")
|
|
440
|
+
.replaceAll("&", "&")
|
|
441
|
+
.replaceAll("<", "<")
|
|
442
|
+
.replaceAll(">", ">")
|
|
443
|
+
.replaceAll('"', """)
|
|
444
|
+
.replaceAll("'", "'");
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function sourceExcerpt(module, zeroBasedLine) {
|
|
448
|
+
if (!module || !Number.isFinite(zeroBasedLine)) {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const lines = String(module.code ?? "").split(/\r?\n/);
|
|
453
|
+
const before = lines[zeroBasedLine - 1] ?? "";
|
|
454
|
+
const current = lines[zeroBasedLine] ?? "";
|
|
455
|
+
const after = lines[zeroBasedLine + 1] ?? "";
|
|
456
|
+
|
|
457
|
+
return { before, current, after };
|
|
217
458
|
}
|
|
218
459
|
|
|
219
460
|
/**
|
|
220
|
-
*
|
|
221
|
-
* when an exception is not handled.
|
|
222
|
-
*
|
|
223
|
-
* It is by default attached to the `window.onerror` when using the development
|
|
224
|
-
* bundle. It is not attached in the production mode, but it is available in the
|
|
225
|
-
* bundle and can be attached.
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
* `
|
|
229
|
-
* window.addEventListener('error', (e) => {
|
|
230
|
-
* Lilact.globalErrorHandler(e);
|
|
231
|
-
* });
|
|
232
|
-
* `
|
|
233
|
-
* @param error - The error object
|
|
234
|
-
*
|
|
461
|
+
* Displays an unhandled error.
|
|
235
462
|
*/
|
|
236
|
-
export function globalErrorHandler(
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
463
|
+
export function globalErrorHandler(eventOrError) {
|
|
464
|
+
const raw =
|
|
465
|
+
eventOrError?.error instanceof Error
|
|
466
|
+
? eventOrError.error
|
|
467
|
+
: eventOrError?.reason !== undefined
|
|
468
|
+
? eventOrError.reason
|
|
469
|
+
: eventOrError;
|
|
470
|
+
|
|
471
|
+
const runPath =
|
|
472
|
+
eventOrError?.fileName ||
|
|
473
|
+
null;
|
|
474
|
+
|
|
475
|
+
const traced = traceError(raw, runPath);
|
|
476
|
+
const module = required_scripts[traced.fileName];
|
|
477
|
+
const excerpt = sourceExcerpt(module, traced.lineNumber);
|
|
478
|
+
|
|
479
|
+
const className = css(`
|
|
480
|
+
background: linear-gradient(135deg, #fff2f2d4, #ffffffd4);
|
|
481
|
+
backdrop-filter: blur(10px);
|
|
482
|
+
border: 1px solid rgba(255,255,255,.25);
|
|
483
|
+
border-radius: 5px;
|
|
484
|
+
box-shadow: 0 10px 30px rgba(0,0,0,.35);
|
|
485
|
+
overflow: hidden;
|
|
486
|
+
min-width: 400px;
|
|
487
|
+
width: 66%;
|
|
488
|
+
|
|
489
|
+
red {
|
|
490
|
+
color: #d00;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
code {
|
|
494
|
+
border: 1px solid #0003;
|
|
495
|
+
overflow: auto;
|
|
496
|
+
padding: 10px;
|
|
497
|
+
display: block;
|
|
498
|
+
}
|
|
499
|
+
`);
|
|
500
|
+
|
|
501
|
+
const dialog = document.createElement("dialog");
|
|
502
|
+
dialog.className = className;
|
|
503
|
+
|
|
504
|
+
const location =
|
|
505
|
+
traced.fileName
|
|
506
|
+
? `At ${escapeHtml(traced.fileName)}`
|
|
507
|
+
: "";
|
|
508
|
+
|
|
509
|
+
const line =
|
|
510
|
+
Number.isFinite(traced.lineNumber)
|
|
511
|
+
? `: Line ${traced.lineNumber + 1}`
|
|
512
|
+
: "";
|
|
513
|
+
|
|
514
|
+
const componentStack =
|
|
515
|
+
traced._error?.componentStackLog ||
|
|
516
|
+
traced._error?.componentStack ||
|
|
517
|
+
"";
|
|
518
|
+
|
|
519
|
+
dialog.innerHTML = `
|
|
520
|
+
<h3><red>Error!</red></h3>
|
|
521
|
+
<b>${location}${line}</b><br><br>
|
|
522
|
+
<b>${escapeHtml(traced.name)}</b>:
|
|
523
|
+
<span>${escapeHtml(traced.message)}</span>
|
|
524
|
+
<br><br>
|
|
525
|
+
|
|
526
|
+
${
|
|
527
|
+
excerpt
|
|
528
|
+
? "<code><pre></pre><red><pre></pre></red><pre></pre></code>"
|
|
529
|
+
: ""
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
${
|
|
533
|
+
componentStack
|
|
534
|
+
? `
|
|
535
|
+
<br>
|
|
536
|
+
Component Stack:
|
|
537
|
+
<br>
|
|
538
|
+
<code><pre>${escapeHtml(componentStack)}</pre></code>
|
|
539
|
+
`
|
|
540
|
+
: ""
|
|
541
|
+
}
|
|
542
|
+
`;
|
|
543
|
+
|
|
544
|
+
if (excerpt) {
|
|
545
|
+
const pre = dialog.querySelectorAll("pre");
|
|
546
|
+
pre[0].innerText = excerpt.before;
|
|
547
|
+
pre[1].innerText = excerpt.current;
|
|
548
|
+
pre[2].innerText = excerpt.after;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
document.body.appendChild(dialog);
|
|
552
|
+
|
|
553
|
+
if (typeof dialog.showModal === "function") {
|
|
554
|
+
dialog.showModal();
|
|
555
|
+
} else {
|
|
556
|
+
dialog.setAttribute("open", "");
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return traced;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Registers the source location of an injected Lilact block.
|
|
564
|
+
*/
|
|
565
|
+
export function scanBlockLabels(code, path) {
|
|
566
|
+
const labels = String(code).matchAll(
|
|
567
|
+
/LILACTBLOCK(\d+):(\d+),(\d+):([^*]+)\*\//gm
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
for (const match of labels) {
|
|
571
|
+
const id = match[1];
|
|
572
|
+
|
|
573
|
+
Lilact.blocks_info.labels[id] = {
|
|
574
|
+
path,
|
|
575
|
+
line: Number(match[2]),
|
|
576
|
+
col: Number(match[3]),
|
|
577
|
+
desc: match[4],
|
|
578
|
+
};
|
|
579
|
+
}
|
|
293
580
|
}
|
|
294
581
|
|
|
295
|
-
|
|
296
|
-
|
|
582
|
+
export const blocks_info = {
|
|
583
|
+
counter: 0,
|
|
584
|
+
labels: {},
|
|
585
|
+
};
|
|
297
586
|
|
|
298
|
-
|
|
299
|
-
export let error = null; // this is only to ease debuggin,
|
|
587
|
+
export let error = null;
|