@jsenv/core 27.4.0 → 27.5.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.
Files changed (86) hide show
  1. package/dist/js/autoreload.js +359 -0
  2. package/dist/js/execute_using_dynamic_import.js +1 -1
  3. package/dist/js/html_supervisor_installer.js +469 -254
  4. package/dist/js/html_supervisor_setup.js +3 -4
  5. package/dist/js/new_stylesheet.js +26 -58
  6. package/dist/js/server_events_client.js +307 -0
  7. package/dist/main.js +7558 -7311
  8. package/package.json +12 -10
  9. package/{README.md → readme.md} +8 -9
  10. package/src/build/build.js +12 -16
  11. package/src/build/start_build_server.js +24 -28
  12. package/src/dev/start_dev_server.js +34 -96
  13. package/src/execute/execute.js +17 -35
  14. package/src/omega/errors.js +20 -18
  15. package/src/omega/kitchen.js +7 -6
  16. package/src/omega/omega_server.js +96 -127
  17. package/src/omega/server/file_service.js +247 -46
  18. package/src/omega/url_graph.js +33 -20
  19. package/src/plugins/autoreload/client/autoreload.js +201 -0
  20. package/src/plugins/autoreload/{dev_sse/client → client}/autoreload_preference.js +0 -0
  21. package/src/plugins/autoreload/{dev_sse/client → client}/reload.js +29 -10
  22. package/src/plugins/autoreload/{dev_sse/client → client}/url_helpers.js +0 -0
  23. package/src/plugins/autoreload/jsenv_plugin_autoreload.js +4 -4
  24. package/src/plugins/autoreload/{dev_sse/jsenv_plugin_dev_sse_client.js → jsenv_plugin_autoreload_client.js} +8 -8
  25. package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +196 -0
  26. package/src/{dev/plugins → plugins}/explorer/client/explorer.html +0 -0
  27. package/src/{dev/plugins → plugins}/explorer/client/jsenv.png +0 -0
  28. package/src/{dev/plugins → plugins}/explorer/jsenv_plugin_explorer.js +1 -3
  29. package/src/plugins/html_supervisor/client/error_formatter.js +300 -0
  30. package/src/plugins/html_supervisor/client/error_overlay.js +172 -0
  31. package/src/plugins/html_supervisor/client/html_supervisor_installer.js +124 -54
  32. package/src/plugins/html_supervisor/client/html_supervisor_setup.js +3 -4
  33. package/src/plugins/html_supervisor/jsenv_plugin_html_supervisor.js +72 -27
  34. package/src/plugins/inline/jsenv_plugin_html_inline_content.js +97 -117
  35. package/src/plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +66 -58
  36. package/src/plugins/plugin_controller.js +102 -67
  37. package/src/plugins/plugins.js +10 -8
  38. package/src/{helpers/event_source/event_source.js → plugins/server_events/client/event_source_connection.js} +102 -31
  39. package/src/plugins/server_events/client/server_events_client.js +17 -0
  40. package/src/plugins/server_events/jsenv_plugin_server_events_client_injection.js +48 -0
  41. package/src/plugins/server_events/server_events_dispatcher.js +69 -0
  42. package/src/{dev/plugins → plugins}/toolbar/client/animation/toolbar_animation.js +0 -0
  43. package/src/{dev/plugins → plugins}/toolbar/client/eventsource/eventsource.css +0 -0
  44. package/src/{dev/plugins → plugins}/toolbar/client/eventsource/toolbar_eventsource.js +0 -0
  45. package/src/{dev/plugins → plugins}/toolbar/client/execution/execution.css +0 -0
  46. package/src/{dev/plugins → plugins}/toolbar/client/execution/toolbar_execution.js +0 -0
  47. package/src/{dev/plugins → plugins}/toolbar/client/focus/focus.css +0 -0
  48. package/src/{dev/plugins → plugins}/toolbar/client/focus/toolbar_focus.js +0 -0
  49. package/src/{dev/plugins → plugins}/toolbar/client/jsenv_logo.svg +0 -0
  50. package/src/{dev/plugins → plugins}/toolbar/client/notification/toolbar_notification.js +0 -0
  51. package/src/{dev/plugins → plugins}/toolbar/client/responsive/overflow_menu.css +0 -0
  52. package/src/{dev/plugins → plugins}/toolbar/client/responsive/toolbar_responsive.js +0 -0
  53. package/src/{dev/plugins → plugins}/toolbar/client/settings/settings.css +0 -0
  54. package/src/{dev/plugins → plugins}/toolbar/client/settings/toolbar_settings.js +0 -0
  55. package/src/{dev/plugins → plugins}/toolbar/client/theme/jsenv_theme.css +0 -0
  56. package/src/{dev/plugins → plugins}/toolbar/client/theme/light_theme.css +0 -0
  57. package/src/{dev/plugins → plugins}/toolbar/client/theme/toolbar_theme.js +0 -0
  58. package/src/{dev/plugins → plugins}/toolbar/client/toolbar.html +0 -0
  59. package/src/{dev/plugins → plugins}/toolbar/client/toolbar_injector.js +0 -0
  60. package/src/{dev/plugins → plugins}/toolbar/client/toolbar_main.css +0 -0
  61. package/src/{dev/plugins → plugins}/toolbar/client/toolbar_main.js +0 -0
  62. package/src/{dev/plugins → plugins}/toolbar/client/tooltip/tooltip.css +0 -0
  63. package/src/{dev/plugins → plugins}/toolbar/client/tooltip/tooltip.js +0 -0
  64. package/src/{dev/plugins → plugins}/toolbar/client/util/animation.js +0 -0
  65. package/src/{dev/plugins → plugins}/toolbar/client/util/dom.js +0 -0
  66. package/src/{dev/plugins → plugins}/toolbar/client/util/fetch_using_xhr.js +0 -0
  67. package/src/{dev/plugins → plugins}/toolbar/client/util/fetching.js +0 -0
  68. package/src/{dev/plugins → plugins}/toolbar/client/util/iframe_to_parent_href.js +0 -0
  69. package/src/{dev/plugins → plugins}/toolbar/client/util/jsenv_logger.js +0 -0
  70. package/src/{dev/plugins → plugins}/toolbar/client/util/preferences.js +0 -0
  71. package/src/{dev/plugins → plugins}/toolbar/client/util/responsive.js +0 -0
  72. package/src/{dev/plugins → plugins}/toolbar/client/util/util.js +0 -0
  73. package/src/{dev/plugins → plugins}/toolbar/client/variant/variant.js +0 -0
  74. package/src/{dev/plugins → plugins}/toolbar/jsenv_plugin_toolbar.js +0 -0
  75. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_html.js +4 -3
  76. package/src/plugins/transpilation/babel/new_stylesheet/client/new_stylesheet.js +25 -55
  77. package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +44 -24
  78. package/src/plugins/transpilation/jsenv_plugin_transpilation.js +6 -1
  79. package/src/plugins/url_analysis/html/html_urls.js +8 -8
  80. package/src/test/execute_plan.js +36 -54
  81. package/src/test/execute_test_plan.js +2 -2
  82. package/dist/js/event_source_client.js +0 -549
  83. package/src/helpers/event_source/sse_service.js +0 -53
  84. package/src/plugins/autoreload/dev_sse/client/event_source_client.js +0 -193
  85. package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_server.js +0 -192
  86. package/src/plugins/html_supervisor/client/error_in_document.js +0 -345
@@ -10,158 +10,169 @@ const unevalException = value => {
10
10
  });
11
11
  };
12
12
 
13
- const JSENV_ERROR_OVERLAY_TAGNAME = "jsenv-error-overlay";
14
- const displayErrorInDocument = (error, {
13
+ const formatError = (error, {
15
14
  rootDirectoryUrl,
15
+ errorBaseUrl,
16
+ openInEditor,
16
17
  url,
17
18
  line,
18
- column
19
+ column,
20
+ codeFrame,
21
+ requestedRessource,
22
+ reportedBy
19
23
  }) => {
20
- document.querySelectorAll(JSENV_ERROR_OVERLAY_TAGNAME).forEach(node => {
21
- node.parentNode.removeChild(node);
22
- });
23
- const {
24
- theme,
25
- title,
24
+ let {
26
25
  message,
27
26
  stack
28
- } = errorToHTML(error, {
27
+ } = normalizeErrorParts(error);
28
+ let codeFramePromiseReference = {
29
+ current: null
30
+ };
31
+ let tip = formatTip({
32
+ reportedBy,
33
+ requestedRessource
34
+ });
35
+ let errorUrlSite;
36
+
37
+ const resolveUrlSite = ({
29
38
  url,
30
39
  line,
31
40
  column
32
- });
33
- const jsenvErrorOverlay = new JsenvErrorOverlay({
34
- theme,
35
- title,
36
- stack: stack ? `${replaceLinks(message, {
37
- rootDirectoryUrl
38
- })}\n${replaceLinks(stack, {
39
- rootDirectoryUrl
40
- })}` : replaceLinks(message, {
41
- rootDirectoryUrl
42
- })
43
- });
44
- document.body.appendChild(jsenvErrorOverlay);
45
- };
41
+ }) => {
42
+ const inlineUrlMatch = url.match(/@L([0-9]+)\-L([0-9]+)\.[\w]+$/);
43
+
44
+ if (inlineUrlMatch) {
45
+ const htmlUrl = url.slice(0, inlineUrlMatch.index);
46
+ const tagLine = parseInt(inlineUrlMatch[1]);
47
+ const tagColumn = parseInt(inlineUrlMatch[2]);
48
+ url = htmlUrl;
49
+ line = tagLine + parseInt(line) - 1;
50
+ column = tagColumn + parseInt(column);
51
+ }
46
52
 
47
- class JsenvErrorOverlay extends HTMLElement {
48
- constructor({
49
- title,
50
- stack,
51
- theme = "dark"
52
- }) {
53
- super();
54
- this.root = this.attachShadow({
55
- mode: "open"
56
- });
57
- this.root.innerHTML = overlayHtml;
58
- this.root.querySelector(".overlay").setAttribute("data-theme", theme);
59
- this.root.querySelector(".title").innerHTML = title;
60
- this.root.querySelector(".stack").innerHTML = stack;
53
+ let urlObject = new URL(url);
61
54
 
62
- this.root.querySelector(".backdrop").onclick = () => {
63
- if (!this.parentNode) {
64
- // not in document anymore
65
- return;
55
+ if (urlObject.origin === window.origin) {
56
+ urlObject = new URL(`${urlObject.pathname.slice(1)}${urlObject.search}`, rootDirectoryUrl);
57
+ }
58
+
59
+ if (urlObject.href.startsWith("file:")) {
60
+ const atFsIndex = urlObject.pathname.indexOf("/@fs/");
61
+
62
+ if (atFsIndex > -1) {
63
+ const afterAtFs = urlObject.pathname.slice(atFsIndex + "/@fs/".length);
64
+ url = new URL(afterAtFs, "file:///").href;
65
+ } else {
66
+ url = urlObject.href;
66
67
  }
68
+ } else {
69
+ url = urlObject.href;
70
+ }
67
71
 
68
- this.root.querySelector(".backdrop").onclick = null;
69
- this.parentNode.removeChild(this);
72
+ return {
73
+ url,
74
+ line,
75
+ column
70
76
  };
71
- }
77
+ };
72
78
 
73
- }
79
+ const generateClickableText = text => {
80
+ const textWithHtmlLinks = makeLinksClickable(text, {
81
+ createLink: (url, {
82
+ line,
83
+ column
84
+ }) => {
85
+ const urlSite = resolveUrlSite({
86
+ url,
87
+ line,
88
+ column
89
+ });
74
90
 
75
- if (customElements && !customElements.get(JSENV_ERROR_OVERLAY_TAGNAME)) {
76
- customElements.define(JSENV_ERROR_OVERLAY_TAGNAME, JsenvErrorOverlay);
77
- }
91
+ if (!errorUrlSite && text === stack) {
92
+ onErrorLocated(urlSite);
93
+ }
78
94
 
79
- const overlayHtml = `
80
- <style>
81
- :host {
82
- position: fixed;
83
- z-index: 99999;
84
- top: 0;
85
- left: 0;
86
- width: 100%;
87
- height: 100%;
88
- overflow-y: scroll;
89
- margin: 0;
90
- background: rgba(0, 0, 0, 0.66);
91
- }
95
+ if (errorBaseUrl) {
96
+ if (urlSite.url.startsWith(rootDirectoryUrl)) {
97
+ urlSite.url = `${errorBaseUrl}${urlSite.url.slice(rootDirectoryUrl.length)}`;
98
+ } else {
99
+ urlSite.url = "file:///mocked_for_snapshots";
100
+ }
101
+ }
92
102
 
93
- .backdrop {
94
- position: absolute;
95
- left: 0;
96
- right: 0;
97
- top: 0;
98
- bottom: 0;
99
- }
103
+ const urlWithLineAndColumn = formatUrlWithLineAndColumn(urlSite);
104
+ return {
105
+ href: url.startsWith("file:") && openInEditor ? `javascript:window.fetch('/__open_in_editor__/${urlWithLineAndColumn}')` : urlSite.url,
106
+ text: urlWithLineAndColumn
107
+ };
108
+ }
109
+ });
110
+ return textWithHtmlLinks;
111
+ };
100
112
 
101
- .overlay {
102
- position: relative;
103
- background: rgba(0, 0, 0, 0.95);
104
- width: 800px;
105
- margin: 30px auto;
106
- padding: 25px 40px;
107
- padding-top: 0;
108
- overflow: hidden; /* for h1 margins */
109
- border-radius: 4px 8px;
110
- box-shadow: 0 20px 40px rgb(0 0 0 / 30%), 0 15px 12px rgb(0 0 0 / 20%);
111
- box-sizing: border-box;
112
- font-family: monospace;
113
- direction: ltr;
114
- }
113
+ const onErrorLocated = urlSite => {
114
+ errorUrlSite = urlSite;
115
115
 
116
- h1 {
117
- color: red;
118
- text-align: center;
119
- }
116
+ if (codeFrame) {
117
+ return;
118
+ }
120
119
 
121
- pre {
122
- overflow: auto;
123
- max-width: 100%;
124
- /* padding is nice + prevents scrollbar from hiding the text behind it */
125
- /* does not work nicely on firefox though https://bugzilla.mozilla.org/show_bug.cgi?id=748518 */
126
- padding: 20px;
127
- }
120
+ if (reportedBy !== "browser") {
121
+ return;
122
+ }
128
123
 
129
- .tip {
130
- border-top: 1px solid #999;
131
- padding-top: 12px;
132
- }
124
+ codeFramePromiseReference.current = (async () => {
125
+ const response = await window.fetch(`/__get_code_frame__/${formatUrlWithLineAndColumn(urlSite)}`);
126
+ const codeFrame = await response.text();
127
+ const codeFrameClickable = generateClickableText(codeFrame);
128
+ return codeFrameClickable;
129
+ })();
130
+ }; // error.stack is more reliable than url/line/column reported on window error events
131
+ // so use it only when error.stack is not available
133
132
 
134
- [data-theme="dark"] {
135
- color: #999;
136
- }
137
- [data-theme="dark"] pre {
138
- background: #111;
139
- border: 1px solid #333;
140
- color: #eee;
141
- }
142
133
 
143
- [data-theme="light"] {
144
- color: #EEEEEE;
145
- }
146
- [data-theme="light"] pre {
147
- background: #1E1E1E;
148
- border: 1px solid white;
149
- color: #EEEEEE;
150
- }
134
+ if (url && !stack) {
135
+ onErrorLocated(resolveUrlSite({
136
+ url,
137
+ line,
138
+ column
139
+ }));
140
+ }
151
141
 
152
- pre a {
153
- color: inherit;
154
- }
155
- </style>
156
- <div class="backdrop"></div>
157
- <div class="overlay">
158
- <h1 class="title"></h1>
159
- <pre class="stack"></pre>
160
- <div class="tip">Click outside to close.</div>
161
- </div>
162
- `;
163
-
164
- const parseErrorInfo = error => {
142
+ let text;
143
+
144
+ if (message && stack) {
145
+ text = `${generateClickableText(message)}\n${generateClickableText(stack)}`;
146
+ } else if (stack) {
147
+ text = generateClickableText(stack);
148
+ } else {
149
+ text = generateClickableText(message);
150
+ }
151
+
152
+ if (codeFrame) {
153
+ text += `\n\n${generateClickableText(codeFrame)}`;
154
+ }
155
+
156
+ return {
157
+ theme: error && error.cause && error.cause.code === "PARSE_ERROR" ? "light" : "dark",
158
+ title: "An error occured",
159
+ text,
160
+ codeFramePromise: codeFramePromiseReference.current,
161
+ tip: `${tip}
162
+ <br />
163
+ Click outside to close.`
164
+ };
165
+ };
166
+
167
+ const formatUrlWithLineAndColumn = ({
168
+ url,
169
+ line,
170
+ column
171
+ }) => {
172
+ return line === undefined && column === undefined ? url : column === undefined ? `${url}:${line}` : `${url}:${line}:${column}`;
173
+ };
174
+
175
+ const normalizeErrorParts = error => {
165
176
  if (error === undefined) {
166
177
  return {
167
178
  message: "undefined"
@@ -239,35 +250,19 @@ const getErrorStackWithoutErrorMessage = error => {
239
250
  return stack;
240
251
  };
241
252
 
242
- const errorToHTML = (error, {
243
- url,
244
- line,
245
- column
253
+ const formatTip = ({
254
+ reportedBy,
255
+ requestedRessource
246
256
  }) => {
247
- let {
248
- message,
249
- stack
250
- } = parseErrorInfo(error);
251
-
252
- if (url) {
253
- if (!stack || error && error.name === "SyntaxError") {
254
- stack = ` at ${appendLineAndColumn(url, {
255
- line,
256
- column
257
- })}`;
258
- }
257
+ if (reportedBy === "browser") {
258
+ return `Reported by the browser while executing <code>${window.location.pathname}${window.location.search}</code>.`;
259
259
  }
260
260
 
261
- return {
262
- theme: error && error.cause && error.cause.code === "PARSE_ERROR" ? "light" : "dark",
263
- title: "An error occured",
264
- message,
265
- stack
266
- };
261
+ return `Reported by the server while serving <code>${requestedRessource}</code>`;
267
262
  };
268
263
 
269
- const replaceLinks = (string, {
270
- rootDirectoryUrl
264
+ const makeLinksClickable = (string, {
265
+ createLink = url => url
271
266
  }) => {
272
267
  // normalize line breaks
273
268
  string = string.replace(/\n/g, "\n");
@@ -278,44 +273,16 @@ const replaceLinks = (string, {
278
273
  line,
279
274
  column
280
275
  }) => {
281
- const urlObject = new URL(url);
282
-
283
- const onFileUrl = fileUrlObject => {
284
- const atFsIndex = fileUrlObject.pathname.indexOf("/@fs/");
285
- let fileUrl;
286
-
287
- if (atFsIndex > -1) {
288
- const afterAtFs = fileUrlObject.pathname.slice(atFsIndex + "/@fs/".length);
289
- fileUrl = new URL(afterAtFs, "file:///").href;
290
- } else {
291
- fileUrl = fileUrlObject.href;
292
- }
293
-
294
- fileUrl = appendLineAndColumn(fileUrl, {
295
- line,
296
- column
297
- });
298
- return link({
299
- href: `javascript:window.fetch('/__open_in_editor__/${fileUrl}')`,
300
- text: fileUrl
301
- });
302
- };
303
-
304
- if (urlObject.origin === window.origin) {
305
- const fileUrlObject = new URL(`${urlObject.pathname.slice(1)}${urlObject.search}`, rootDirectoryUrl);
306
- return onFileUrl(fileUrlObject);
307
- }
308
-
309
- if (urlObject.href.startsWith("file:")) {
310
- return onFileUrl(urlObject);
311
- }
312
-
276
+ const {
277
+ href,
278
+ text
279
+ } = createLink(url, {
280
+ line,
281
+ column
282
+ });
313
283
  return link({
314
- href: url,
315
- text: appendLineAndColumn(url, {
316
- line,
317
- column
318
- })
284
+ href,
285
+ text
319
286
  });
320
287
  }
321
288
  });
@@ -324,21 +291,6 @@ const replaceLinks = (string, {
324
291
 
325
292
  const escapeHtml = string => {
326
293
  return string.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
327
- };
328
-
329
- const appendLineAndColumn = (url, {
330
- line,
331
- column
332
- }) => {
333
- if (line !== undefined && column !== undefined) {
334
- return `${url}:${line}:${column}`;
335
- }
336
-
337
- if (line !== undefined) {
338
- return `${url}:${line}`;
339
- }
340
-
341
- return url;
342
294
  }; // `Error: yo
343
295
  // at Object.execute (http://127.0.0.1:57300/build/src/__test__/file-throw.js:9:13)
344
296
  // at doExec (http://127.0.0.1:3000/src/__test__/file-throw.js:452:38)
@@ -405,6 +357,193 @@ const link = ({
405
357
  text = href
406
358
  }) => `<a href="${href}">${text}</a>`;
407
359
 
360
+ const JSENV_ERROR_OVERLAY_TAGNAME = "jsenv-error-overlay";
361
+ const displayErrorInDocument = (error, {
362
+ rootDirectoryUrl,
363
+ errorBaseUrl,
364
+ openInEditor,
365
+ url,
366
+ line,
367
+ column,
368
+ codeFrame,
369
+ reportedBy,
370
+ requestedRessource
371
+ }) => {
372
+ const {
373
+ theme,
374
+ title,
375
+ text,
376
+ codeFramePromise,
377
+ tip
378
+ } = formatError(error, {
379
+ rootDirectoryUrl,
380
+ errorBaseUrl,
381
+ openInEditor,
382
+ url,
383
+ line,
384
+ column,
385
+ codeFrame,
386
+ reportedBy,
387
+ requestedRessource
388
+ });
389
+ let jsenvErrorOverlay = new JsenvErrorOverlay({
390
+ theme,
391
+ title,
392
+ text,
393
+ codeFramePromise,
394
+ tip
395
+ });
396
+ document.querySelectorAll(JSENV_ERROR_OVERLAY_TAGNAME).forEach(node => {
397
+ node.parentNode.removeChild(node);
398
+ });
399
+ document.body.appendChild(jsenvErrorOverlay);
400
+
401
+ const removeErrorOverlay = () => {
402
+ if (jsenvErrorOverlay && jsenvErrorOverlay.parentNode) {
403
+ document.body.removeChild(jsenvErrorOverlay);
404
+ jsenvErrorOverlay = null;
405
+ }
406
+ };
407
+
408
+ if (window.__reloader__) {
409
+ window.__reloader__.onstatuschange = () => {
410
+ if (window.__reloader__.status === "reloading") {
411
+ removeErrorOverlay();
412
+ }
413
+ };
414
+ }
415
+
416
+ return removeErrorOverlay;
417
+ };
418
+
419
+ class JsenvErrorOverlay extends HTMLElement {
420
+ constructor({
421
+ theme,
422
+ title,
423
+ text,
424
+ codeFramePromise,
425
+ tip
426
+ }) {
427
+ super();
428
+ this.root = this.attachShadow({
429
+ mode: "open"
430
+ });
431
+ this.root.innerHTML = `
432
+ <style>
433
+ ${overlayCSS}
434
+ </style>
435
+ <div class="backdrop"></div>
436
+ <div class="overlay" data-theme=${theme}>
437
+ <h1 class="title">
438
+ ${title}
439
+ </h1>
440
+ <pre class="text">${text}</pre>
441
+ <div class="tip">
442
+ ${tip}
443
+ </div>
444
+ </div>`;
445
+
446
+ this.root.querySelector(".backdrop").onclick = () => {
447
+ if (!this.parentNode) {
448
+ // not in document anymore
449
+ return;
450
+ }
451
+
452
+ this.root.querySelector(".backdrop").onclick = null;
453
+ this.parentNode.removeChild(this);
454
+ };
455
+
456
+ if (codeFramePromise) {
457
+ codeFramePromise.then(codeFrame => {
458
+ if (this.parentNode) {
459
+ this.root.querySelector(".text").innerHTML += `\n\n${codeFrame}`;
460
+ }
461
+ });
462
+ }
463
+ }
464
+
465
+ }
466
+
467
+ if (customElements && !customElements.get(JSENV_ERROR_OVERLAY_TAGNAME)) {
468
+ customElements.define(JSENV_ERROR_OVERLAY_TAGNAME, JsenvErrorOverlay);
469
+ }
470
+
471
+ const overlayCSS = `
472
+ :host {
473
+ position: fixed;
474
+ z-index: 99999;
475
+ top: 0;
476
+ left: 0;
477
+ width: 100%;
478
+ height: 100%;
479
+ overflow-y: scroll;
480
+ margin: 0;
481
+ background: rgba(0, 0, 0, 0.66);
482
+ }
483
+
484
+ .backdrop {
485
+ position: absolute;
486
+ left: 0;
487
+ right: 0;
488
+ top: 0;
489
+ bottom: 0;
490
+ }
491
+
492
+ .overlay {
493
+ position: relative;
494
+ background: rgba(0, 0, 0, 0.95);
495
+ width: 800px;
496
+ margin: 30px auto;
497
+ padding: 25px 40px;
498
+ padding-top: 0;
499
+ overflow: hidden; /* for h1 margins */
500
+ border-radius: 4px 8px;
501
+ box-shadow: 0 20px 40px rgb(0 0 0 / 30%), 0 15px 12px rgb(0 0 0 / 20%);
502
+ box-sizing: border-box;
503
+ font-family: monospace;
504
+ direction: ltr;
505
+ }
506
+
507
+ h1 {
508
+ color: red;
509
+ text-align: center;
510
+ }
511
+
512
+ pre {
513
+ overflow: auto;
514
+ max-width: 100%;
515
+ /* padding is nice + prevents scrollbar from hiding the text behind it */
516
+ /* does not work nicely on firefox though https://bugzilla.mozilla.org/show_bug.cgi?id=748518 */
517
+ padding: 20px;
518
+ }
519
+
520
+ .tip {
521
+ border-top: 1px solid #999;
522
+ padding-top: 12px;
523
+ }
524
+
525
+ [data-theme="dark"] {
526
+ color: #999;
527
+ }
528
+ [data-theme="dark"] pre {
529
+ background: #111;
530
+ border: 1px solid #333;
531
+ color: #eee;
532
+ }
533
+
534
+ [data-theme="light"] {
535
+ color: #EEEEEE;
536
+ }
537
+ [data-theme="light"] pre {
538
+ background: #1E1E1E;
539
+ border: 1px solid white;
540
+ color: #EEEEEE;
541
+ }
542
+
543
+ pre a {
544
+ color: inherit;
545
+ }`;
546
+
408
547
  const {
409
548
  Notification
410
549
  } = window;
@@ -432,10 +571,14 @@ const displayErrorNotification = typeof Notification === "function" ? displayErr
432
571
  const {
433
572
  __html_supervisor__
434
573
  } = window;
574
+ const supervisedScripts = [];
435
575
  const installHtmlSupervisor = ({
576
+ rootDirectoryUrl,
436
577
  logs,
437
578
  measurePerf,
438
- rootDirectoryUrl
579
+ errorOverlay,
580
+ errorBaseUrl,
581
+ openInEditor
439
582
  }) => {
440
583
 
441
584
  const scriptExecutionResults = {};
@@ -471,8 +614,7 @@ const installHtmlSupervisor = ({
471
614
 
472
615
  const onExecutionError = (executionResult, {
473
616
  currentScript,
474
- errorExposureInNotification = false,
475
- errorExposureInDocument = false
617
+ errorExposureInNotification = false
476
618
  }) => {
477
619
  const error = executionResult.error;
478
620
 
@@ -494,12 +636,6 @@ const installHtmlSupervisor = ({
494
636
  displayErrorNotification(error);
495
637
  }
496
638
 
497
- if (errorExposureInDocument) {
498
- displayErrorInDocument(error, {
499
- rootDirectoryUrl
500
- });
501
- }
502
-
503
639
  executionResult.exceptionSource = unevalException(error);
504
640
  delete executionResult.error;
505
641
  };
@@ -518,7 +654,9 @@ const installHtmlSupervisor = ({
518
654
  currentScript,
519
655
  execute // https://developer.mozilla.org/en-US/docs/web/html/element/script
520
656
 
521
- }) => {
657
+ }, {
658
+ reload = false
659
+ } = {}) => {
522
660
  if (logs) {
523
661
  console.group(`[jsenv] loading ${type} ${src}`);
524
662
  }
@@ -529,7 +667,13 @@ const installHtmlSupervisor = ({
529
667
  let error;
530
668
 
531
669
  try {
532
- result = await execute();
670
+ const urlObject = new URL(src, window.location);
671
+
672
+ if (reload) {
673
+ urlObject.searchParams.set("hmr", Date.now());
674
+ }
675
+
676
+ result = await execute(urlObject.href);
533
677
  completed = true;
534
678
  } catch (e) {
535
679
  completed = false;
@@ -593,12 +737,22 @@ const installHtmlSupervisor = ({
593
737
  }));
594
738
 
595
739
  __html_supervisor__.addScriptToExecute = async scriptToExecute => {
740
+ if (!supervisedScripts.includes(scriptToExecute)) {
741
+ supervisedScripts.push(scriptToExecute);
742
+
743
+ scriptToExecute.reload = () => {
744
+ return performExecution(scriptToExecute, {
745
+ reload: true
746
+ });
747
+ };
748
+ }
749
+
596
750
  if (scriptToExecute.async) {
597
751
  performExecution(scriptToExecute);
598
752
  return;
599
753
  }
600
754
 
601
- const useDeferQueue = scriptToExecute.defer || scriptToExecute.type === "js_module";
755
+ const useDeferQueue = scriptToExecute.defer || scriptToExecute.type === "module";
602
756
 
603
757
  if (useDeferQueue) {
604
758
  // defer must wait for classic script to be done
@@ -652,60 +806,121 @@ const installHtmlSupervisor = ({
652
806
  copy.forEach(scriptToExecute => {
653
807
  __html_supervisor__.addScriptToExecute(scriptToExecute);
654
808
  });
655
- window.addEventListener("error", errorEvent => {
656
- if (!errorEvent.isTrusted) {
657
- // ignore custom error event (not sent by browser)
658
- return;
659
- }
660
809
 
661
- const {
662
- error
663
- } = errorEvent;
664
- displayErrorInDocument(error, {
665
- rootDirectoryUrl,
666
- url: errorEvent.filename,
667
- line: errorEvent.lineno,
668
- column: errorEvent.colno
669
- });
670
- });
810
+ if (errorOverlay) {
811
+ window.addEventListener("error", errorEvent => {
812
+ if (!errorEvent.isTrusted) {
813
+ // ignore custom error event (not sent by browser)
814
+ return;
815
+ }
671
816
 
672
- if (window.__jsenv_event_source_client__) {
673
- const onServerErrorEvent = serverErrorEvent => {
674
817
  const {
675
- reason,
676
- stack,
677
- url,
678
- line,
679
- column,
680
- contentFrame
681
- } = JSON.parse(serverErrorEvent.data);
682
- displayErrorInDocument({
683
- message: reason,
684
- stack: stack ? `${stack}\n\n${contentFrame}` : contentFrame
685
- }, {
818
+ error
819
+ } = errorEvent;
820
+ displayErrorInDocument(error, {
686
821
  rootDirectoryUrl,
687
- url,
688
- line,
689
- column
822
+ errorBaseUrl,
823
+ openInEditor,
824
+ url: errorEvent.filename,
825
+ line: errorEvent.lineno,
826
+ column: errorEvent.colno,
827
+ reportedBy: "browser"
690
828
  });
691
- };
692
-
693
- window.__jsenv_event_source_client__.addEventCallbacks({
694
- file_not_found: onServerErrorEvent,
695
- parse_error: onServerErrorEvent,
696
- unexpected_error: onServerErrorEvent
697
829
  });
830
+
831
+ if (window.__server_events__) {
832
+ const isExecuting = () => {
833
+ if (pendingExecutionCount > 0) {
834
+ return true;
835
+ }
836
+
837
+ if (document.readyState === "loading" || document.readyState === "interactive") {
838
+ return true;
839
+ }
840
+
841
+ if (window.__reloader__ && window.__reloader__.status === "reloading") {
842
+ return true;
843
+ }
844
+
845
+ return false;
846
+ };
847
+
848
+ window.__server_events__.addEventCallbacks({
849
+ error_while_serving_file: serverErrorEvent => {
850
+ if (!isExecuting()) {
851
+ return;
852
+ }
853
+
854
+ const {
855
+ message,
856
+ stack,
857
+ traceUrl,
858
+ traceLine,
859
+ traceColumn,
860
+ traceMessage,
861
+ requestedRessource,
862
+ isFaviconAutoRequest
863
+ } = JSON.parse(serverErrorEvent.data);
864
+
865
+ if (isFaviconAutoRequest) {
866
+ return;
867
+ } // setTimeout is to ensure the error
868
+ // dispatched on window by browser is displayed first,
869
+ // then the server error replaces it (because it contains more information)
870
+
871
+
872
+ setTimeout(() => {
873
+ displayErrorInDocument({
874
+ message,
875
+ stack
876
+ }, {
877
+ rootDirectoryUrl,
878
+ errorBaseUrl,
879
+ openInEditor,
880
+ url: traceUrl,
881
+ line: traceLine,
882
+ column: traceColumn,
883
+ codeFrame: traceMessage,
884
+ reportedBy: "server",
885
+ requestedRessource
886
+ });
887
+ }, 10);
888
+ }
889
+ });
890
+ }
891
+ }
892
+ };
893
+
894
+ __html_supervisor__.reloadSupervisedScript = ({
895
+ type,
896
+ src
897
+ }) => {
898
+ const supervisedScript = supervisedScripts.find(supervisedScriptCandidate => {
899
+ if (type && supervisedScriptCandidate.type !== type) {
900
+ return false;
901
+ }
902
+
903
+ if (supervisedScriptCandidate.src !== src) {
904
+ return false;
905
+ }
906
+
907
+ return true;
908
+ });
909
+
910
+ if (supervisedScript) {
911
+ supervisedScript.reload();
698
912
  }
699
913
  };
914
+
700
915
  const superviseScriptTypeModule = ({
701
916
  src,
702
917
  isInline
703
918
  }) => {
704
919
  __html_supervisor__.addScriptToExecute({
705
920
  src,
706
- type: "js_module",
921
+ type: "module",
707
922
  isInline,
708
- execute: () => import(new URL(src, document.location.href).href)
923
+ execute: url => import(url)
709
924
  });
710
925
  };
711
926