@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
@@ -0,0 +1,300 @@
1
+ export const formatError = (
2
+ error,
3
+ {
4
+ rootDirectoryUrl,
5
+ errorBaseUrl,
6
+ openInEditor,
7
+ url,
8
+ line,
9
+ column,
10
+ codeFrame,
11
+ requestedRessource,
12
+ reportedBy,
13
+ },
14
+ ) => {
15
+ let { message, stack } = normalizeErrorParts(error)
16
+ let codeFramePromiseReference = { current: null }
17
+ let tip = formatTip({ reportedBy, requestedRessource })
18
+ let errorUrlSite
19
+
20
+ const resolveUrlSite = ({ url, line, column }) => {
21
+ const inlineUrlMatch = url.match(/@L([0-9]+)\-L([0-9]+)\.[\w]+$/)
22
+ if (inlineUrlMatch) {
23
+ const htmlUrl = url.slice(0, inlineUrlMatch.index)
24
+ const tagLine = parseInt(inlineUrlMatch[1])
25
+ const tagColumn = parseInt(inlineUrlMatch[2])
26
+ url = htmlUrl
27
+ line = tagLine + parseInt(line) - 1
28
+ column = tagColumn + parseInt(column)
29
+ }
30
+
31
+ let urlObject = new URL(url)
32
+ if (urlObject.origin === window.origin) {
33
+ urlObject = new URL(
34
+ `${urlObject.pathname.slice(1)}${urlObject.search}`,
35
+ rootDirectoryUrl,
36
+ )
37
+ }
38
+ if (urlObject.href.startsWith("file:")) {
39
+ const atFsIndex = urlObject.pathname.indexOf("/@fs/")
40
+ if (atFsIndex > -1) {
41
+ const afterAtFs = urlObject.pathname.slice(atFsIndex + "/@fs/".length)
42
+ url = new URL(afterAtFs, "file:///").href
43
+ } else {
44
+ url = urlObject.href
45
+ }
46
+ } else {
47
+ url = urlObject.href
48
+ }
49
+
50
+ return {
51
+ url,
52
+ line,
53
+ column,
54
+ }
55
+ }
56
+
57
+ const generateClickableText = (text) => {
58
+ const textWithHtmlLinks = makeLinksClickable(text, {
59
+ createLink: (url, { line, column }) => {
60
+ const urlSite = resolveUrlSite({ url, line, column })
61
+ if (!errorUrlSite && text === stack) {
62
+ onErrorLocated(urlSite)
63
+ }
64
+ if (errorBaseUrl) {
65
+ if (urlSite.url.startsWith(rootDirectoryUrl)) {
66
+ urlSite.url = `${errorBaseUrl}${urlSite.url.slice(
67
+ rootDirectoryUrl.length,
68
+ )}`
69
+ } else {
70
+ urlSite.url = "file:///mocked_for_snapshots"
71
+ }
72
+ }
73
+ const urlWithLineAndColumn = formatUrlWithLineAndColumn(urlSite)
74
+ return {
75
+ href:
76
+ url.startsWith("file:") && openInEditor
77
+ ? `javascript:window.fetch('/__open_in_editor__/${urlWithLineAndColumn}')`
78
+ : urlSite.url,
79
+ text: urlWithLineAndColumn,
80
+ }
81
+ },
82
+ })
83
+ return textWithHtmlLinks
84
+ }
85
+
86
+ const onErrorLocated = (urlSite) => {
87
+ errorUrlSite = urlSite
88
+ if (codeFrame) {
89
+ return
90
+ }
91
+ if (reportedBy !== "browser") {
92
+ return
93
+ }
94
+ codeFramePromiseReference.current = (async () => {
95
+ const response = await window.fetch(
96
+ `/__get_code_frame__/${formatUrlWithLineAndColumn(urlSite)}`,
97
+ )
98
+ const codeFrame = await response.text()
99
+ const codeFrameClickable = generateClickableText(codeFrame)
100
+ return codeFrameClickable
101
+ })()
102
+ }
103
+
104
+ // error.stack is more reliable than url/line/column reported on window error events
105
+ // so use it only when error.stack is not available
106
+ if (url && !stack) {
107
+ onErrorLocated(resolveUrlSite({ url, line, column }))
108
+ }
109
+
110
+ let text
111
+
112
+ if (message && stack) {
113
+ text = `${generateClickableText(message)}\n${generateClickableText(stack)}`
114
+ } else if (stack) {
115
+ text = generateClickableText(stack)
116
+ } else {
117
+ text = generateClickableText(message)
118
+ }
119
+
120
+ if (codeFrame) {
121
+ text += `\n\n${generateClickableText(codeFrame)}`
122
+ }
123
+
124
+ return {
125
+ theme:
126
+ error && error.cause && error.cause.code === "PARSE_ERROR"
127
+ ? "light"
128
+ : "dark",
129
+ title: "An error occured",
130
+ text,
131
+ codeFramePromise: codeFramePromiseReference.current,
132
+ tip: `${tip}
133
+ <br />
134
+ Click outside to close.`,
135
+ }
136
+ }
137
+
138
+ const formatUrlWithLineAndColumn = ({ url, line, column }) => {
139
+ return line === undefined && column === undefined
140
+ ? url
141
+ : column === undefined
142
+ ? `${url}:${line}`
143
+ : `${url}:${line}:${column}`
144
+ }
145
+
146
+ const normalizeErrorParts = (error) => {
147
+ if (error === undefined) {
148
+ return {
149
+ message: "undefined",
150
+ }
151
+ }
152
+ if (error === null) {
153
+ return {
154
+ message: "null",
155
+ }
156
+ }
157
+ if (typeof error === "string") {
158
+ return {
159
+ message: error,
160
+ }
161
+ }
162
+ if (error instanceof Error) {
163
+ if (error.name === "SyntaxError") {
164
+ return {
165
+ message: error.message,
166
+ }
167
+ }
168
+ if (error.cause && error.cause.code === "PARSE_ERROR") {
169
+ if (error.messageHTML) {
170
+ return {
171
+ message: error.messageHTML,
172
+ }
173
+ }
174
+ return {
175
+ message: error.message,
176
+ }
177
+ }
178
+ // stackTrace formatted by V8
179
+ if (Error.captureStackTrace) {
180
+ return {
181
+ message: error.message,
182
+ stack: getErrorStackWithoutErrorMessage(error),
183
+ }
184
+ }
185
+ return {
186
+ message: error.message,
187
+ stack: error.stack ? ` ${error.stack}` : null,
188
+ }
189
+ }
190
+ if (typeof error === "object") {
191
+ return error
192
+ }
193
+ return {
194
+ message: JSON.stringify(error),
195
+ }
196
+ }
197
+
198
+ const getErrorStackWithoutErrorMessage = (error) => {
199
+ let stack = error.stack
200
+ const messageInStack = `${error.name}: ${error.message}`
201
+ if (stack.startsWith(messageInStack)) {
202
+ stack = stack.slice(messageInStack.length)
203
+ }
204
+ const nextLineIndex = stack.indexOf("\n")
205
+ if (nextLineIndex > -1) {
206
+ stack = stack.slice(nextLineIndex + 1)
207
+ }
208
+ return stack
209
+ }
210
+
211
+ const formatTip = ({ reportedBy, requestedRessource }) => {
212
+ if (reportedBy === "browser") {
213
+ return `Reported by the browser while executing <code>${window.location.pathname}${window.location.search}</code>.`
214
+ }
215
+ return `Reported by the server while serving <code>${requestedRessource}</code>`
216
+ }
217
+
218
+ const makeLinksClickable = (string, { createLink = (url) => url }) => {
219
+ // normalize line breaks
220
+ string = string.replace(/\n/g, "\n")
221
+ string = escapeHtml(string)
222
+ // render links
223
+ string = stringToStringWithLink(string, {
224
+ transform: (url, { line, column }) => {
225
+ const { href, text } = createLink(url, { line, column })
226
+ return link({ href, text })
227
+ },
228
+ })
229
+ return string
230
+ }
231
+
232
+ const escapeHtml = (string) => {
233
+ return string
234
+ .replace(/&/g, "&amp;")
235
+ .replace(/</g, "&lt;")
236
+ .replace(/>/g, "&gt;")
237
+ .replace(/"/g, "&quot;")
238
+ .replace(/'/g, "&#039;")
239
+ }
240
+
241
+ // `Error: yo
242
+ // at Object.execute (http://127.0.0.1:57300/build/src/__test__/file-throw.js:9:13)
243
+ // at doExec (http://127.0.0.1:3000/src/__test__/file-throw.js:452:38)
244
+ // at postOrderExec (http://127.0.0.1:3000/src/__test__/file-throw.js:448:16)
245
+ // at http://127.0.0.1:3000/src/__test__/file-throw.js:399:18`.replace(/(?:https?|ftp|file):\/\/(.*+)$/gm, (...args) => {
246
+ // debugger
247
+ // })
248
+ const stringToStringWithLink = (
249
+ source,
250
+ {
251
+ transform = (url) => {
252
+ return {
253
+ href: url,
254
+ text: url,
255
+ }
256
+ },
257
+ } = {},
258
+ ) => {
259
+ return source.replace(/(?:https?|ftp|file):\/\/\S+/gm, (match) => {
260
+ let linkHTML = ""
261
+
262
+ const lastChar = match[match.length - 1]
263
+
264
+ // hotfix because our url regex sucks a bit
265
+ const endsWithSeparationChar = lastChar === ")" || lastChar === ":"
266
+ if (endsWithSeparationChar) {
267
+ match = match.slice(0, -1)
268
+ }
269
+
270
+ const lineAndColumnPattern = /:([0-9]+):([0-9]+)$/
271
+ const lineAndColumMatch = match.match(lineAndColumnPattern)
272
+ if (lineAndColumMatch) {
273
+ const lineAndColumnString = lineAndColumMatch[0]
274
+ const lineNumber = lineAndColumMatch[1]
275
+ const columnNumber = lineAndColumMatch[2]
276
+ linkHTML = transform(match.slice(0, -lineAndColumnString.length), {
277
+ line: lineNumber,
278
+ column: columnNumber,
279
+ })
280
+ } else {
281
+ const linePattern = /:([0-9]+)$/
282
+ const lineMatch = match.match(linePattern)
283
+ if (lineMatch) {
284
+ const lineString = lineMatch[0]
285
+ const lineNumber = lineMatch[1]
286
+ linkHTML = transform(match.slice(0, -lineString.length), {
287
+ line: lineNumber,
288
+ })
289
+ } else {
290
+ linkHTML = transform(match, {})
291
+ }
292
+ }
293
+ if (endsWithSeparationChar) {
294
+ return `${linkHTML}${lastChar}`
295
+ }
296
+ return linkHTML
297
+ })
298
+ }
299
+
300
+ const link = ({ href, text = href }) => `<a href="${href}">${text}</a>`
@@ -0,0 +1,172 @@
1
+ import { formatError } from "./error_formatter.js"
2
+
3
+ const JSENV_ERROR_OVERLAY_TAGNAME = "jsenv-error-overlay"
4
+
5
+ export const displayErrorInDocument = (
6
+ error,
7
+ {
8
+ rootDirectoryUrl,
9
+ errorBaseUrl,
10
+ openInEditor,
11
+ url,
12
+ line,
13
+ column,
14
+ codeFrame,
15
+ reportedBy,
16
+ requestedRessource,
17
+ },
18
+ ) => {
19
+ const { theme, title, text, codeFramePromise, tip } = formatError(error, {
20
+ rootDirectoryUrl,
21
+ errorBaseUrl,
22
+ openInEditor,
23
+ url,
24
+ line,
25
+ column,
26
+ codeFrame,
27
+ reportedBy,
28
+ requestedRessource,
29
+ })
30
+
31
+ let jsenvErrorOverlay = new JsenvErrorOverlay({
32
+ theme,
33
+ title,
34
+ text,
35
+ codeFramePromise,
36
+ tip,
37
+ })
38
+ document.querySelectorAll(JSENV_ERROR_OVERLAY_TAGNAME).forEach((node) => {
39
+ node.parentNode.removeChild(node)
40
+ })
41
+ document.body.appendChild(jsenvErrorOverlay)
42
+ const removeErrorOverlay = () => {
43
+ if (jsenvErrorOverlay && jsenvErrorOverlay.parentNode) {
44
+ document.body.removeChild(jsenvErrorOverlay)
45
+ jsenvErrorOverlay = null
46
+ }
47
+ }
48
+ if (window.__reloader__) {
49
+ window.__reloader__.onstatuschange = () => {
50
+ if (window.__reloader__.status === "reloading") {
51
+ removeErrorOverlay()
52
+ }
53
+ }
54
+ }
55
+ return removeErrorOverlay
56
+ }
57
+
58
+ class JsenvErrorOverlay extends HTMLElement {
59
+ constructor({ theme, title, text, codeFramePromise, tip }) {
60
+ super()
61
+ this.root = this.attachShadow({ mode: "open" })
62
+ this.root.innerHTML = `
63
+ <style>
64
+ ${overlayCSS}
65
+ </style>
66
+ <div class="backdrop"></div>
67
+ <div class="overlay" data-theme=${theme}>
68
+ <h1 class="title">
69
+ ${title}
70
+ </h1>
71
+ <pre class="text">${text}</pre>
72
+ <div class="tip">
73
+ ${tip}
74
+ </div>
75
+ </div>`
76
+ this.root.querySelector(".backdrop").onclick = () => {
77
+ if (!this.parentNode) {
78
+ // not in document anymore
79
+ return
80
+ }
81
+ this.root.querySelector(".backdrop").onclick = null
82
+ this.parentNode.removeChild(this)
83
+ }
84
+ if (codeFramePromise) {
85
+ codeFramePromise.then((codeFrame) => {
86
+ if (this.parentNode) {
87
+ this.root.querySelector(".text").innerHTML += `\n\n${codeFrame}`
88
+ }
89
+ })
90
+ }
91
+ }
92
+ }
93
+
94
+ if (customElements && !customElements.get(JSENV_ERROR_OVERLAY_TAGNAME)) {
95
+ customElements.define(JSENV_ERROR_OVERLAY_TAGNAME, JsenvErrorOverlay)
96
+ }
97
+
98
+ const overlayCSS = `
99
+ :host {
100
+ position: fixed;
101
+ z-index: 99999;
102
+ top: 0;
103
+ left: 0;
104
+ width: 100%;
105
+ height: 100%;
106
+ overflow-y: scroll;
107
+ margin: 0;
108
+ background: rgba(0, 0, 0, 0.66);
109
+ }
110
+
111
+ .backdrop {
112
+ position: absolute;
113
+ left: 0;
114
+ right: 0;
115
+ top: 0;
116
+ bottom: 0;
117
+ }
118
+
119
+ .overlay {
120
+ position: relative;
121
+ background: rgba(0, 0, 0, 0.95);
122
+ width: 800px;
123
+ margin: 30px auto;
124
+ padding: 25px 40px;
125
+ padding-top: 0;
126
+ overflow: hidden; /* for h1 margins */
127
+ border-radius: 4px 8px;
128
+ box-shadow: 0 20px 40px rgb(0 0 0 / 30%), 0 15px 12px rgb(0 0 0 / 20%);
129
+ box-sizing: border-box;
130
+ font-family: monospace;
131
+ direction: ltr;
132
+ }
133
+
134
+ h1 {
135
+ color: red;
136
+ text-align: center;
137
+ }
138
+
139
+ pre {
140
+ overflow: auto;
141
+ max-width: 100%;
142
+ /* padding is nice + prevents scrollbar from hiding the text behind it */
143
+ /* does not work nicely on firefox though https://bugzilla.mozilla.org/show_bug.cgi?id=748518 */
144
+ padding: 20px;
145
+ }
146
+
147
+ .tip {
148
+ border-top: 1px solid #999;
149
+ padding-top: 12px;
150
+ }
151
+
152
+ [data-theme="dark"] {
153
+ color: #999;
154
+ }
155
+ [data-theme="dark"] pre {
156
+ background: #111;
157
+ border: 1px solid #333;
158
+ color: #eee;
159
+ }
160
+
161
+ [data-theme="light"] {
162
+ color: #EEEEEE;
163
+ }
164
+ [data-theme="light"] pre {
165
+ background: #1E1E1E;
166
+ border: 1px solid white;
167
+ color: #EEEEEE;
168
+ }
169
+
170
+ pre a {
171
+ color: inherit;
172
+ }`
@@ -1,13 +1,18 @@
1
1
  import { unevalException } from "./uneval_exception.js"
2
- import { displayErrorInDocument } from "./error_in_document.js"
2
+ import { displayErrorInDocument } from "./error_overlay.js"
3
3
  import { displayErrorNotification } from "./error_in_notification.js"
4
4
 
5
5
  const { __html_supervisor__ } = window
6
6
 
7
+ const supervisedScripts = []
8
+
7
9
  export const installHtmlSupervisor = ({
10
+ rootDirectoryUrl,
8
11
  logs,
9
12
  measurePerf,
10
- rootDirectoryUrl,
13
+ errorOverlay,
14
+ errorBaseUrl,
15
+ openInEditor,
11
16
  }) => {
12
17
  const errorTransformer = null // could implement error stack remapping if needed
13
18
  const scriptExecutionResults = {}
@@ -36,11 +41,7 @@ export const installHtmlSupervisor = ({
36
41
  }
37
42
  const onExecutionError = (
38
43
  executionResult,
39
- {
40
- currentScript,
41
- errorExposureInNotification = false,
42
- errorExposureInDocument = false,
43
- },
44
+ { currentScript, errorExposureInNotification = false },
44
45
  ) => {
45
46
  const error = executionResult.error
46
47
  if (error && error.code === "NETWORK_FAILURE") {
@@ -59,9 +60,6 @@ export const installHtmlSupervisor = ({
59
60
  if (errorExposureInNotification) {
60
61
  displayErrorNotification(error)
61
62
  }
62
- if (errorExposureInDocument) {
63
- displayErrorInDocument(error, { rootDirectoryUrl })
64
- }
65
63
  executionResult.exceptionSource = unevalException(error)
66
64
  delete executionResult.error
67
65
  }
@@ -73,13 +71,16 @@ export const installHtmlSupervisor = ({
73
71
  }
74
72
  }
75
73
 
76
- const performExecution = async ({
77
- src,
78
- type,
79
- currentScript,
80
- execute,
81
- // https://developer.mozilla.org/en-US/docs/web/html/element/script
82
- }) => {
74
+ const performExecution = async (
75
+ {
76
+ src,
77
+ type,
78
+ currentScript,
79
+ execute,
80
+ // https://developer.mozilla.org/en-US/docs/web/html/element/script
81
+ },
82
+ { reload = false } = {},
83
+ ) => {
83
84
  if (logs) {
84
85
  console.group(`[jsenv] loading ${type} ${src}`)
85
86
  }
@@ -88,7 +89,11 @@ export const installHtmlSupervisor = ({
88
89
  let result
89
90
  let error
90
91
  try {
91
- result = await execute()
92
+ const urlObject = new URL(src, window.location)
93
+ if (reload) {
94
+ urlObject.searchParams.set("hmr", Date.now())
95
+ }
96
+ result = await execute(urlObject.href)
92
97
  completed = true
93
98
  } catch (e) {
94
99
  completed = false
@@ -156,12 +161,19 @@ export const installHtmlSupervisor = ({
156
161
  }),
157
162
  )
158
163
  __html_supervisor__.addScriptToExecute = async (scriptToExecute) => {
164
+ if (!supervisedScripts.includes(scriptToExecute)) {
165
+ supervisedScripts.push(scriptToExecute)
166
+ scriptToExecute.reload = () => {
167
+ return performExecution(scriptToExecute, { reload: true })
168
+ }
169
+ }
170
+
159
171
  if (scriptToExecute.async) {
160
172
  performExecution(scriptToExecute)
161
173
  return
162
174
  }
163
175
  const useDeferQueue =
164
- scriptToExecute.defer || scriptToExecute.type === "js_module"
176
+ scriptToExecute.defer || scriptToExecute.type === "module"
165
177
  if (useDeferQueue) {
166
178
  // defer must wait for classic script to be done
167
179
  const classicExecutionPromise = classicExecutionQueue.getPromise()
@@ -207,51 +219,109 @@ export const installHtmlSupervisor = ({
207
219
  __html_supervisor__.addScriptToExecute(scriptToExecute)
208
220
  })
209
221
 
210
- window.addEventListener("error", (errorEvent) => {
211
- if (!errorEvent.isTrusted) {
212
- // ignore custom error event (not sent by browser)
213
- return
214
- }
215
- const { error } = errorEvent
216
- displayErrorInDocument(error, {
217
- rootDirectoryUrl,
218
- url: errorEvent.filename,
219
- line: errorEvent.lineno,
220
- column: errorEvent.colno,
222
+ if (errorOverlay) {
223
+ window.addEventListener("error", (errorEvent) => {
224
+ if (!errorEvent.isTrusted) {
225
+ // ignore custom error event (not sent by browser)
226
+ return
227
+ }
228
+ const { error } = errorEvent
229
+ displayErrorInDocument(error, {
230
+ rootDirectoryUrl,
231
+ errorBaseUrl,
232
+ openInEditor,
233
+ url: errorEvent.filename,
234
+ line: errorEvent.lineno,
235
+ column: errorEvent.colno,
236
+ reportedBy: "browser",
237
+ })
221
238
  })
222
- })
223
- if (window.__jsenv_event_source_client__) {
224
- const onServerErrorEvent = (serverErrorEvent) => {
225
- const { reason, stack, url, line, column, contentFrame } = JSON.parse(
226
- serverErrorEvent.data,
227
- )
228
- displayErrorInDocument(
229
- {
230
- message: reason,
231
- stack: stack ? `${stack}\n\n${contentFrame}` : contentFrame,
232
- },
233
- {
234
- rootDirectoryUrl,
235
- url,
236
- line,
237
- column,
239
+ if (window.__server_events__) {
240
+ const isExecuting = () => {
241
+ if (pendingExecutionCount > 0) {
242
+ return true
243
+ }
244
+ if (
245
+ document.readyState === "loading" ||
246
+ document.readyState === "interactive"
247
+ ) {
248
+ return true
249
+ }
250
+ if (window.__reloader__ && window.__reloader__.status === "reloading") {
251
+ return true
252
+ }
253
+ return false
254
+ }
255
+
256
+ window.__server_events__.addEventCallbacks({
257
+ error_while_serving_file: (serverErrorEvent) => {
258
+ if (!isExecuting()) {
259
+ return
260
+ }
261
+ const {
262
+ message,
263
+ stack,
264
+ traceUrl,
265
+ traceLine,
266
+ traceColumn,
267
+ traceMessage,
268
+ requestedRessource,
269
+ isFaviconAutoRequest,
270
+ } = JSON.parse(serverErrorEvent.data)
271
+ if (isFaviconAutoRequest) {
272
+ return
273
+ }
274
+ // setTimeout is to ensure the error
275
+ // dispatched on window by browser is displayed first,
276
+ // then the server error replaces it (because it contains more information)
277
+ setTimeout(() => {
278
+ displayErrorInDocument(
279
+ {
280
+ message,
281
+ stack,
282
+ },
283
+ {
284
+ rootDirectoryUrl,
285
+ errorBaseUrl,
286
+ openInEditor,
287
+ url: traceUrl,
288
+ line: traceLine,
289
+ column: traceColumn,
290
+ codeFrame: traceMessage,
291
+ reportedBy: "server",
292
+ requestedRessource,
293
+ },
294
+ )
295
+ }, 10)
238
296
  },
239
- )
297
+ })
240
298
  }
241
- window.__jsenv_event_source_client__.addEventCallbacks({
242
- file_not_found: onServerErrorEvent,
243
- parse_error: onServerErrorEvent,
244
- unexpected_error: onServerErrorEvent,
245
- })
299
+ }
300
+ }
301
+
302
+ __html_supervisor__.reloadSupervisedScript = ({ type, src }) => {
303
+ const supervisedScript = supervisedScripts.find(
304
+ (supervisedScriptCandidate) => {
305
+ if (type && supervisedScriptCandidate.type !== type) {
306
+ return false
307
+ }
308
+ if (supervisedScriptCandidate.src !== src) {
309
+ return false
310
+ }
311
+ return true
312
+ },
313
+ )
314
+ if (supervisedScript) {
315
+ supervisedScript.reload()
246
316
  }
247
317
  }
248
318
 
249
319
  export const superviseScriptTypeModule = ({ src, isInline }) => {
250
320
  __html_supervisor__.addScriptToExecute({
251
321
  src,
252
- type: "js_module",
322
+ type: "module",
253
323
  isInline,
254
- execute: () => import(new URL(src, document.location.href).href),
324
+ execute: (url) => import(url),
255
325
  })
256
326
  }
257
327