@jsenv/core 36.0.2 → 36.1.1

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.
@@ -1,1175 +0,0 @@
1
- window.__supervisor__ = (() => {
2
- const notImplemented = () => {
3
- throw new Error(`window.__supervisor__.setup() not called`);
4
- };
5
- const supervisor = {
6
- reportException: notImplemented,
7
- superviseScript: notImplemented,
8
- superviseScriptTypeModule: notImplemented,
9
- reloadSupervisedScript: notImplemented,
10
- getDocumentExecutionResult: notImplemented
11
- };
12
- const executionResults = {};
13
- let documentExecutionStartTime;
14
- try {
15
- documentExecutionStartTime = window.performance.timing.navigationStart;
16
- } catch (e) {
17
- documentExecutionStartTime = Date.now();
18
- }
19
- let documentExecutionEndTime;
20
- supervisor.setup = ({
21
- rootDirectoryUrl,
22
- scriptInfos,
23
- serverIsJsenvDevServer,
24
- logs,
25
- errorOverlay,
26
- errorBaseUrl,
27
- openInEditor
28
- }) => {
29
- const executions = {};
30
- const promises = [];
31
- let remainingScriptCount = scriptInfos.length;
32
-
33
- // respect execution order
34
- // - wait for classic scripts to be done (non async)
35
- // - wait module script previous execution (non async)
36
- // see https://gist.github.com/jakub-g/385ee6b41085303a53ad92c7c8afd7a6#typemodule-vs-non-module-typetextjavascript-vs-script-nomodule
37
- const executionQueue = [];
38
- let executing = false;
39
- const addToExecutionQueue = async execution => {
40
- if (execution.async) {
41
- execution.execute();
42
- return;
43
- }
44
- if (executing) {
45
- executionQueue.push(execution);
46
- return;
47
- }
48
- execThenDequeue(execution);
49
- };
50
- const execThenDequeue = async execution => {
51
- executing = true;
52
- // start next js module execution as soon as current js module starts to execute
53
- // (do not wait in case of top level await)
54
- let resolveExecutingPromise;
55
- const executingPromise = new Promise(resolve => {
56
- resolveExecutingPromise = resolve;
57
- });
58
- const promise = execution.execute({
59
- onExecuting: () => resolveExecutingPromise()
60
- });
61
- await Promise.race([promise, executingPromise]);
62
- executing = false;
63
- if (executionQueue.length) {
64
- const nextExecution = executionQueue.shift();
65
- execThenDequeue(nextExecution);
66
- }
67
- };
68
- const asExecutionId = src => {
69
- const url = new URL(src, window.location).href;
70
- if (url.startsWith(window.location.origin)) {
71
- return src;
72
- }
73
- return url;
74
- };
75
- const createExecutionController = (src, type) => {
76
- const result = {
77
- status: "pending",
78
- duration: null,
79
- coverage: null,
80
- exception: null,
81
- value: null
82
- };
83
- let resolve;
84
- const promise = new Promise(_resolve => {
85
- resolve = _resolve;
86
- });
87
- promises.push(promise);
88
- executionResults[src] = result;
89
- const start = () => {
90
- result.duration = null;
91
- result.coverage = null;
92
- result.status = "started";
93
- result.exception = null;
94
- if (logs) {
95
- console.group(`[jsenv] ${src} execution started (${type})`);
96
- }
97
- };
98
- const end = () => {
99
- const now = Date.now();
100
- remainingScriptCount--;
101
- result.duration = now - documentExecutionStartTime;
102
- result.coverage = window.__coverage__;
103
- if (logs) {
104
- console.log(`execution ${result.status}`);
105
- console.groupEnd();
106
- }
107
- if (remainingScriptCount === 0) {
108
- documentExecutionEndTime = now;
109
- }
110
- resolve();
111
- };
112
- const complete = () => {
113
- result.status = "completed";
114
- end();
115
- };
116
- const fail = (error, info) => {
117
- result.status = "failed";
118
- const exception = supervisor.createException(error, info);
119
- result.exception = exception;
120
- end();
121
- };
122
- return {
123
- result,
124
- start,
125
- complete,
126
- fail
127
- };
128
- };
129
- const prepareJsClassicRemoteExecution = src => {
130
- const urlObject = new URL(src, window.location);
131
- const url = urlObject.href;
132
- const {
133
- result,
134
- start,
135
- complete,
136
- fail
137
- } = createExecutionController(src, "js_classic");
138
- let parentNode;
139
- let currentScript;
140
- let nodeToReplace;
141
- let currentScriptClone;
142
- const init = () => {
143
- currentScript = document.currentScript;
144
- parentNode = currentScript.parentNode;
145
- executions[src].async = currentScript.async;
146
- };
147
- const execute = async ({
148
- isReload
149
- } = {}) => {
150
- start();
151
- currentScriptClone = prepareScriptToLoad(currentScript);
152
- if (isReload) {
153
- urlObject.searchParams.set("hmr", Date.now());
154
- nodeToReplace = currentScriptClone;
155
- currentScriptClone.src = urlObject.href;
156
- } else {
157
- nodeToReplace = currentScript;
158
- currentScriptClone.src = url;
159
- }
160
- const scriptLoadPromise = getScriptLoadPromise(currentScriptClone);
161
- parentNode.replaceChild(currentScriptClone, nodeToReplace);
162
- const {
163
- detectedBy,
164
- failed,
165
- error
166
- } = await scriptLoadPromise;
167
- if (failed) {
168
- if (detectedBy === "script_error_event") {
169
- // window.error won't be dispatched for this error
170
- reportErrorBackToBrowser(error);
171
- }
172
- fail(error, {
173
- message: `Error while loading script: ${urlObject.href}`,
174
- reportedBy: "script_error_event",
175
- url: urlObject.href
176
- });
177
- if (detectedBy === "script_error_event") {
178
- supervisor.reportException(result.exception);
179
- }
180
- } else {
181
- complete();
182
- }
183
- return result;
184
- };
185
- executions[src] = {
186
- init,
187
- execute
188
- };
189
- };
190
- const prepareJsClassicInlineExecution = src => {
191
- const {
192
- start,
193
- complete,
194
- fail
195
- } = createExecutionController(src, "js_classic");
196
- const end = complete;
197
- const error = e => {
198
- reportErrorBackToBrowser(e); // supervision shallowed the error, report back to browser
199
- fail(e);
200
- };
201
- executions[src] = {
202
- isInline: true,
203
- start,
204
- end,
205
- error
206
- };
207
- };
208
- const isWebkitOrSafari = typeof window.webkitConvertPointFromNodeToPage === "function";
209
- // https://twitter.com/damienmaillard/status/1554752482273787906
210
- const prepareJsModuleExecutionWithDynamicImport = src => {
211
- const urlObject = new URL(src, window.location);
212
- const {
213
- result,
214
- start,
215
- complete,
216
- fail
217
- } = createExecutionController(src, "js_classic");
218
- let importFn;
219
- let currentScript;
220
- const init = _importFn => {
221
- importFn = _importFn;
222
- currentScript = document.querySelector(`script[type="module"][inlined-from-src="${src}"]`);
223
- executions[src].async = currentScript.async;
224
- };
225
- const execute = async ({
226
- isReload
227
- } = {}) => {
228
- start();
229
- if (isReload) {
230
- urlObject.searchParams.set("hmr", Date.now());
231
- }
232
- try {
233
- const namespace = await importFn(urlObject.href);
234
- complete(namespace);
235
- return result;
236
- } catch (e) {
237
- fail(e, {
238
- message: `Error while importing module: ${urlObject.href}`,
239
- reportedBy: "dynamic_import",
240
- url: urlObject.href
241
- });
242
- if (isWebkitOrSafari) {
243
- supervisor.reportException(result.exception);
244
- }
245
- return result;
246
- }
247
- };
248
- executions[src] = {
249
- init,
250
- execute
251
- };
252
- };
253
- const prepareJsModuleExecutionWithScriptThenDynamicImport = src => {
254
- const urlObject = new URL(src, window.location);
255
- const {
256
- result,
257
- start,
258
- complete,
259
- fail
260
- } = createExecutionController(src, "js_module");
261
- let importFn;
262
- let currentScript;
263
- let parentNode;
264
- let nodeToReplace;
265
- let currentScriptClone;
266
- const init = _importFn => {
267
- importFn = _importFn;
268
- currentScript = document.querySelector(`script[type="module"][inlined-from-src="${src}"]`);
269
- parentNode = currentScript.parentNode;
270
- executions[src].async = currentScript.async;
271
- };
272
- const execute = async ({
273
- isReload,
274
- onExecuting = () => {}
275
- } = {}) => {
276
- start();
277
- currentScriptClone = prepareScriptToLoad(currentScript);
278
- if (isReload) {
279
- urlObject.searchParams.set("hmr", Date.now());
280
- nodeToReplace = currentScriptClone;
281
- currentScriptClone.src = urlObject.href;
282
- } else {
283
- nodeToReplace = currentScript;
284
- currentScriptClone.src = urlObject.href;
285
- }
286
- const scriptLoadResultPromise = getScriptLoadPromise(currentScriptClone);
287
- parentNode.replaceChild(currentScriptClone, nodeToReplace);
288
- const {
289
- detectedBy,
290
- failed,
291
- error
292
- } = await scriptLoadResultPromise;
293
- if (failed) {
294
- // if (detectedBy === "script_error_event") {
295
- // reportErrorBackToBrowser(error)
296
- // }
297
- fail(error, {
298
- message: `Error while loading module: ${urlObject.href}`,
299
- reportedBy: "script_error_event",
300
- url: urlObject.href
301
- });
302
- if (detectedBy === "script_error_event") {
303
- supervisor.reportException(result.exception);
304
- }
305
- return result;
306
- }
307
- onExecuting();
308
- result.status = "executing";
309
- if (logs) {
310
- console.log(`load ended`);
311
- }
312
- try {
313
- const namespace = await importFn(urlObject.href);
314
- complete(namespace);
315
- return result;
316
- } catch (e) {
317
- fail(e, {
318
- message: `Error while importing module: ${urlObject.href}`,
319
- reportedBy: "dynamic_import",
320
- url: urlObject.href
321
- });
322
- return result;
323
- }
324
- };
325
- executions[src] = {
326
- init,
327
- execute
328
- };
329
- };
330
- const prepareJsModuleRemoteExecution = isWebkitOrSafari ? prepareJsModuleExecutionWithDynamicImport : prepareJsModuleExecutionWithScriptThenDynamicImport;
331
- const prepareJsModuleInlineExecution = src => {
332
- const {
333
- start,
334
- complete,
335
- fail
336
- } = createExecutionController(src, "js_module");
337
- const end = complete;
338
- const error = e => {
339
- // supervision shallowed the error, report back to browser
340
- reportErrorBackToBrowser(e);
341
- fail(e);
342
- };
343
- executions[src] = {
344
- isInline: true,
345
- start,
346
- end,
347
- error
348
- };
349
- };
350
- supervisor.setupReportException({
351
- logs,
352
- serverIsJsenvDevServer,
353
- rootDirectoryUrl,
354
- errorOverlay,
355
- errorBaseUrl,
356
- openInEditor
357
- });
358
- scriptInfos.forEach(scriptInfo => {
359
- const {
360
- type,
361
- src,
362
- isInline
363
- } = scriptInfo;
364
- if (type === "js_module") {
365
- if (isInline) {
366
- prepareJsModuleInlineExecution(src);
367
- } else {
368
- prepareJsModuleRemoteExecution(src);
369
- }
370
- } else if (isInline) {
371
- prepareJsClassicInlineExecution(src);
372
- } else {
373
- prepareJsClassicRemoteExecution(src);
374
- }
375
- });
376
-
377
- // js classic
378
- supervisor.jsClassicStart = inlineSrc => {
379
- executions[inlineSrc].start();
380
- };
381
- supervisor.jsClassicEnd = inlineSrc => {
382
- executions[inlineSrc].end();
383
- };
384
- supervisor.jsClassicError = (inlineSrc, e) => {
385
- executions[inlineSrc].error(e);
386
- };
387
- supervisor.superviseScript = src => {
388
- const execution = executions[asExecutionId(src)];
389
- execution.init();
390
- return addToExecutionQueue(execution);
391
- };
392
- // js module
393
- supervisor.jsModuleStart = inlineSrc => {
394
- executions[inlineSrc].start();
395
- };
396
- supervisor.jsModuleEnd = inlineSrc => {
397
- executions[inlineSrc].end();
398
- };
399
- supervisor.jsModuleError = (inlineSrc, e) => {
400
- executions[inlineSrc].error(e);
401
- };
402
- supervisor.superviseScriptTypeModule = (src, importFn) => {
403
- const execution = executions[asExecutionId(src)];
404
- execution.init(importFn);
405
- return addToExecutionQueue(execution);
406
- };
407
- supervisor.reloadSupervisedScript = src => {
408
- const execution = executions[src];
409
- if (!execution) {
410
- throw new Error(`no execution for ${src}`);
411
- }
412
- if (execution.isInline) {
413
- throw new Error(`cannot reload inline script ${src}`);
414
- }
415
- return execution.execute({
416
- isReload: true
417
- });
418
- };
419
- supervisor.getDocumentExecutionResult = async () => {
420
- await Promise.all(promises);
421
- return {
422
- startTime: documentExecutionStartTime,
423
- endTime: documentExecutionEndTime,
424
- status: "completed",
425
- executionResults
426
- };
427
- };
428
- };
429
- const reportErrorBackToBrowser = error => {
430
- if (typeof window.reportError === "function") {
431
- window.reportError(error);
432
- } else {
433
- console.error(error);
434
- }
435
- };
436
- supervisor.setupReportException = ({
437
- logs,
438
- rootDirectoryUrl,
439
- serverIsJsenvDevServer,
440
- errorNotification,
441
- errorOverlay,
442
- errorBaseUrl,
443
- openInEditor
444
- }) => {
445
- const DYNAMIC_IMPORT_FETCH_ERROR = "dynamic_import_fetch_error";
446
- const DYNAMIC_IMPORT_EXPORT_MISSING = "dynamic_import_export_missing";
447
- const DYNAMIC_IMPORT_SYNTAX_ERROR = "dynamic_import_syntax_error";
448
- const createException = (reason,
449
- // can be error, string, object
450
- {
451
- message,
452
- reportedBy,
453
- url,
454
- line,
455
- column
456
- } = {}) => {
457
- const exception = {
458
- reason,
459
- isError: false,
460
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw
461
- reportedBy,
462
- code: null,
463
- message: null,
464
- stack: null,
465
- stackFormatIsV8: null,
466
- stackSourcemapped: null,
467
- originalStack: null,
468
- meta: null,
469
- site: {
470
- isInline: null,
471
- url: null,
472
- line: null,
473
- column: null,
474
- originalUrl: null
475
- }
476
- };
477
- const writeBasicProperties = () => {
478
- if (reason === undefined) {
479
- exception.message = "undefined";
480
- return;
481
- }
482
- if (reason === null) {
483
- exception.message = "null";
484
- return;
485
- }
486
- if (typeof reason === "string") {
487
- exception.message = reason;
488
- return;
489
- }
490
- if (reason instanceof Error) {
491
- const error = reason;
492
- let message = error.message;
493
- exception.isError = true;
494
- if (Error.captureStackTrace) {
495
- // stackTrace formatted by V8
496
- exception.message = message;
497
- exception.stack = getErrorStackWithoutErrorMessage(error);
498
- exception.stackFormatIsV8 = true;
499
- exception.stackSourcemapped = true;
500
- } else {
501
- exception.message = message;
502
- exception.stack = error.stack ? ` ${error.stack}` : null;
503
- exception.stackFormatIsV8 = false;
504
- exception.stackSourcemapped = false;
505
- }
506
- if (error.reportedBy) {
507
- exception.reportedBy = error.reportedBy;
508
- }
509
- if (error.url) {
510
- Object.assign(exception.site, resolveUrlSite({
511
- url: error.url
512
- }));
513
- }
514
- {
515
- // chrome
516
- if (message.includes("does not provide an export named")) {
517
- exception.code = DYNAMIC_IMPORT_EXPORT_MISSING;
518
- return;
519
- }
520
- // firefox
521
- if (message.startsWith("import not found:") || message.startsWith("ambiguous indirect export:")) {
522
- exception.code = DYNAMIC_IMPORT_EXPORT_MISSING;
523
- return;
524
- }
525
- // safari
526
- if (message.startsWith("import binding name")) {
527
- exception.code = DYNAMIC_IMPORT_EXPORT_MISSING;
528
- return;
529
- }
530
- if (message.includes("Importing a module script failed")) {
531
- exception.code = DYNAMIC_IMPORT_FETCH_ERROR;
532
- return;
533
- }
534
- }
535
- {
536
- if (error.name === "SyntaxError" && typeof line === "number") {
537
- exception.code = DYNAMIC_IMPORT_SYNTAX_ERROR;
538
- return;
539
- }
540
- }
541
- return;
542
- }
543
- if (typeof reason === "object") {
544
- // happens when reason is an Event for instance
545
- exception.code = reason.code;
546
- exception.message = reason.message || message;
547
- exception.stack = reason.stack;
548
- if (reason.reportedBy) {
549
- exception.reportedBy = reason.reportedBy;
550
- }
551
- if (reason.url) {
552
- Object.assign(exception.site, resolveUrlSite({
553
- url: reason.url
554
- }));
555
- }
556
- return;
557
- }
558
- exception.message = JSON.stringify(reason);
559
- };
560
- writeBasicProperties();
561
-
562
- // first create a version of the stack with file://
563
- // (and use it to locate exception url+line+column)
564
- if (exception.stack) {
565
- exception.originalStack = exception.stack;
566
- exception.stack = replaceUrls(exception.originalStack, serverUrlSite => {
567
- const fileUrlSite = resolveUrlSite(serverUrlSite);
568
- if (exception.site.url === null) {
569
- Object.assign(exception.site, fileUrlSite);
570
- }
571
- return stringifyUrlSite(fileUrlSite);
572
- });
573
- }
574
- // then if it fails, use url+line+column passed
575
- if (exception.site.url === null && url) {
576
- if (typeof line === "string") {
577
- line = parseInt(line);
578
- }
579
- if (typeof column === "string") {
580
- column = parseInt(column);
581
- }
582
- const fileUrlSite = resolveUrlSite({
583
- url,
584
- line,
585
- column
586
- });
587
- if (fileUrlSite.isInline && exception.code === DYNAMIC_IMPORT_SYNTAX_ERROR) {
588
- // syntax error on inline script need line-1 for some reason
589
- fileUrlSite.line = fileUrlSite.line - 1;
590
- }
591
- Object.assign(exception.site, fileUrlSite);
592
- }
593
- exception.text = stringifyMessageAndStack(exception);
594
- return exception;
595
- };
596
- const stringifyMessageAndStack = ({
597
- message,
598
- stack
599
- }) => {
600
- if (message && stack) {
601
- return `${message}\n${stack}`;
602
- }
603
- if (stack) {
604
- return stack;
605
- }
606
- return message;
607
- };
608
- const stringifyUrlSite = ({
609
- url,
610
- line,
611
- column
612
- }) => {
613
- if (typeof line === "number" && typeof column === "number") {
614
- return `${url}:${line}:${column}`;
615
- }
616
- if (typeof line === "number") {
617
- return `${url}:${line}`;
618
- }
619
- return url;
620
- };
621
- const resolveUrlSite = ({
622
- url,
623
- line,
624
- column
625
- }) => {
626
- const inlineUrlMatch = url.match(/@L([0-9]+)C([0-9]+)\-L([0-9]+)C([0-9]+)(\.[\w]+)$/);
627
- if (inlineUrlMatch) {
628
- const htmlUrl = url.slice(0, inlineUrlMatch.index);
629
- const tagLineStart = parseInt(inlineUrlMatch[1]);
630
- const tagColumnStart = parseInt(inlineUrlMatch[2]);
631
- const tagLineEnd = parseInt(inlineUrlMatch[3]);
632
- const tagColumnEnd = parseInt(inlineUrlMatch[4]);
633
- const extension = inlineUrlMatch[5];
634
- url = htmlUrl;
635
- line = tagLineStart + (typeof line === "number" ? line : 0);
636
- line = line - 1; // sauf pour les erreur de syntaxe
637
- column = tagColumnStart + (typeof column === "number" ? column : 0);
638
- const fileUrl = resolveFileUrl(url);
639
- return {
640
- isInline: true,
641
- serverUrl: url,
642
- originalUrl: `${fileUrl}@L${tagLineStart}C${tagColumnStart}-L${tagLineEnd}C${tagColumnEnd}${extension}`,
643
- url: fileUrl,
644
- line,
645
- column
646
- };
647
- }
648
- return {
649
- isInline: false,
650
- serverUrl: url,
651
- url: resolveFileUrl(url),
652
- line,
653
- column
654
- };
655
- };
656
- const getErrorStackWithoutErrorMessage = error => {
657
- let stack = error.stack;
658
- if (!stack) return "";
659
- const messageInStack = `${error.name}: ${error.message}`;
660
- if (stack.startsWith(messageInStack)) {
661
- stack = stack.slice(messageInStack.length);
662
- }
663
- const nextLineIndex = stack.indexOf("\n");
664
- if (nextLineIndex > -1) {
665
- stack = stack.slice(nextLineIndex + 1);
666
- }
667
- return stack;
668
- };
669
- const resolveFileUrl = url => {
670
- let urlObject = new URL(url, window.origin);
671
- if (urlObject.origin === window.origin) {
672
- urlObject = new URL(`${urlObject.pathname.slice(1)}${urlObject.search}`, rootDirectoryUrl);
673
- }
674
- if (urlObject.href.startsWith("file:")) {
675
- const atFsIndex = urlObject.pathname.indexOf("/@fs/");
676
- if (atFsIndex > -1) {
677
- const afterAtFs = urlObject.pathname.slice(atFsIndex + "/@fs/".length);
678
- return new URL(afterAtFs, "file:///").href;
679
- }
680
- }
681
- return urlObject.href;
682
- };
683
-
684
- // `Error: yo
685
- // at Object.execute (http://127.0.0.1:57300/build/src/__test__/file-throw.js:9:13)
686
- // at doExec (http://127.0.0.1:3000/src/__test__/file-throw.js:452:38)
687
- // at postOrderExec (http://127.0.0.1:3000/src/__test__/file-throw.js:448:16)
688
- // at http://127.0.0.1:3000/src/__test__/file-throw.js:399:18`.replace(/(?:https?|ftp|file):\/\/(.*+)$/gm, (...args) => {
689
- // debugger
690
- // })
691
- const replaceUrls = (source, replace) => {
692
- return source.replace(/(?:https?|ftp|file):\/\/\S+/gm, match => {
693
- let replacement = "";
694
- const lastChar = match[match.length - 1];
695
-
696
- // hotfix because our url regex sucks a bit
697
- const endsWithSeparationChar = lastChar === ")" || lastChar === ":";
698
- if (endsWithSeparationChar) {
699
- match = match.slice(0, -1);
700
- }
701
- const lineAndColumnPattern = /:([0-9]+):([0-9]+)$/;
702
- const lineAndColumMatch = match.match(lineAndColumnPattern);
703
- if (lineAndColumMatch) {
704
- const lineAndColumnString = lineAndColumMatch[0];
705
- const lineString = lineAndColumMatch[1];
706
- const columnString = lineAndColumMatch[2];
707
- replacement = replace({
708
- url: match.slice(0, -lineAndColumnString.length),
709
- line: lineString ? parseInt(lineString) : null,
710
- column: columnString ? parseInt(columnString) : null
711
- });
712
- } else {
713
- const linePattern = /:([0-9]+)$/;
714
- const lineMatch = match.match(linePattern);
715
- if (lineMatch) {
716
- const lineString = lineMatch[0];
717
- replacement = replace({
718
- url: match.slice(0, -lineString.length),
719
- line: lineString ? parseInt(lineString) : null
720
- });
721
- } else {
722
- replacement = replace({
723
- url: match
724
- });
725
- }
726
- }
727
- if (endsWithSeparationChar) {
728
- return `${replacement}${lastChar}`;
729
- }
730
- return replacement;
731
- });
732
- };
733
- let formatError;
734
- {
735
- formatError = exceptionInfo => {
736
- const errorParts = {
737
- theme: "dark",
738
- title: "An error occured",
739
- text: "",
740
- tip: "",
741
- errorDetailsPromise: null
742
- };
743
- const tips = [];
744
- tips.push("Click outside to close.");
745
- errorParts.tip = tips.join(`\n <br />\n `);
746
- const generateClickableText = text => {
747
- const textWithHtmlLinks = makeLinksClickable(text, {
748
- createLink: ({
749
- url,
750
- line,
751
- column
752
- }) => {
753
- const urlSite = resolveUrlSite({
754
- url,
755
- line,
756
- column
757
- });
758
- if (errorBaseUrl) {
759
- if (urlSite.url.startsWith(rootDirectoryUrl)) {
760
- urlSite.url = `${errorBaseUrl}${urlSite.url.slice(rootDirectoryUrl.length)}`;
761
- } else {
762
- urlSite.url = "file:///mocked_for_snapshots";
763
- }
764
- }
765
- const urlWithLineAndColumn = stringifyUrlSite(urlSite);
766
- return {
767
- href: urlSite.url.startsWith("file:") && openInEditor ? `javascript:window.fetch('/__open_in_editor__/${encodeURIComponent(urlWithLineAndColumn)}')` : urlSite.url,
768
- text: urlWithLineAndColumn
769
- };
770
- }
771
- });
772
- return textWithHtmlLinks;
773
- };
774
- errorParts.text = stringifyMessageAndStack({
775
- message: exceptionInfo.message ? generateClickableText(exceptionInfo.message) : "",
776
- stack: exceptionInfo.stack ? generateClickableText(exceptionInfo.stack) : ""
777
- });
778
- if (exceptionInfo.site.url) {
779
- errorParts.errorDetailsPromise = (async () => {
780
- if (!serverIsJsenvDevServer) {
781
- return null;
782
- }
783
- try {
784
- if (exceptionInfo.code === DYNAMIC_IMPORT_FETCH_ERROR || exceptionInfo.reportedBy === "script_error_event") {
785
- const response = await window.fetch(`/__get_error_cause__/${encodeURIComponent(exceptionInfo.site.isInline ? exceptionInfo.site.originalUrl : exceptionInfo.site.url)}`);
786
- if (response.status !== 200) {
787
- return null;
788
- }
789
- const causeInfo = await response.json();
790
- if (!causeInfo) {
791
- return null;
792
- }
793
- const causeText = causeInfo.code === "NOT_FOUND" ? stringifyMessageAndStack({
794
- message: generateClickableText(causeInfo.reason),
795
- stack: generateClickableText(causeInfo.codeFrame)
796
- }) : stringifyMessageAndStack({
797
- message: generateClickableText(causeInfo.stack),
798
- stack: generateClickableText(causeInfo.codeFrame)
799
- });
800
- return {
801
- cause: causeText
802
- };
803
- }
804
- if (exceptionInfo.site.line !== undefined) {
805
- const urlToFetch = new URL(`/__get_code_frame__/${encodeURIComponent(stringifyUrlSite(exceptionInfo.site))}`, window.origin);
806
- if (!exceptionInfo.stackSourcemapped) {
807
- urlToFetch.searchParams.set("remap", "");
808
- }
809
- const response = await window.fetch(urlToFetch);
810
- if (response.status !== 200) {
811
- return null;
812
- }
813
- const codeFrame = await response.text();
814
- return {
815
- codeFrame: generateClickableText(codeFrame)
816
- };
817
- }
818
- } catch (e) {
819
- // happens if server is closed for instance
820
- return null;
821
- }
822
- return null;
823
- })();
824
- }
825
- return errorParts;
826
- };
827
- const makeLinksClickable = (string, {
828
- createLink = ({
829
- url
830
- }) => url
831
- }) => {
832
- // normalize line breaks
833
- string = string.replace(/\n/g, "\n");
834
- string = escapeHtml(string);
835
- // render links
836
- string = replaceUrls(string, ({
837
- url,
838
- line,
839
- column
840
- }) => {
841
- const {
842
- href,
843
- text
844
- } = createLink({
845
- url,
846
- line,
847
- column
848
- });
849
- return link({
850
- href,
851
- text
852
- });
853
- });
854
- return string;
855
- };
856
- const escapeHtml = string => {
857
- return string.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
858
- };
859
- const link = ({
860
- href,
861
- text = href
862
- }) => `<a href="${href}">${text}</a>`;
863
- }
864
- let displayErrorNotification;
865
- {
866
- const {
867
- Notification
868
- } = window;
869
- displayErrorNotification = typeof Notification === "function" ? ({
870
- title,
871
- text,
872
- icon
873
- }) => {
874
- if (Notification.permission === "granted") {
875
- const notification = new Notification(title, {
876
- lang: "en",
877
- body: text,
878
- icon
879
- });
880
- notification.onclick = () => {
881
- window.focus();
882
- };
883
- }
884
- } : () => {};
885
- }
886
- const JSENV_ERROR_OVERLAY_TAGNAME = "jsenv-error-overlay";
887
- let displayJsenvErrorOverlay;
888
- {
889
- displayJsenvErrorOverlay = params => {
890
- if (logs) {
891
- console.log("display jsenv error overlay", params);
892
- }
893
- let jsenvErrorOverlay = new JsenvErrorOverlay(params);
894
- document.querySelectorAll(JSENV_ERROR_OVERLAY_TAGNAME).forEach(node => {
895
- node.parentNode.removeChild(node);
896
- });
897
- document.body.appendChild(jsenvErrorOverlay);
898
- const removeErrorOverlay = () => {
899
- if (jsenvErrorOverlay && jsenvErrorOverlay.parentNode) {
900
- document.body.removeChild(jsenvErrorOverlay);
901
- jsenvErrorOverlay = null;
902
- }
903
- };
904
- return removeErrorOverlay;
905
- };
906
- class JsenvErrorOverlay extends HTMLElement {
907
- constructor({
908
- theme,
909
- title,
910
- text,
911
- tip,
912
- errorDetailsPromise
913
- }) {
914
- super();
915
- this.root = this.attachShadow({
916
- mode: "open"
917
- });
918
- this.root.innerHTML = `
919
- <style>
920
- ${overlayCSS}
921
- </style>
922
- <div class="backdrop"></div>
923
- <div class="overlay" data-theme=${theme}>
924
- <h1 class="title">
925
- ${title}
926
- </h1>
927
- <pre class="text">${text}</pre>
928
- <div class="tip">
929
- ${tip}
930
- </div>
931
- </div>`;
932
- this.root.querySelector(".backdrop").onclick = () => {
933
- if (!this.parentNode) {
934
- // not in document anymore
935
- return;
936
- }
937
- this.root.querySelector(".backdrop").onclick = null;
938
- this.parentNode.removeChild(this);
939
- };
940
- if (errorDetailsPromise) {
941
- errorDetailsPromise.then(errorDetails => {
942
- if (!errorDetails || !this.parentNode) {
943
- return;
944
- }
945
- const {
946
- codeFrame,
947
- cause
948
- } = errorDetails;
949
- if (codeFrame) {
950
- this.root.querySelector(".text").innerHTML += `\n\n${codeFrame}`;
951
- }
952
- if (cause) {
953
- const causeIndented = prefixRemainingLines(cause, " ");
954
- this.root.querySelector(".text").innerHTML += `\n [cause]: ${causeIndented}`;
955
- }
956
- });
957
- }
958
- }
959
- }
960
- const prefixRemainingLines = (text, prefix) => {
961
- const lines = text.split(/\r?\n/);
962
- const firstLine = lines.shift();
963
- let result = firstLine;
964
- let i = 0;
965
- while (i < lines.length) {
966
- const line = lines[i];
967
- i++;
968
- result += line.length ? `\n${prefix}${line}` : `\n`;
969
- }
970
- return result;
971
- };
972
- if (customElements && !customElements.get(JSENV_ERROR_OVERLAY_TAGNAME)) {
973
- customElements.define(JSENV_ERROR_OVERLAY_TAGNAME, JsenvErrorOverlay);
974
- }
975
- const overlayCSS = `
976
- :host {
977
- position: fixed;
978
- z-index: 99999;
979
- top: 0;
980
- left: 0;
981
- width: 100%;
982
- height: 100%;
983
- overflow-y: scroll;
984
- margin: 0;
985
- background: rgba(0, 0, 0, 0.66);
986
- }
987
-
988
- .backdrop {
989
- position: absolute;
990
- left: 0;
991
- right: 0;
992
- top: 0;
993
- bottom: 0;
994
- }
995
-
996
- .overlay {
997
- position: relative;
998
- background: rgba(0, 0, 0, 0.95);
999
- width: 800px;
1000
- margin: 30px auto;
1001
- padding: 25px 40px;
1002
- padding-top: 0;
1003
- overflow: hidden; /* for h1 margins */
1004
- border-radius: 4px 8px;
1005
- box-shadow: 0 20px 40px rgb(0 0 0 / 30%), 0 15px 12px rgb(0 0 0 / 20%);
1006
- box-sizing: border-box;
1007
- font-family: monospace;
1008
- direction: ltr;
1009
- }
1010
-
1011
- h1 {
1012
- color: red;
1013
- text-align: center;
1014
- }
1015
-
1016
- pre {
1017
- overflow: auto;
1018
- max-width: 100%;
1019
- /* padding is nice + prevents scrollbar from hiding the text behind it */
1020
- /* does not work nicely on firefox though https://bugzilla.mozilla.org/show_bug.cgi?id=748518 */
1021
- padding: 20px;
1022
- }
1023
-
1024
- .tip {
1025
- border-top: 1px solid #999;
1026
- padding-top: 12px;
1027
- }
1028
-
1029
- [data-theme="dark"] {
1030
- color: #999;
1031
- }
1032
- [data-theme="dark"] pre {
1033
- background: #111;
1034
- border: 1px solid #333;
1035
- color: #eee;
1036
- }
1037
-
1038
- [data-theme="light"] {
1039
- color: #EEEEEE;
1040
- }
1041
- [data-theme="light"] pre {
1042
- background: #1E1E1E;
1043
- border: 1px solid white;
1044
- color: #EEEEEE;
1045
- }
1046
-
1047
- pre a {
1048
- color: inherit;
1049
- }`;
1050
- }
1051
- supervisor.createException = createException;
1052
- supervisor.reportException = exception => {
1053
- const {
1054
- theme,
1055
- title,
1056
- text,
1057
- tip,
1058
- errorDetailsPromise
1059
- } = formatError(exception);
1060
- if (errorOverlay) {
1061
- const removeErrorOverlay = displayJsenvErrorOverlay({
1062
- theme,
1063
- title,
1064
- text,
1065
- tip,
1066
- errorDetailsPromise
1067
- });
1068
- if (window.__reloader__) {
1069
- const onchange = window.__reloader__.status.onchange;
1070
- window.__reloader__.status.onchange = () => {
1071
- onchange();
1072
- if (window.__reloader__.status.value === "reloading") {
1073
- removeErrorOverlay();
1074
- }
1075
- };
1076
- }
1077
- }
1078
- if (errorNotification) {
1079
- displayErrorNotification({
1080
- title,
1081
- text
1082
- });
1083
- }
1084
- return exception;
1085
- };
1086
- window.addEventListener("error", errorEvent => {
1087
- if (!errorEvent.isTrusted) {
1088
- // ignore custom error event (not sent by browser)
1089
- if (logs) {
1090
- console.log("ignore non trusted error event", errorEvent);
1091
- }
1092
- return;
1093
- }
1094
- if (logs) {
1095
- console.log('window "error" event received', errorEvent);
1096
- }
1097
- const {
1098
- error,
1099
- message,
1100
- filename,
1101
- lineno,
1102
- colno
1103
- } = errorEvent;
1104
- const exception = supervisor.createException(error || message, {
1105
- // when error is reported within a worker error is null
1106
- // but there is a message property on errorEvent
1107
- reportedBy: "window_error_event",
1108
- url: filename,
1109
- line: lineno,
1110
- column: colno
1111
- });
1112
- supervisor.reportException(exception);
1113
- });
1114
- window.addEventListener("unhandledrejection", event => {
1115
- if (event.defaultPrevented) {
1116
- return;
1117
- }
1118
- const exception = supervisor.createException(event.reason, {
1119
- reportedBy: "window_unhandledrejection_event"
1120
- });
1121
- supervisor.reportException(exception);
1122
- });
1123
- };
1124
- const prepareScriptToLoad = script => {
1125
- // do not use script.cloneNode()
1126
- // bcause https://stackoverflow.com/questions/28771542/why-dont-clonenode-script-tags-execute
1127
- const scriptClone = document.createElement("script");
1128
- // browsers set async by default when creating script(s)
1129
- // we want an exact copy to preserves how code is executed
1130
- scriptClone.async = false;
1131
- Array.from(script.attributes).forEach(attribute => {
1132
- scriptClone.setAttribute(attribute.nodeName, attribute.nodeValue);
1133
- });
1134
- scriptClone.removeAttribute("jsenv-cooked-by");
1135
- scriptClone.removeAttribute("jsenv-inlined-by");
1136
- scriptClone.removeAttribute("jsenv-injected-by");
1137
- scriptClone.removeAttribute("inlined-from-src");
1138
- scriptClone.removeAttribute("original-position");
1139
- scriptClone.removeAttribute("original-src-position");
1140
- return scriptClone;
1141
- };
1142
- const getScriptLoadPromise = async script => {
1143
- return new Promise(resolve => {
1144
- const windowErrorEventCallback = errorEvent => {
1145
- if (errorEvent.filename === script.src) {
1146
- removeWindowErrorEventCallback();
1147
- resolve({
1148
- detectedBy: "window_error_event",
1149
- failed: true,
1150
- error: errorEvent
1151
- });
1152
- }
1153
- };
1154
- const removeWindowErrorEventCallback = () => {
1155
- window.removeEventListener("error", windowErrorEventCallback);
1156
- };
1157
- window.addEventListener("error", windowErrorEventCallback);
1158
- script.addEventListener("error", errorEvent => {
1159
- removeWindowErrorEventCallback();
1160
- resolve({
1161
- detectedBy: "script_error_event",
1162
- failed: true,
1163
- error: errorEvent
1164
- });
1165
- });
1166
- script.addEventListener("load", () => {
1167
- removeWindowErrorEventCallback();
1168
- resolve({
1169
- detectedBy: "script_load_event"
1170
- });
1171
- });
1172
- });
1173
- };
1174
- return supervisor;
1175
- })();