@jsenv/plugin-supervisor 1.4.0 → 1.4.2
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/js/supervisor.js +326 -268
- package/dist/jsenv_plugin_supervisor.js +118 -139
- package/package.json +4 -4
- package/src/client/supervisor.js +349 -274
- package/src/jsenv_plugin_supervisor.js +16 -10
package/src/client/supervisor.js
CHANGED
|
@@ -13,13 +13,18 @@ window.__supervisor__ = (() => {
|
|
|
13
13
|
typeof window.webkitConvertPointFromNodeToPage === "function";
|
|
14
14
|
|
|
15
15
|
const executionResults = {};
|
|
16
|
-
let
|
|
16
|
+
let documentTimingsOrigin;
|
|
17
17
|
try {
|
|
18
|
-
|
|
18
|
+
documentTimingsOrigin = window.performance.timing.navigationStart;
|
|
19
19
|
} catch (e) {
|
|
20
|
-
|
|
20
|
+
documentTimingsOrigin = Date.now();
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
const documentTimings = {
|
|
23
|
+
origin: documentTimingsOrigin,
|
|
24
|
+
start: Date.now() - documentTimingsOrigin,
|
|
25
|
+
end: null,
|
|
26
|
+
};
|
|
27
|
+
|
|
23
28
|
supervisor.setup = ({
|
|
24
29
|
rootDirectoryUrl,
|
|
25
30
|
scriptInfos,
|
|
@@ -80,10 +85,13 @@ window.__supervisor__ = (() => {
|
|
|
80
85
|
const createExecutionController = (src, type) => {
|
|
81
86
|
const result = {
|
|
82
87
|
status: "pending",
|
|
83
|
-
duration: null,
|
|
84
88
|
coverage: null,
|
|
85
89
|
exception: null,
|
|
86
90
|
value: null,
|
|
91
|
+
timings: {
|
|
92
|
+
start: null,
|
|
93
|
+
end: null,
|
|
94
|
+
},
|
|
87
95
|
};
|
|
88
96
|
|
|
89
97
|
let resolve;
|
|
@@ -94,7 +102,7 @@ window.__supervisor__ = (() => {
|
|
|
94
102
|
executionResults[src] = result;
|
|
95
103
|
|
|
96
104
|
const start = () => {
|
|
97
|
-
result.
|
|
105
|
+
result.timings.start = Date.now() - documentTimingsOrigin;
|
|
98
106
|
result.coverage = null;
|
|
99
107
|
result.status = "started";
|
|
100
108
|
result.exception = null;
|
|
@@ -103,16 +111,15 @@ window.__supervisor__ = (() => {
|
|
|
103
111
|
}
|
|
104
112
|
};
|
|
105
113
|
const end = () => {
|
|
106
|
-
const now = Date.now();
|
|
107
114
|
remainingScriptCount--;
|
|
108
|
-
result.
|
|
115
|
+
result.timings.end = Date.now() - documentTimingsOrigin;
|
|
109
116
|
result.coverage = window.__coverage__;
|
|
110
117
|
if (logs) {
|
|
111
118
|
console.log(`execution ${result.status}`);
|
|
112
119
|
console.groupEnd();
|
|
113
120
|
}
|
|
114
121
|
if (remainingScriptCount === 0) {
|
|
115
|
-
|
|
122
|
+
documentTimings.end = Date.now() - documentTimingsOrigin;
|
|
116
123
|
}
|
|
117
124
|
resolve();
|
|
118
125
|
};
|
|
@@ -160,16 +167,20 @@ window.__supervisor__ = (() => {
|
|
|
160
167
|
}
|
|
161
168
|
const scriptLoadPromise = getScriptLoadPromise(currentScriptClone);
|
|
162
169
|
parentNode.replaceChild(currentScriptClone, nodeToReplace);
|
|
163
|
-
const { detectedBy, failed,
|
|
170
|
+
const { detectedBy, failed, errorEvent } = await scriptLoadPromise;
|
|
164
171
|
if (failed) {
|
|
172
|
+
const messageDefault = `Error while loading script`;
|
|
173
|
+
const { error, message, filename, lineno, colno } = errorEvent;
|
|
165
174
|
if (detectedBy === "script_error_event") {
|
|
166
175
|
// window.error won't be dispatched for this error
|
|
167
|
-
reportErrorBackToBrowser(error);
|
|
176
|
+
reportErrorBackToBrowser(error || errorEvent);
|
|
168
177
|
}
|
|
169
|
-
fail(error, {
|
|
170
|
-
message:
|
|
178
|
+
fail(error || message || messageDefault, {
|
|
179
|
+
message: messageDefault,
|
|
171
180
|
reportedBy: "script_error_event",
|
|
172
|
-
url: urlObject.href,
|
|
181
|
+
url: filename || urlObject.href,
|
|
182
|
+
line: lineno,
|
|
183
|
+
column: colno,
|
|
173
184
|
});
|
|
174
185
|
if (detectedBy === "script_error_event") {
|
|
175
186
|
supervisor.reportException(result.exception);
|
|
@@ -222,7 +233,7 @@ window.__supervisor__ = (() => {
|
|
|
222
233
|
return result;
|
|
223
234
|
} catch (e) {
|
|
224
235
|
fail(e, {
|
|
225
|
-
message: `Error while importing module
|
|
236
|
+
message: `Error while importing module`,
|
|
226
237
|
reportedBy: "dynamic_import",
|
|
227
238
|
url: urlObject.href,
|
|
228
239
|
});
|
|
@@ -268,16 +279,21 @@ window.__supervisor__ = (() => {
|
|
|
268
279
|
const scriptLoadResultPromise =
|
|
269
280
|
getScriptLoadPromise(currentScriptClone);
|
|
270
281
|
parentNode.replaceChild(currentScriptClone, nodeToReplace);
|
|
271
|
-
const { detectedBy, failed,
|
|
282
|
+
const { detectedBy, failed, errorEvent } =
|
|
283
|
+
await scriptLoadResultPromise;
|
|
272
284
|
|
|
273
285
|
if (failed) {
|
|
286
|
+
const messageDefault = `Error while loading module`;
|
|
287
|
+
const { error, message, filename, lineno, colno } = errorEvent;
|
|
274
288
|
// if (detectedBy === "script_error_event") {
|
|
275
289
|
// reportErrorBackToBrowser(error)
|
|
276
290
|
// }
|
|
277
|
-
fail(error, {
|
|
278
|
-
message:
|
|
291
|
+
fail(error || message || messageDefault, {
|
|
292
|
+
message: messageDefault,
|
|
279
293
|
reportedBy: "script_error_event",
|
|
280
|
-
url: urlObject.href,
|
|
294
|
+
url: filename || urlObject.href,
|
|
295
|
+
line: lineno,
|
|
296
|
+
column: colno,
|
|
281
297
|
});
|
|
282
298
|
if (detectedBy === "script_error_event") {
|
|
283
299
|
supervisor.reportException(result.exception);
|
|
@@ -391,8 +407,7 @@ window.__supervisor__ = (() => {
|
|
|
391
407
|
supervisor.getDocumentExecutionResult = async () => {
|
|
392
408
|
await Promise.all(promises);
|
|
393
409
|
return {
|
|
394
|
-
|
|
395
|
-
endTime: documentExecutionEndTime,
|
|
410
|
+
timings: documentTimings,
|
|
396
411
|
status: "completed",
|
|
397
412
|
executionResults,
|
|
398
413
|
};
|
|
@@ -435,10 +450,11 @@ window.__supervisor__ = (() => {
|
|
|
435
450
|
stackSourcemapped: null,
|
|
436
451
|
stackOriginal: "", // the stack from runtime, not normalized to v8
|
|
437
452
|
stackTrace: "", // the stack trace (without error name and message)
|
|
453
|
+
stackFrames: undefined,
|
|
438
454
|
withServerUrls: {
|
|
439
455
|
message: "",
|
|
440
|
-
stackTrace: "",
|
|
441
456
|
stack: "",
|
|
457
|
+
stackTrace: "",
|
|
442
458
|
},
|
|
443
459
|
meta: null,
|
|
444
460
|
site: {
|
|
@@ -463,78 +479,154 @@ window.__supervisor__ = (() => {
|
|
|
463
479
|
exception.message = reason;
|
|
464
480
|
return;
|
|
465
481
|
}
|
|
466
|
-
if (reason
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
!isWebkitOrSafari
|
|
475
|
-
) {
|
|
476
|
-
// stackTrace formatted by V8
|
|
477
|
-
exception.stackSourcemapped = true;
|
|
478
|
-
exception.stack = error.stack;
|
|
479
|
-
exception.stackTrace = getErrorStackTrace(error);
|
|
480
|
-
} else if (error.stack) {
|
|
481
|
-
exception.stackSourcemapped = false;
|
|
482
|
-
exception.stackTrace = error.stack;
|
|
483
|
-
exception.stack = stringifyStack(exception);
|
|
484
|
-
}
|
|
482
|
+
if (typeof reason !== "object") {
|
|
483
|
+
exception.message = JSON.stringify(reason);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
// isError can be false when reason is an ErrorEvent for instance
|
|
487
|
+
exception.isError = reason instanceof Error;
|
|
488
|
+
exception.name = reason.name || "Error";
|
|
489
|
+
exception.message = reason.message || message || "";
|
|
485
490
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
491
|
+
let stackFrames;
|
|
492
|
+
if (
|
|
493
|
+
exception.isError &&
|
|
494
|
+
Error.captureStackTrace &&
|
|
495
|
+
// captureStackTrace exists on webkit but error.stack is not v8
|
|
496
|
+
!isWebkitOrSafari
|
|
497
|
+
) {
|
|
498
|
+
// stackTrace formatted by V8
|
|
499
|
+
const { prepareStackTrace } = Error;
|
|
500
|
+
Error.prepareStackTrace = (e, callSites) => {
|
|
501
|
+
Error.prepareStackTrace = prepareStackTrace;
|
|
502
|
+
|
|
503
|
+
const getPropertiesFromEvalOrigin = (origin) => {
|
|
504
|
+
// Most eval() calls are in this format
|
|
505
|
+
const topLevelEvalMatch =
|
|
506
|
+
/^eval at ([^(]+) \((.+):(\d+):(\d+)\)$/.exec(origin);
|
|
507
|
+
if (topLevelEvalMatch) {
|
|
508
|
+
const source = topLevelEvalMatch[2];
|
|
509
|
+
const line = Number(topLevelEvalMatch[3]);
|
|
510
|
+
const column = topLevelEvalMatch[4] - 1;
|
|
511
|
+
return {
|
|
512
|
+
url: source,
|
|
513
|
+
line,
|
|
514
|
+
column,
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
// Parse nested eval() calls using recursion
|
|
518
|
+
const nestedEvalMatch = /^eval at ([^(]+) \((.+)\)$/.exec(origin);
|
|
519
|
+
if (nestedEvalMatch) {
|
|
520
|
+
return getPropertiesFromEvalOrigin(nestedEvalMatch[2]);
|
|
521
|
+
}
|
|
522
|
+
return null;
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
stackFrames = [];
|
|
526
|
+
for (const callSite of callSites) {
|
|
527
|
+
const url =
|
|
528
|
+
callSite.getFileName() || callSite.getScriptNameOrSourceURL();
|
|
529
|
+
const line = callSite.getLineNumber();
|
|
530
|
+
const column = callSite.getColumnNumber();
|
|
531
|
+
const site = resolveUrlSite({ url, line, column });
|
|
532
|
+
const stackFrame = {
|
|
533
|
+
raw: ` at ${String(callSite)}`,
|
|
534
|
+
url: site.url,
|
|
535
|
+
line: site.line,
|
|
536
|
+
column: site.column,
|
|
537
|
+
functionName: callSite.getFunctionName(),
|
|
538
|
+
isNative: callSite.isNative(),
|
|
539
|
+
isEval: callSite.isEval(),
|
|
540
|
+
isConstructor: callSite.isConstructor(),
|
|
541
|
+
isAsync: callSite.isAsync(),
|
|
542
|
+
evalSite: null,
|
|
543
|
+
};
|
|
544
|
+
if (stackFrame.isEval) {
|
|
545
|
+
const evalOrigin = stackFrame.getEvalOrigin();
|
|
546
|
+
if (evalOrigin) {
|
|
547
|
+
stackFrame.evalSite = getPropertiesFromEvalOrigin(evalOrigin);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
stackFrames.push(stackFrame);
|
|
505
551
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
552
|
+
let stackTrace = "";
|
|
553
|
+
for (const stackFrame of stackFrames) {
|
|
554
|
+
if (stackTrace) stackTrace += "\n";
|
|
555
|
+
stackTrace += stackFrame.raw;
|
|
510
556
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
557
|
+
exception.stackTrace = stackTrace;
|
|
558
|
+
|
|
559
|
+
let stack = "";
|
|
560
|
+
const name = reason.name || "Error";
|
|
561
|
+
const message = reason.message || "";
|
|
562
|
+
stack += `${name}: ${message}`;
|
|
563
|
+
if (stackTrace) {
|
|
564
|
+
stack += `\n${stackTrace}`;
|
|
514
565
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
566
|
+
return stack;
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
exception.stackSourcemapped = true;
|
|
570
|
+
exception.stack = reason.stack;
|
|
571
|
+
if (stackFrames === undefined) {
|
|
572
|
+
// Error.prepareStackTrace not trigerred
|
|
573
|
+
// - reason is not an error
|
|
574
|
+
// - reason.stack already get
|
|
575
|
+
Error.prepareStackTrace = prepareStackTrace;
|
|
576
|
+
exception.stackTrace = getErrorStackTrace(reason);
|
|
577
|
+
} else {
|
|
578
|
+
exception.stackFrames = stackFrames;
|
|
579
|
+
const [firstCallFrame] = stackFrames;
|
|
580
|
+
if (
|
|
581
|
+
exception.site.url === null &&
|
|
582
|
+
firstCallFrame &&
|
|
583
|
+
firstCallFrame.url
|
|
584
|
+
) {
|
|
585
|
+
Object.assign(exception.site, {
|
|
586
|
+
url: firstCallFrame.url,
|
|
587
|
+
line: firstCallFrame.line,
|
|
588
|
+
column: firstCallFrame.column,
|
|
589
|
+
});
|
|
520
590
|
}
|
|
521
591
|
}
|
|
522
|
-
|
|
592
|
+
} else {
|
|
593
|
+
exception.stackSourcemapped = false;
|
|
594
|
+
exception.stackTrace = reason.stack;
|
|
595
|
+
exception.stack = stringifyStack(exception);
|
|
523
596
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
exception.
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
597
|
+
|
|
598
|
+
if (exception.site.url === null && reason.url) {
|
|
599
|
+
Object.assign(exception.site, resolveUrlSite({ url: reason.url }));
|
|
600
|
+
}
|
|
601
|
+
export_missing: {
|
|
602
|
+
// chrome
|
|
603
|
+
if (exception.message.includes("does not provide an export named")) {
|
|
604
|
+
exception.code = DYNAMIC_IMPORT_EXPORT_MISSING;
|
|
605
|
+
}
|
|
606
|
+
// firefox
|
|
607
|
+
if (
|
|
608
|
+
exception.message.startsWith("import not found:") ||
|
|
609
|
+
exception.message.startsWith("ambiguous indirect export:")
|
|
610
|
+
) {
|
|
611
|
+
exception.code = DYNAMIC_IMPORT_EXPORT_MISSING;
|
|
531
612
|
}
|
|
532
|
-
|
|
533
|
-
|
|
613
|
+
// safari
|
|
614
|
+
if (exception.message.startsWith("import binding name")) {
|
|
615
|
+
exception.code = DYNAMIC_IMPORT_EXPORT_MISSING;
|
|
616
|
+
}
|
|
617
|
+
if (exception.message.includes("Importing a module script failed")) {
|
|
618
|
+
exception.code = DYNAMIC_IMPORT_FETCH_ERROR;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
js_syntax_error: {
|
|
622
|
+
if (
|
|
623
|
+
!exception.code &&
|
|
624
|
+
exception.name === "SyntaxError" &&
|
|
625
|
+
typeof line === "number"
|
|
626
|
+
) {
|
|
627
|
+
exception.code = DYNAMIC_IMPORT_SYNTAX_ERROR;
|
|
534
628
|
}
|
|
535
|
-
return;
|
|
536
629
|
}
|
|
537
|
-
exception.message = JSON.stringify(reason);
|
|
538
630
|
};
|
|
539
631
|
writeBasicProperties();
|
|
540
632
|
|
|
@@ -549,7 +641,8 @@ window.__supervisor__ = (() => {
|
|
|
549
641
|
const fileUrlSite = resolveUrlSite({ url, line, column });
|
|
550
642
|
if (
|
|
551
643
|
fileUrlSite.isInline &&
|
|
552
|
-
exception.
|
|
644
|
+
exception.name === "SyntaxError" &&
|
|
645
|
+
exception.code !== DYNAMIC_IMPORT_EXPORT_MISSING
|
|
553
646
|
) {
|
|
554
647
|
// syntax error on inline script need line-1 for some reason
|
|
555
648
|
fileUrlSite.line = fileUrlSite.line - 1;
|
|
@@ -588,14 +681,15 @@ window.__supervisor__ = (() => {
|
|
|
588
681
|
return exception;
|
|
589
682
|
};
|
|
590
683
|
|
|
591
|
-
const stringifyStack = ({ name, message, stackTrace }) => {
|
|
592
|
-
let stack;
|
|
684
|
+
const stringifyStack = ({ codeFrame, name, message, stackTrace }) => {
|
|
685
|
+
let stack = "";
|
|
686
|
+
if (codeFrame) {
|
|
687
|
+
stack += `${codeFrame}\n`;
|
|
688
|
+
}
|
|
593
689
|
if (name && message) {
|
|
594
|
-
stack
|
|
690
|
+
stack += `${name}: ${message}`;
|
|
595
691
|
} else if (message) {
|
|
596
|
-
stack
|
|
597
|
-
} else {
|
|
598
|
-
stack = "";
|
|
692
|
+
stack += message;
|
|
599
693
|
}
|
|
600
694
|
if (stackTrace) {
|
|
601
695
|
stack += `\n${stackTrace}`;
|
|
@@ -737,153 +831,6 @@ window.__supervisor__ = (() => {
|
|
|
737
831
|
});
|
|
738
832
|
};
|
|
739
833
|
|
|
740
|
-
let formatError;
|
|
741
|
-
error_formatter: {
|
|
742
|
-
formatError = (exceptionInfo) => {
|
|
743
|
-
const errorParts = {
|
|
744
|
-
theme: "dark",
|
|
745
|
-
title: "An error occured",
|
|
746
|
-
text: "",
|
|
747
|
-
tip: "",
|
|
748
|
-
errorDetailsPromise: null,
|
|
749
|
-
};
|
|
750
|
-
const tips = [];
|
|
751
|
-
tips.push("Click outside to close.");
|
|
752
|
-
errorParts.tip = tips.join(`\n <br />\n `);
|
|
753
|
-
|
|
754
|
-
const generateClickableText = (text) => {
|
|
755
|
-
const textWithHtmlLinks = makeLinksClickable(text, {
|
|
756
|
-
createLink: ({ url, line, column }) => {
|
|
757
|
-
const urlSite = resolveUrlSite({ url, line, column });
|
|
758
|
-
if (errorBaseUrl) {
|
|
759
|
-
if (urlSite.url.startsWith(rootDirectoryUrl)) {
|
|
760
|
-
urlSite.url = `${errorBaseUrl}${urlSite.url.slice(
|
|
761
|
-
rootDirectoryUrl.length,
|
|
762
|
-
)}`;
|
|
763
|
-
} else {
|
|
764
|
-
urlSite.url = "file:///mocked_for_snapshots";
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
const urlWithLineAndColumn = stringifyUrlSite(urlSite);
|
|
768
|
-
return {
|
|
769
|
-
href:
|
|
770
|
-
urlSite.url.startsWith("file:") && openInEditor
|
|
771
|
-
? `javascript:window.fetch('/__open_in_editor__/${encodeURIComponent(
|
|
772
|
-
urlWithLineAndColumn,
|
|
773
|
-
)}')`
|
|
774
|
-
: urlSite.url,
|
|
775
|
-
text: urlWithLineAndColumn,
|
|
776
|
-
};
|
|
777
|
-
},
|
|
778
|
-
});
|
|
779
|
-
return textWithHtmlLinks;
|
|
780
|
-
};
|
|
781
|
-
|
|
782
|
-
errorParts.text = stringifyStack({
|
|
783
|
-
name: exceptionInfo.name,
|
|
784
|
-
message: exceptionInfo.message
|
|
785
|
-
? generateClickableText(exceptionInfo.message)
|
|
786
|
-
: "",
|
|
787
|
-
stackTrace: exceptionInfo.stackTrace
|
|
788
|
-
? generateClickableText(exceptionInfo.stackTrace)
|
|
789
|
-
: "",
|
|
790
|
-
});
|
|
791
|
-
if (exceptionInfo.site.url) {
|
|
792
|
-
errorParts.errorDetailsPromise = (async () => {
|
|
793
|
-
if (!serverIsJsenvDevServer) {
|
|
794
|
-
return null;
|
|
795
|
-
}
|
|
796
|
-
try {
|
|
797
|
-
if (
|
|
798
|
-
exceptionInfo.code === DYNAMIC_IMPORT_FETCH_ERROR ||
|
|
799
|
-
exceptionInfo.reportedBy === "script_error_event"
|
|
800
|
-
) {
|
|
801
|
-
const response = await window.fetch(
|
|
802
|
-
`/__get_error_cause__/${encodeURIComponent(
|
|
803
|
-
exceptionInfo.site.isInline
|
|
804
|
-
? exceptionInfo.site.originalUrl
|
|
805
|
-
: exceptionInfo.site.url,
|
|
806
|
-
)}`,
|
|
807
|
-
);
|
|
808
|
-
if (response.status !== 200) {
|
|
809
|
-
return null;
|
|
810
|
-
}
|
|
811
|
-
const causeInfo = await response.json();
|
|
812
|
-
if (!causeInfo) {
|
|
813
|
-
return null;
|
|
814
|
-
}
|
|
815
|
-
const causeText =
|
|
816
|
-
causeInfo.code === "NOT_FOUND"
|
|
817
|
-
? stringifyStack({
|
|
818
|
-
name: causeInfo.name,
|
|
819
|
-
message: generateClickableText(causeInfo.reason),
|
|
820
|
-
stackTrace: generateClickableText(causeInfo.codeFrame),
|
|
821
|
-
})
|
|
822
|
-
: stringifyStack({
|
|
823
|
-
name: causeInfo.name,
|
|
824
|
-
message: generateClickableText(causeInfo.stack),
|
|
825
|
-
stackTrace: generateClickableText(causeInfo.codeFrame),
|
|
826
|
-
});
|
|
827
|
-
return {
|
|
828
|
-
cause: causeText,
|
|
829
|
-
};
|
|
830
|
-
}
|
|
831
|
-
if (exceptionInfo.site.line !== undefined) {
|
|
832
|
-
const urlToFetch = new URL(
|
|
833
|
-
`/__get_code_frame__/${encodeURIComponent(
|
|
834
|
-
stringifyUrlSite(exceptionInfo.site),
|
|
835
|
-
)}`,
|
|
836
|
-
window.origin,
|
|
837
|
-
);
|
|
838
|
-
if (!exceptionInfo.stackSourcemapped) {
|
|
839
|
-
urlToFetch.searchParams.set("remap", "");
|
|
840
|
-
}
|
|
841
|
-
const response = await window.fetch(urlToFetch);
|
|
842
|
-
if (response.status !== 200) {
|
|
843
|
-
return null;
|
|
844
|
-
}
|
|
845
|
-
const codeFrame = await response.text();
|
|
846
|
-
return {
|
|
847
|
-
codeFrame: generateClickableText(codeFrame),
|
|
848
|
-
};
|
|
849
|
-
}
|
|
850
|
-
} catch (e) {
|
|
851
|
-
// happens if server is closed for instance
|
|
852
|
-
return null;
|
|
853
|
-
}
|
|
854
|
-
return null;
|
|
855
|
-
})();
|
|
856
|
-
}
|
|
857
|
-
return errorParts;
|
|
858
|
-
};
|
|
859
|
-
|
|
860
|
-
const makeLinksClickable = (
|
|
861
|
-
string,
|
|
862
|
-
{ createLink = ({ url }) => url },
|
|
863
|
-
) => {
|
|
864
|
-
// normalize line breaks
|
|
865
|
-
string = string.replace(/\n/g, "\n");
|
|
866
|
-
string = escapeHtml(string);
|
|
867
|
-
// render links
|
|
868
|
-
string = replaceUrls(string, ({ url, line, column }) => {
|
|
869
|
-
const { href, text } = createLink({ url, line, column });
|
|
870
|
-
return link({ href, text });
|
|
871
|
-
});
|
|
872
|
-
return string;
|
|
873
|
-
};
|
|
874
|
-
|
|
875
|
-
const escapeHtml = (string) => {
|
|
876
|
-
return string
|
|
877
|
-
.replace(/&/g, "&")
|
|
878
|
-
.replace(/</g, "<")
|
|
879
|
-
.replace(/>/g, ">")
|
|
880
|
-
.replace(/"/g, """)
|
|
881
|
-
.replace(/'/g, "'");
|
|
882
|
-
};
|
|
883
|
-
|
|
884
|
-
const link = ({ href, text = href }) => `<a href="${href}">${text}</a>`;
|
|
885
|
-
}
|
|
886
|
-
|
|
887
834
|
let displayErrorNotification;
|
|
888
835
|
error_notification: {
|
|
889
836
|
const { Notification } = window;
|
|
@@ -928,51 +875,178 @@ window.__supervisor__ = (() => {
|
|
|
928
875
|
};
|
|
929
876
|
|
|
930
877
|
class JsenvErrorOverlay extends HTMLElement {
|
|
931
|
-
constructor(
|
|
878
|
+
constructor(exception) {
|
|
932
879
|
super();
|
|
933
|
-
|
|
934
|
-
|
|
880
|
+
const root = this.attachShadow({ mode: "open" });
|
|
881
|
+
const tips = [];
|
|
882
|
+
tips.push("Click outside to close.");
|
|
883
|
+
const tip = tips.join(`\n <br />\n `);
|
|
884
|
+
root.innerHTML = `
|
|
935
885
|
<style>
|
|
936
886
|
${overlayCSS}
|
|
937
887
|
</style>
|
|
938
888
|
<div class="backdrop"></div>
|
|
939
|
-
<div class="overlay" data-theme
|
|
889
|
+
<div class="overlay" data-theme="dark">
|
|
940
890
|
<h1 class="title">
|
|
941
|
-
|
|
891
|
+
An error occured
|
|
942
892
|
</h1>
|
|
943
|
-
<pre class="text"
|
|
893
|
+
<pre class="text"></pre>
|
|
944
894
|
<div class="tip">
|
|
945
895
|
${tip}
|
|
946
896
|
</div>
|
|
947
897
|
</div>`;
|
|
948
|
-
|
|
898
|
+
root.querySelector(".backdrop").onclick = () => {
|
|
949
899
|
if (!this.parentNode) {
|
|
950
900
|
// not in document anymore
|
|
951
901
|
return;
|
|
952
902
|
}
|
|
953
|
-
|
|
903
|
+
root.querySelector(".backdrop").onclick = null;
|
|
954
904
|
this.parentNode.removeChild(this);
|
|
955
905
|
};
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
906
|
+
|
|
907
|
+
const renderException = () => {
|
|
908
|
+
const { cause = {} } = exception;
|
|
909
|
+
const { trace = {} } = cause;
|
|
910
|
+
|
|
911
|
+
root.querySelector(".text").innerHTML = stringifyStack({
|
|
912
|
+
codeFrame: trace.codeFrame
|
|
913
|
+
? generateClickableText(trace.codeFrame)
|
|
914
|
+
: "",
|
|
915
|
+
name: exception.name,
|
|
916
|
+
message: exception.message
|
|
917
|
+
? generateClickableText(exception.message)
|
|
918
|
+
: "",
|
|
919
|
+
stackTrace: exception.stackTrace
|
|
920
|
+
? generateClickableText(exception.stackTrace)
|
|
921
|
+
: "",
|
|
922
|
+
});
|
|
923
|
+
if (cause) {
|
|
924
|
+
const causeText = stringifyStack({
|
|
925
|
+
name: cause.name,
|
|
926
|
+
message: cause.reason
|
|
927
|
+
? generateClickableText(cause.reason)
|
|
928
|
+
: cause.stack
|
|
929
|
+
? generateClickableText(cause.stack)
|
|
930
|
+
: "",
|
|
931
|
+
});
|
|
932
|
+
if (causeText) {
|
|
933
|
+
const causeTextIndented = prefixRemainingLines(causeText, " ");
|
|
934
|
+
root.querySelector(".text").innerHTML +=
|
|
935
|
+
`\n [cause]: ${causeTextIndented}`;
|
|
960
936
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
937
|
+
}
|
|
938
|
+
if (
|
|
939
|
+
!exception.stackTrace &&
|
|
940
|
+
(exception.reportedBy === "script_error_event" ||
|
|
941
|
+
exception.reportedBy === "window_error_event" ||
|
|
942
|
+
exception.name === "SyntaxError" ||
|
|
943
|
+
exception.code === DYNAMIC_IMPORT_FETCH_ERROR ||
|
|
944
|
+
exception.code === DYNAMIC_IMPORT_EXPORT_MISSING ||
|
|
945
|
+
exception.code === DYNAMIC_IMPORT_SYNTAX_ERROR)
|
|
946
|
+
) {
|
|
947
|
+
let traceSite = stringifyUrlSite(trace);
|
|
948
|
+
if (traceSite) {
|
|
949
|
+
root.querySelector(".text").innerHTML +=
|
|
950
|
+
`\n at ${generateClickableText(traceSite)}`;
|
|
965
951
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
renderException();
|
|
955
|
+
if (exception.site.url && serverIsJsenvDevServer) {
|
|
956
|
+
(async () => {
|
|
957
|
+
try {
|
|
958
|
+
if (
|
|
959
|
+
exception.code === DYNAMIC_IMPORT_FETCH_ERROR ||
|
|
960
|
+
exception.reportedBy === "script_error_event"
|
|
961
|
+
) {
|
|
962
|
+
const response = await window.fetch(
|
|
963
|
+
`/__get_error_cause__/${encodeURIComponent(
|
|
964
|
+
exception.site.isInline
|
|
965
|
+
? exception.site.originalUrl
|
|
966
|
+
: exception.site.url,
|
|
967
|
+
)}`,
|
|
968
|
+
);
|
|
969
|
+
if (response.status !== 200) {
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
const causeInfo = await response.json();
|
|
973
|
+
if (!causeInfo) {
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
exception.cause = causeInfo;
|
|
977
|
+
renderException();
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
if (exception.site.line !== undefined) {
|
|
981
|
+
const urlToFetch = new URL(
|
|
982
|
+
`/__get_cause_trace__/${encodeURIComponent(
|
|
983
|
+
stringifyUrlSite(exception.site),
|
|
984
|
+
)}`,
|
|
985
|
+
window.origin,
|
|
986
|
+
);
|
|
987
|
+
if (!exception.stackSourcemapped) {
|
|
988
|
+
urlToFetch.searchParams.set("remap", "");
|
|
989
|
+
}
|
|
990
|
+
const response = await window.fetch(urlToFetch);
|
|
991
|
+
if (response.status !== 200) {
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
const causeTrace = await response.json();
|
|
995
|
+
exception.cause = {
|
|
996
|
+
trace: causeTrace,
|
|
997
|
+
};
|
|
998
|
+
renderException();
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
} catch (e) {
|
|
1002
|
+
// happens if server is closed for instance
|
|
1003
|
+
return;
|
|
970
1004
|
}
|
|
971
|
-
});
|
|
1005
|
+
})();
|
|
972
1006
|
}
|
|
973
1007
|
}
|
|
974
1008
|
}
|
|
975
|
-
|
|
1009
|
+
const generateClickableText = (text) => {
|
|
1010
|
+
const textWithHtmlLinks = makeLinksClickable(text, {
|
|
1011
|
+
createLink: ({ url, line, column }) => {
|
|
1012
|
+
const urlSite = resolveUrlSite({ url, line, column });
|
|
1013
|
+
if (errorBaseUrl) {
|
|
1014
|
+
if (urlSite.url.startsWith(rootDirectoryUrl)) {
|
|
1015
|
+
urlSite.url = `${errorBaseUrl}${urlSite.url.slice(
|
|
1016
|
+
rootDirectoryUrl.length,
|
|
1017
|
+
)}`;
|
|
1018
|
+
} else {
|
|
1019
|
+
urlSite.url = "file:///mocked_for_snapshots";
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
const urlWithLineAndColumn = stringifyUrlSite(urlSite);
|
|
1023
|
+
return {
|
|
1024
|
+
href:
|
|
1025
|
+
urlSite.url.startsWith("file:") && openInEditor
|
|
1026
|
+
? `javascript:window.fetch('/__open_in_editor__/${encodeURIComponent(
|
|
1027
|
+
urlWithLineAndColumn,
|
|
1028
|
+
)}')`
|
|
1029
|
+
: urlSite.url,
|
|
1030
|
+
text: urlWithLineAndColumn,
|
|
1031
|
+
};
|
|
1032
|
+
},
|
|
1033
|
+
});
|
|
1034
|
+
return textWithHtmlLinks;
|
|
1035
|
+
};
|
|
1036
|
+
const makeLinksClickable = (
|
|
1037
|
+
string,
|
|
1038
|
+
{ createLink = ({ url }) => url },
|
|
1039
|
+
) => {
|
|
1040
|
+
// normalize line breaks
|
|
1041
|
+
string = string.replace(/\r\n/g, "\n");
|
|
1042
|
+
string = escapeHtml(string);
|
|
1043
|
+
// render links
|
|
1044
|
+
string = replaceUrls(string, ({ url, line, column }) => {
|
|
1045
|
+
const { href, text } = createLink({ url, line, column });
|
|
1046
|
+
return link({ href, text });
|
|
1047
|
+
});
|
|
1048
|
+
return string;
|
|
1049
|
+
};
|
|
976
1050
|
const prefixRemainingLines = (text, prefix) => {
|
|
977
1051
|
const lines = text.split(/\r?\n/);
|
|
978
1052
|
const firstLine = lines.shift();
|
|
@@ -985,6 +1059,15 @@ window.__supervisor__ = (() => {
|
|
|
985
1059
|
}
|
|
986
1060
|
return result;
|
|
987
1061
|
};
|
|
1062
|
+
const escapeHtml = (string) => {
|
|
1063
|
+
return string
|
|
1064
|
+
.replace(/&/g, "&")
|
|
1065
|
+
.replace(/</g, "<")
|
|
1066
|
+
.replace(/>/g, ">")
|
|
1067
|
+
.replace(/"/g, """)
|
|
1068
|
+
.replace(/'/g, "'");
|
|
1069
|
+
};
|
|
1070
|
+
const link = ({ href, text = href }) => `<a href="${href}">${text}</a>`;
|
|
988
1071
|
|
|
989
1072
|
if (customElements && !customElements.get(JSENV_ERROR_OVERLAY_TAGNAME)) {
|
|
990
1073
|
customElements.define(JSENV_ERROR_OVERLAY_TAGNAME, JsenvErrorOverlay);
|
|
@@ -1069,17 +1152,8 @@ window.__supervisor__ = (() => {
|
|
|
1069
1152
|
|
|
1070
1153
|
supervisor.createException = createException;
|
|
1071
1154
|
supervisor.reportException = (exception) => {
|
|
1072
|
-
const { theme, title, text, tip, errorDetailsPromise } =
|
|
1073
|
-
formatError(exception);
|
|
1074
|
-
|
|
1075
1155
|
if (errorOverlay) {
|
|
1076
|
-
const removeErrorOverlay = displayJsenvErrorOverlay(
|
|
1077
|
-
theme,
|
|
1078
|
-
title,
|
|
1079
|
-
text,
|
|
1080
|
-
tip,
|
|
1081
|
-
errorDetailsPromise,
|
|
1082
|
-
});
|
|
1156
|
+
const removeErrorOverlay = displayJsenvErrorOverlay(exception);
|
|
1083
1157
|
if (window.__reloader__) {
|
|
1084
1158
|
const onchange = window.__reloader__.status.onchange;
|
|
1085
1159
|
window.__reloader__.status.onchange = () => {
|
|
@@ -1092,8 +1166,8 @@ window.__supervisor__ = (() => {
|
|
|
1092
1166
|
}
|
|
1093
1167
|
if (errorNotification) {
|
|
1094
1168
|
displayErrorNotification({
|
|
1095
|
-
title,
|
|
1096
|
-
text,
|
|
1169
|
+
title: "An error occured",
|
|
1170
|
+
text: "todo",
|
|
1097
1171
|
});
|
|
1098
1172
|
}
|
|
1099
1173
|
return exception;
|
|
@@ -1114,6 +1188,7 @@ window.__supervisor__ = (() => {
|
|
|
1114
1188
|
// when error is reported within a worker error is null
|
|
1115
1189
|
// but there is a message property on errorEvent
|
|
1116
1190
|
reportedBy: "window_error_event",
|
|
1191
|
+
message,
|
|
1117
1192
|
url: filename,
|
|
1118
1193
|
line: lineno,
|
|
1119
1194
|
column: colno,
|
|
@@ -1159,7 +1234,7 @@ window.__supervisor__ = (() => {
|
|
|
1159
1234
|
resolve({
|
|
1160
1235
|
detectedBy: "window_error_event",
|
|
1161
1236
|
failed: true,
|
|
1162
|
-
|
|
1237
|
+
errorEvent,
|
|
1163
1238
|
});
|
|
1164
1239
|
}
|
|
1165
1240
|
};
|
|
@@ -1172,7 +1247,7 @@ window.__supervisor__ = (() => {
|
|
|
1172
1247
|
resolve({
|
|
1173
1248
|
detectedBy: "script_error_event",
|
|
1174
1249
|
failed: true,
|
|
1175
|
-
|
|
1250
|
+
errorEvent,
|
|
1176
1251
|
});
|
|
1177
1252
|
});
|
|
1178
1253
|
script.addEventListener("load", () => {
|