@jsenv/core 27.3.3 → 27.5.0

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 (89) 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 +524 -147
  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 +7699 -7307
  8. package/package.json +15 -15
  9. package/{README.md → readme.md} +18 -7
  10. package/src/build/build.js +16 -18
  11. package/src/build/start_build_server.js +24 -28
  12. package/src/dev/start_dev_server.js +30 -94
  13. package/src/execute/execute.js +17 -35
  14. package/src/execute/run.js +2 -0
  15. package/src/omega/errors.js +43 -9
  16. package/src/omega/kitchen.js +42 -25
  17. package/src/omega/omega_server.js +96 -74
  18. package/src/omega/server/file_service.js +256 -28
  19. package/src/omega/url_graph.js +39 -20
  20. package/src/plugins/autoreload/client/autoreload.js +201 -0
  21. package/src/plugins/autoreload/{dev_sse/client → client}/autoreload_preference.js +0 -0
  22. package/src/plugins/autoreload/{dev_sse/client → client}/reload.js +29 -10
  23. package/src/plugins/autoreload/{dev_sse/client → client}/url_helpers.js +0 -0
  24. package/src/plugins/autoreload/jsenv_plugin_autoreload.js +4 -8
  25. package/src/plugins/autoreload/{dev_sse/jsenv_plugin_dev_sse_client.js → jsenv_plugin_autoreload_client.js} +8 -8
  26. package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +196 -0
  27. package/src/{dev/plugins → plugins}/explorer/client/explorer.html +0 -0
  28. package/src/{dev/plugins → plugins}/explorer/client/jsenv.png +0 -0
  29. package/src/{dev/plugins → plugins}/explorer/jsenv_plugin_explorer.js +1 -3
  30. package/src/plugins/html_supervisor/client/error_overlay.js +401 -0
  31. package/src/plugins/html_supervisor/client/html_supervisor_installer.js +138 -23
  32. package/src/plugins/html_supervisor/client/html_supervisor_setup.js +3 -4
  33. package/src/plugins/html_supervisor/jsenv_plugin_html_supervisor.js +55 -23
  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 -10
  38. package/src/{helpers/event_source/event_source.js → plugins/server_events/client/event_source_connection.js} +125 -33
  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/plugins/url_analysis/jsenv_plugin_url_analysis.js +3 -1
  81. package/src/test/execute_plan.js +36 -54
  82. package/src/test/execute_test_plan.js +2 -2
  83. package/src/test/logs_file_execution.js +60 -27
  84. package/src/test/logs_file_execution.test.mjs +41 -0
  85. package/dist/js/event_source_client.js +0 -528
  86. package/src/helpers/event_source/sse_service.js +0 -53
  87. package/src/plugins/autoreload/dev_sse/client/event_source_client.js +0 -193
  88. package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_server.js +0 -203
  89. package/src/plugins/html_supervisor/client/error_in_document.js +0 -198
@@ -0,0 +1,401 @@
1
+ const JSENV_ERROR_OVERLAY_TAGNAME = "jsenv-error-overlay"
2
+
3
+ export const displayErrorInDocument = (
4
+ error,
5
+ {
6
+ rootDirectoryUrl,
7
+ openInEditor,
8
+ url,
9
+ line,
10
+ column,
11
+ reportedBy,
12
+ requestedRessource,
13
+ },
14
+ ) => {
15
+ document.querySelectorAll(JSENV_ERROR_OVERLAY_TAGNAME).forEach((node) => {
16
+ node.parentNode.removeChild(node)
17
+ })
18
+ const { theme, title, message, stack, tip } = errorToHTML(error, {
19
+ url,
20
+ line,
21
+ column,
22
+ reportedBy,
23
+ requestedRessource,
24
+ })
25
+ let jsenvErrorOverlay = new JsenvErrorOverlay({
26
+ theme,
27
+ title,
28
+ text: createErrorText({ rootDirectoryUrl, openInEditor, message, stack }),
29
+ tip,
30
+ })
31
+ document.body.appendChild(jsenvErrorOverlay)
32
+ const removeErrorOverlay = () => {
33
+ if (jsenvErrorOverlay && jsenvErrorOverlay.parentNode) {
34
+ document.body.removeChild(jsenvErrorOverlay)
35
+ jsenvErrorOverlay = null
36
+ }
37
+ }
38
+ if (window.__reloader__) {
39
+ window.__reloader__.onstatuschange = () => {
40
+ if (window.__reloader__.status === "reloading") {
41
+ removeErrorOverlay()
42
+ }
43
+ }
44
+ }
45
+ return removeErrorOverlay
46
+ }
47
+
48
+ const createErrorText = ({
49
+ rootDirectoryUrl,
50
+ openInEditor,
51
+ message,
52
+ stack,
53
+ }) => {
54
+ if (message && stack) {
55
+ return `${replaceLinks(message, {
56
+ rootDirectoryUrl,
57
+ openInEditor,
58
+ })}\n${replaceLinks(stack, { rootDirectoryUrl, openInEditor })}`
59
+ }
60
+ if (stack) {
61
+ return replaceLinks(stack, { rootDirectoryUrl, openInEditor })
62
+ }
63
+ return replaceLinks(message, { rootDirectoryUrl, openInEditor })
64
+ }
65
+
66
+ class JsenvErrorOverlay extends HTMLElement {
67
+ constructor({ theme, title, text, tip }) {
68
+ super()
69
+ this.root = this.attachShadow({ mode: "open" })
70
+ this.root.innerHTML = overlayHtml
71
+ this.root.querySelector(".backdrop").onclick = () => {
72
+ if (!this.parentNode) {
73
+ // not in document anymore
74
+ return
75
+ }
76
+ this.root.querySelector(".backdrop").onclick = null
77
+ this.parentNode.removeChild(this)
78
+ }
79
+ this.root.querySelector(".overlay").setAttribute("data-theme", theme)
80
+ this.root.querySelector(".title").innerHTML = title
81
+ this.root.querySelector(".text").innerHTML = text
82
+ this.root.querySelector(".tip").innerHTML = tip
83
+ }
84
+ }
85
+
86
+ if (customElements && !customElements.get(JSENV_ERROR_OVERLAY_TAGNAME)) {
87
+ customElements.define(JSENV_ERROR_OVERLAY_TAGNAME, JsenvErrorOverlay)
88
+ }
89
+
90
+ const overlayHtml = `
91
+ <style>
92
+ :host {
93
+ position: fixed;
94
+ z-index: 99999;
95
+ top: 0;
96
+ left: 0;
97
+ width: 100%;
98
+ height: 100%;
99
+ overflow-y: scroll;
100
+ margin: 0;
101
+ background: rgba(0, 0, 0, 0.66);
102
+ }
103
+
104
+ .backdrop {
105
+ position: absolute;
106
+ left: 0;
107
+ right: 0;
108
+ top: 0;
109
+ bottom: 0;
110
+ }
111
+
112
+ .overlay {
113
+ position: relative;
114
+ background: rgba(0, 0, 0, 0.95);
115
+ width: 800px;
116
+ margin: 30px auto;
117
+ padding: 25px 40px;
118
+ padding-top: 0;
119
+ overflow: hidden; /* for h1 margins */
120
+ border-radius: 4px 8px;
121
+ box-shadow: 0 20px 40px rgb(0 0 0 / 30%), 0 15px 12px rgb(0 0 0 / 20%);
122
+ box-sizing: border-box;
123
+ font-family: monospace;
124
+ direction: ltr;
125
+ }
126
+
127
+ h1 {
128
+ color: red;
129
+ text-align: center;
130
+ }
131
+
132
+ pre {
133
+ overflow: auto;
134
+ max-width: 100%;
135
+ /* padding is nice + prevents scrollbar from hiding the text behind it */
136
+ /* does not work nicely on firefox though https://bugzilla.mozilla.org/show_bug.cgi?id=748518 */
137
+ padding: 20px;
138
+ }
139
+
140
+ .tip {
141
+ border-top: 1px solid #999;
142
+ padding-top: 12px;
143
+ }
144
+
145
+ [data-theme="dark"] {
146
+ color: #999;
147
+ }
148
+ [data-theme="dark"] pre {
149
+ background: #111;
150
+ border: 1px solid #333;
151
+ color: #eee;
152
+ }
153
+
154
+ [data-theme="light"] {
155
+ color: #EEEEEE;
156
+ }
157
+ [data-theme="light"] pre {
158
+ background: #1E1E1E;
159
+ border: 1px solid white;
160
+ color: #EEEEEE;
161
+ }
162
+
163
+ pre a {
164
+ color: inherit;
165
+ }
166
+ </style>
167
+ <div class="backdrop"></div>
168
+ <div class="overlay">
169
+ <h1 class="title"></h1>
170
+ <pre class="text"></pre>
171
+ <div class="tip"></div>
172
+ </div>
173
+ `
174
+
175
+ const parseErrorInfo = (error) => {
176
+ if (error === undefined) {
177
+ return {
178
+ message: "undefined",
179
+ }
180
+ }
181
+ if (error === null) {
182
+ return {
183
+ message: "null",
184
+ }
185
+ }
186
+ if (typeof error === "string") {
187
+ return {
188
+ message: error,
189
+ }
190
+ }
191
+ if (error instanceof Error) {
192
+ if (error.name === "SyntaxError") {
193
+ return {
194
+ message: error.message,
195
+ }
196
+ }
197
+ if (error.cause && error.cause.code === "PARSE_ERROR") {
198
+ if (error.messageHTML) {
199
+ return {
200
+ message: error.messageHTML,
201
+ }
202
+ }
203
+ return {
204
+ message: error.message,
205
+ }
206
+ }
207
+ // stackTrace formatted by V8
208
+ if (Error.captureStackTrace) {
209
+ return {
210
+ message: error.message,
211
+ stack: getErrorStackWithoutErrorMessage(error),
212
+ }
213
+ }
214
+ return {
215
+ message: error.message,
216
+ stack: error.stack ? ` ${error.stack}` : null,
217
+ }
218
+ }
219
+ if (typeof error === "object") {
220
+ return error
221
+ }
222
+ return {
223
+ message: JSON.stringify(error),
224
+ }
225
+ }
226
+
227
+ const getErrorStackWithoutErrorMessage = (error) => {
228
+ let stack = error.stack
229
+ const messageInStack = `${error.name}: ${error.message}`
230
+ if (stack.startsWith(messageInStack)) {
231
+ stack = stack.slice(messageInStack.length)
232
+ }
233
+ const nextLineIndex = stack.indexOf("\n")
234
+ if (nextLineIndex > -1) {
235
+ stack = stack.slice(nextLineIndex + 1)
236
+ }
237
+ return stack
238
+ }
239
+
240
+ const errorToHTML = (
241
+ error,
242
+ { url, line, column, reportedBy, requestedRessource },
243
+ ) => {
244
+ let { message, stack } = parseErrorInfo(error)
245
+ if (url) {
246
+ if (!stack || (error && error.name === "SyntaxError")) {
247
+ stack = ` at ${appendLineAndColumn(url, { line, column })}`
248
+ }
249
+ }
250
+ let tip = formatTip({ reportedBy, requestedRessource })
251
+ return {
252
+ theme:
253
+ error && error.cause && error.cause.code === "PARSE_ERROR"
254
+ ? "light"
255
+ : "dark",
256
+ title: "An error occured",
257
+ message,
258
+ stack,
259
+ tip: `${tip}
260
+ <br />
261
+ Click outside to close.`,
262
+ }
263
+ }
264
+
265
+ const formatTip = ({ reportedBy, requestedRessource }) => {
266
+ if (reportedBy === "browser") {
267
+ return `Reported by the browser while executing <code>${window.location.pathname}${window.location.search}</code>.`
268
+ }
269
+ return `Reported by the server while serving <code>${requestedRessource}</code>`
270
+ }
271
+
272
+ const replaceLinks = (string, { rootDirectoryUrl, openInEditor }) => {
273
+ // normalize line breaks
274
+ string = string.replace(/\n/g, "\n")
275
+ string = escapeHtml(string)
276
+ // render links
277
+ string = stringToStringWithLink(string, {
278
+ transform: (url, { line, column }) => {
279
+ const urlObject = new URL(url)
280
+
281
+ const onFileUrl = (fileUrlObject) => {
282
+ const atFsIndex = fileUrlObject.pathname.indexOf("/@fs/")
283
+ let fileUrl
284
+ if (atFsIndex > -1) {
285
+ const afterAtFs = fileUrlObject.pathname.slice(
286
+ atFsIndex + "/@fs/".length,
287
+ )
288
+ fileUrl = new URL(afterAtFs, "file:///").href
289
+ } else {
290
+ fileUrl = fileUrlObject.href
291
+ }
292
+ fileUrl = appendLineAndColumn(fileUrl, {
293
+ line,
294
+ column,
295
+ })
296
+ return link({
297
+ href: openInEditor
298
+ ? `javascript:window.fetch('/__open_in_editor__/${fileUrl}')`
299
+ : fileUrl,
300
+ text: fileUrl,
301
+ })
302
+ }
303
+
304
+ if (urlObject.origin === window.origin) {
305
+ const fileUrlObject = new URL(
306
+ `${urlObject.pathname.slice(1)}${urlObject.search}`,
307
+ rootDirectoryUrl,
308
+ )
309
+ return onFileUrl(fileUrlObject)
310
+ }
311
+ if (urlObject.href.startsWith("file:")) {
312
+ return onFileUrl(urlObject)
313
+ }
314
+ return link({
315
+ href: url,
316
+ text: appendLineAndColumn(url, { line, column }),
317
+ })
318
+ },
319
+ })
320
+ return string
321
+ }
322
+
323
+ const escapeHtml = (string) => {
324
+ return string
325
+ .replace(/&/g, "&amp;")
326
+ .replace(/</g, "&lt;")
327
+ .replace(/>/g, "&gt;")
328
+ .replace(/"/g, "&quot;")
329
+ .replace(/'/g, "&#039;")
330
+ }
331
+
332
+ const appendLineAndColumn = (url, { line, column }) => {
333
+ if (line !== undefined && column !== undefined) {
334
+ return `${url}:${line}:${column}`
335
+ }
336
+ if (line !== undefined) {
337
+ return `${url}:${line}`
338
+ }
339
+ return url
340
+ }
341
+
342
+ // `Error: yo
343
+ // at Object.execute (http://127.0.0.1:57300/build/src/__test__/file-throw.js:9:13)
344
+ // at doExec (http://127.0.0.1:3000/src/__test__/file-throw.js:452:38)
345
+ // at postOrderExec (http://127.0.0.1:3000/src/__test__/file-throw.js:448:16)
346
+ // at http://127.0.0.1:3000/src/__test__/file-throw.js:399:18`.replace(/(?:https?|ftp|file):\/\/(.*+)$/gm, (...args) => {
347
+ // debugger
348
+ // })
349
+ const stringToStringWithLink = (
350
+ source,
351
+ {
352
+ transform = (url) => {
353
+ return {
354
+ href: url,
355
+ text: url,
356
+ }
357
+ },
358
+ } = {},
359
+ ) => {
360
+ return source.replace(/(?:https?|ftp|file):\/\/\S+/gm, (match) => {
361
+ let linkHTML = ""
362
+
363
+ const lastChar = match[match.length - 1]
364
+
365
+ // hotfix because our url regex sucks a bit
366
+ const endsWithSeparationChar = lastChar === ")" || lastChar === ":"
367
+ if (endsWithSeparationChar) {
368
+ match = match.slice(0, -1)
369
+ }
370
+
371
+ const lineAndColumnPattern = /:([0-9]+):([0-9]+)$/
372
+ const lineAndColumMatch = match.match(lineAndColumnPattern)
373
+ if (lineAndColumMatch) {
374
+ const lineAndColumnString = lineAndColumMatch[0]
375
+ const lineNumber = lineAndColumMatch[1]
376
+ const columnNumber = lineAndColumMatch[2]
377
+ linkHTML = transform(match.slice(0, -lineAndColumnString.length), {
378
+ line: lineNumber,
379
+ column: columnNumber,
380
+ })
381
+ } else {
382
+ const linePattern = /:([0-9]+)$/
383
+ const lineMatch = match.match(linePattern)
384
+ if (lineMatch) {
385
+ const lineString = lineMatch[0]
386
+ const lineNumber = lineMatch[1]
387
+ linkHTML = transform(match.slice(0, -lineString.length), {
388
+ line: lineNumber,
389
+ })
390
+ } else {
391
+ linkHTML = transform(match, {})
392
+ }
393
+ }
394
+ if (endsWithSeparationChar) {
395
+ return `${linkHTML}${lastChar}`
396
+ }
397
+ return linkHTML
398
+ })
399
+ }
400
+
401
+ const link = ({ href, text = href }) => `<a href="${href}">${text}</a>`
@@ -1,10 +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
- export const installHtmlSupervisor = ({ logs, measurePerf }) => {
7
+ const supervisedScripts = []
8
+
9
+ export const installHtmlSupervisor = ({
10
+ rootDirectoryUrl,
11
+ logs,
12
+ measurePerf,
13
+ errorOverlay,
14
+ openInEditor,
15
+ }) => {
8
16
  const errorTransformer = null // could implement error stack remapping if needed
9
17
  const scriptExecutionResults = {}
10
18
  let collectCalled = false
@@ -32,17 +40,13 @@ export const installHtmlSupervisor = ({ logs, measurePerf }) => {
32
40
  }
33
41
  const onExecutionError = (
34
42
  executionResult,
35
- {
36
- currentScript,
37
- errorExposureInNotification = false,
38
- errorExposureInDocument = true,
39
- },
43
+ { currentScript, errorExposureInNotification = false },
40
44
  ) => {
41
45
  const error = executionResult.error
42
46
  if (error && error.code === "NETWORK_FAILURE") {
43
47
  if (currentScript) {
44
- const errorEvent = new Event("error")
45
- currentScript.dispatchEvent(errorEvent)
48
+ const currentScriptErrorEvent = new Event("error")
49
+ currentScript.dispatchEvent(currentScriptErrorEvent)
46
50
  }
47
51
  } else if (typeof error === "object") {
48
52
  const globalErrorEvent = new Event("error")
@@ -55,9 +59,6 @@ export const installHtmlSupervisor = ({ logs, measurePerf }) => {
55
59
  if (errorExposureInNotification) {
56
60
  displayErrorNotification(error)
57
61
  }
58
- if (errorExposureInDocument) {
59
- displayErrorInDocument(error)
60
- }
61
62
  executionResult.exceptionSource = unevalException(error)
62
63
  delete executionResult.error
63
64
  }
@@ -69,13 +70,16 @@ export const installHtmlSupervisor = ({ logs, measurePerf }) => {
69
70
  }
70
71
  }
71
72
 
72
- const performExecution = async ({
73
- src,
74
- type,
75
- currentScript,
76
- execute,
77
- // https://developer.mozilla.org/en-US/docs/web/html/element/script
78
- }) => {
73
+ const performExecution = async (
74
+ {
75
+ src,
76
+ type,
77
+ currentScript,
78
+ execute,
79
+ // https://developer.mozilla.org/en-US/docs/web/html/element/script
80
+ },
81
+ { reload = false } = {},
82
+ ) => {
79
83
  if (logs) {
80
84
  console.group(`[jsenv] loading ${type} ${src}`)
81
85
  }
@@ -84,7 +88,11 @@ export const installHtmlSupervisor = ({ logs, measurePerf }) => {
84
88
  let result
85
89
  let error
86
90
  try {
87
- result = await execute()
91
+ const urlObject = new URL(src, window.location)
92
+ if (reload) {
93
+ urlObject.searchParams.set("hmr", Date.now())
94
+ }
95
+ result = await execute(urlObject.href)
88
96
  completed = true
89
97
  } catch (e) {
90
98
  completed = false
@@ -152,12 +160,19 @@ export const installHtmlSupervisor = ({ logs, measurePerf }) => {
152
160
  }),
153
161
  )
154
162
  __html_supervisor__.addScriptToExecute = async (scriptToExecute) => {
163
+ if (!supervisedScripts.includes(scriptToExecute)) {
164
+ supervisedScripts.push(scriptToExecute)
165
+ scriptToExecute.reload = () => {
166
+ return performExecution(scriptToExecute, { reload: true })
167
+ }
168
+ }
169
+
155
170
  if (scriptToExecute.async) {
156
171
  performExecution(scriptToExecute)
157
172
  return
158
173
  }
159
174
  const useDeferQueue =
160
- scriptToExecute.defer || scriptToExecute.type === "js_module"
175
+ scriptToExecute.defer || scriptToExecute.type === "module"
161
176
  if (useDeferQueue) {
162
177
  // defer must wait for classic script to be done
163
178
  const classicExecutionPromise = classicExecutionQueue.getPromise()
@@ -202,14 +217,114 @@ export const installHtmlSupervisor = ({ logs, measurePerf }) => {
202
217
  copy.forEach((scriptToExecute) => {
203
218
  __html_supervisor__.addScriptToExecute(scriptToExecute)
204
219
  })
220
+
221
+ if (errorOverlay) {
222
+ window.addEventListener("error", (errorEvent) => {
223
+ if (!errorEvent.isTrusted) {
224
+ // ignore custom error event (not sent by browser)
225
+ return
226
+ }
227
+ const { error } = errorEvent
228
+ displayErrorInDocument(error, {
229
+ rootDirectoryUrl,
230
+ openInEditor,
231
+ url: errorEvent.filename,
232
+ line: errorEvent.lineno,
233
+ column: errorEvent.colno,
234
+ reportedBy: "browser",
235
+ })
236
+ })
237
+ if (window.__server_events__) {
238
+ const isExecuting = () => {
239
+ if (pendingExecutionCount > 0) {
240
+ return true
241
+ }
242
+ if (
243
+ document.readyState === "loading" ||
244
+ document.readyState === "interactive"
245
+ ) {
246
+ return true
247
+ }
248
+ if (window.__reloader__ && window.__reloader__.status === "reloading") {
249
+ return true
250
+ }
251
+ return false
252
+ }
253
+
254
+ window.__server_events__.addEventCallbacks({
255
+ error_while_serving_file: (serverErrorEvent) => {
256
+ if (!isExecuting()) {
257
+ return
258
+ }
259
+ const {
260
+ message,
261
+ stack,
262
+ traceUrl,
263
+ traceLine,
264
+ traceColumn,
265
+ traceMessage,
266
+ requestedRessource,
267
+ isFaviconAutoRequest,
268
+ } = JSON.parse(serverErrorEvent.data)
269
+ if (isFaviconAutoRequest) {
270
+ return
271
+ }
272
+ // setTimeout is to ensure the error
273
+ // dispatched on window by browser is displayed first,
274
+ // then the server error replaces it (because it contains more information)
275
+ setTimeout(() => {
276
+ displayErrorInDocument(
277
+ {
278
+ message,
279
+ stack:
280
+ stack && traceMessage
281
+ ? `${stack}\n\n${traceMessage}`
282
+ : stack
283
+ ? stack
284
+ : traceMessage
285
+ ? `\n${traceMessage}`
286
+ : "",
287
+ },
288
+ {
289
+ rootDirectoryUrl,
290
+ openInEditor,
291
+ url: traceUrl,
292
+ line: traceLine,
293
+ column: traceColumn,
294
+ reportedBy: "server",
295
+ requestedRessource,
296
+ },
297
+ )
298
+ }, 10)
299
+ },
300
+ })
301
+ }
302
+ }
303
+ }
304
+
305
+ __html_supervisor__.reloadSupervisedScript = ({ type, src }) => {
306
+ const supervisedScript = supervisedScripts.find(
307
+ (supervisedScriptCandidate) => {
308
+ if (type && supervisedScriptCandidate.type !== type) {
309
+ return false
310
+ }
311
+ if (supervisedScriptCandidate.src !== src) {
312
+ return false
313
+ }
314
+ return true
315
+ },
316
+ )
317
+ if (supervisedScript) {
318
+ supervisedScript.reload()
319
+ }
205
320
  }
206
321
 
207
322
  export const superviseScriptTypeModule = ({ src, isInline }) => {
208
323
  __html_supervisor__.addScriptToExecute({
209
324
  src,
210
- type: "js_module",
325
+ type: "module",
211
326
  isInline,
212
- execute: () => import(new URL(src, document.location.href).href),
327
+ execute: (url) => import(url),
213
328
  })
214
329
  }
215
330
 
@@ -14,7 +14,7 @@ window.__html_supervisor__ = {
14
14
  type: "js_classic",
15
15
  isInline,
16
16
  currentScript: document.currentScript,
17
- execute: () => {
17
+ execute: (url) => {
18
18
  return new Promise((resolve, reject) => {
19
19
  const script = document.createElement("script")
20
20
  if (crossorigin) {
@@ -23,8 +23,7 @@ window.__html_supervisor__ = {
23
23
  if (integrity) {
24
24
  script.integrity = integrity
25
25
  }
26
- script.src = src
27
- const scriptUrl = new URL(src, window.location).href
26
+ script.src = url
28
27
  let lastWindowErrorUrl
29
28
  let lastWindowError
30
29
  const windowErrorCallback = (e) => {
@@ -45,7 +44,7 @@ window.__html_supervisor__ = {
45
44
  })
46
45
  script.addEventListener("load", () => {
47
46
  cleanup()
48
- if (lastWindowErrorUrl === scriptUrl) {
47
+ if (lastWindowErrorUrl === url) {
49
48
  reject(lastWindowError)
50
49
  } else {
51
50
  resolve()