@jsenv/core 27.5.1 → 27.6.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.
- package/dist/js/autoreload.js +7 -0
- package/dist/js/html_supervisor_installer.js +524 -344
- package/dist/main.js +179 -95
- package/package.json +3 -2
- package/src/dev/start_dev_server.js +4 -0
- package/src/omega/kitchen.js +13 -15
- package/src/omega/omega_server.js +4 -0
- package/src/omega/server/file_service.js +9 -66
- package/src/omega/url_graph/url_graph_load.js +0 -2
- package/src/omega/url_graph/url_info_transformations.js +27 -0
- package/src/omega/url_graph.js +1 -0
- package/src/plugins/autoreload/client/autoreload.js +7 -0
- package/src/plugins/html_supervisor/client/error_formatter.js +413 -0
- package/src/plugins/html_supervisor/client/error_overlay.js +62 -272
- package/src/plugins/html_supervisor/client/error_site_remap.js +85 -0
- package/src/plugins/html_supervisor/client/html_supervisor_installer.js +26 -77
- package/src/plugins/html_supervisor/jsenv_plugin_html_supervisor.js +103 -14
- package/src/test/execute_test_plan.js +1 -2
|
@@ -1,32 +1,38 @@
|
|
|
1
|
+
import { formatError } from "./error_formatter.js"
|
|
2
|
+
|
|
1
3
|
const JSENV_ERROR_OVERLAY_TAGNAME = "jsenv-error-overlay"
|
|
2
4
|
|
|
3
5
|
export const displayErrorInDocument = (
|
|
4
6
|
error,
|
|
5
7
|
{
|
|
6
8
|
rootDirectoryUrl,
|
|
9
|
+
errorBaseUrl,
|
|
7
10
|
openInEditor,
|
|
8
11
|
url,
|
|
9
12
|
line,
|
|
10
13
|
column,
|
|
11
|
-
|
|
12
|
-
requestedRessource,
|
|
14
|
+
codeFrame,
|
|
13
15
|
},
|
|
14
16
|
) => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
const { theme, title, text, tip, errorDetailsPromise } = formatError(error, {
|
|
18
|
+
rootDirectoryUrl,
|
|
19
|
+
errorBaseUrl,
|
|
20
|
+
openInEditor,
|
|
19
21
|
url,
|
|
20
22
|
line,
|
|
21
23
|
column,
|
|
22
|
-
|
|
23
|
-
requestedRessource,
|
|
24
|
+
codeFrame,
|
|
24
25
|
})
|
|
26
|
+
|
|
25
27
|
let jsenvErrorOverlay = new JsenvErrorOverlay({
|
|
26
28
|
theme,
|
|
27
29
|
title,
|
|
28
|
-
text
|
|
30
|
+
text,
|
|
29
31
|
tip,
|
|
32
|
+
errorDetailsPromise,
|
|
33
|
+
})
|
|
34
|
+
document.querySelectorAll(JSENV_ERROR_OVERLAY_TAGNAME).forEach((node) => {
|
|
35
|
+
node.parentNode.removeChild(node)
|
|
30
36
|
})
|
|
31
37
|
document.body.appendChild(jsenvErrorOverlay)
|
|
32
38
|
const removeErrorOverlay = () => {
|
|
@@ -45,29 +51,24 @@ export const displayErrorInDocument = (
|
|
|
45
51
|
return removeErrorOverlay
|
|
46
52
|
}
|
|
47
53
|
|
|
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
54
|
class JsenvErrorOverlay extends HTMLElement {
|
|
67
|
-
constructor({ theme, title, text, tip }) {
|
|
55
|
+
constructor({ theme, title, text, tip, errorDetailsPromise }) {
|
|
68
56
|
super()
|
|
69
57
|
this.root = this.attachShadow({ mode: "open" })
|
|
70
|
-
this.root.innerHTML =
|
|
58
|
+
this.root.innerHTML = `
|
|
59
|
+
<style>
|
|
60
|
+
${overlayCSS}
|
|
61
|
+
</style>
|
|
62
|
+
<div class="backdrop"></div>
|
|
63
|
+
<div class="overlay" data-theme=${theme}>
|
|
64
|
+
<h1 class="title">
|
|
65
|
+
${title}
|
|
66
|
+
</h1>
|
|
67
|
+
<pre class="text">${text}</pre>
|
|
68
|
+
<div class="tip">
|
|
69
|
+
${tip}
|
|
70
|
+
</div>
|
|
71
|
+
</div>`
|
|
71
72
|
this.root.querySelector(".backdrop").onclick = () => {
|
|
72
73
|
if (!this.parentNode) {
|
|
73
74
|
// not in document anymore
|
|
@@ -76,19 +77,44 @@ class JsenvErrorOverlay extends HTMLElement {
|
|
|
76
77
|
this.root.querySelector(".backdrop").onclick = null
|
|
77
78
|
this.parentNode.removeChild(this)
|
|
78
79
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
if (errorDetailsPromise) {
|
|
81
|
+
errorDetailsPromise.then((errorDetails) => {
|
|
82
|
+
if (!errorDetails || !this.parentNode) {
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
const { codeFrame, cause } = errorDetails
|
|
86
|
+
if (codeFrame) {
|
|
87
|
+
this.root.querySelector(".text").innerHTML += `\n\n${codeFrame}`
|
|
88
|
+
}
|
|
89
|
+
if (cause) {
|
|
90
|
+
const causeIndented = prefixRemainingLines(cause, " ")
|
|
91
|
+
this.root.querySelector(
|
|
92
|
+
".text",
|
|
93
|
+
).innerHTML += `\n [cause]: ${causeIndented}`
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
}
|
|
83
97
|
}
|
|
84
98
|
}
|
|
85
99
|
|
|
100
|
+
const prefixRemainingLines = (text, prefix) => {
|
|
101
|
+
const lines = text.split(/\r?\n/)
|
|
102
|
+
const firstLine = lines.shift()
|
|
103
|
+
let result = firstLine
|
|
104
|
+
let i = 0
|
|
105
|
+
while (i < lines.length) {
|
|
106
|
+
const line = lines[i]
|
|
107
|
+
i++
|
|
108
|
+
result += line.length ? `\n${prefix}${line}` : `\n`
|
|
109
|
+
}
|
|
110
|
+
return result
|
|
111
|
+
}
|
|
112
|
+
|
|
86
113
|
if (customElements && !customElements.get(JSENV_ERROR_OVERLAY_TAGNAME)) {
|
|
87
114
|
customElements.define(JSENV_ERROR_OVERLAY_TAGNAME, JsenvErrorOverlay)
|
|
88
115
|
}
|
|
89
116
|
|
|
90
|
-
const
|
|
91
|
-
<style>
|
|
117
|
+
const overlayCSS = `
|
|
92
118
|
:host {
|
|
93
119
|
position: fixed;
|
|
94
120
|
z-index: 99999;
|
|
@@ -162,240 +188,4 @@ pre {
|
|
|
162
188
|
|
|
163
189
|
pre a {
|
|
164
190
|
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, "&")
|
|
326
|
-
.replace(/</g, "<")
|
|
327
|
-
.replace(/>/g, ">")
|
|
328
|
-
.replace(/"/g, """)
|
|
329
|
-
.replace(/'/g, "'")
|
|
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>`
|
|
191
|
+
}`
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { memoize } from "@jsenv/utils/src/memoize/memoize.js"
|
|
2
|
+
import { remapSourcePosition } from "@jsenv/sourcemap/src/error_stack_remap/remap_source_position.js"
|
|
3
|
+
import { SOURCEMAP } from "@jsenv/sourcemap/src/sourcemap_comment.js"
|
|
4
|
+
import { DATA_URL } from "@jsenv/urls/src/data_url.js"
|
|
5
|
+
|
|
6
|
+
const loadSourceMapConsumer = memoize(async () => {
|
|
7
|
+
const script = document.createElement("script")
|
|
8
|
+
script.src = "https://unpkg.com/source-map@0.7.3/dist/source-map.js"
|
|
9
|
+
|
|
10
|
+
const scriptLoadedPromise = new Promise((resolve) => {
|
|
11
|
+
script.onload = resolve
|
|
12
|
+
})
|
|
13
|
+
document.head.appendChild(script)
|
|
14
|
+
await scriptLoadedPromise
|
|
15
|
+
const { SourceMapConsumer } = window.sourceMap
|
|
16
|
+
await SourceMapConsumer.initialize({
|
|
17
|
+
"lib/mappings.wasm": "https://unpkg.com/source-map@0.7.3/lib/mappings.wasm",
|
|
18
|
+
})
|
|
19
|
+
return SourceMapConsumer
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
export const remapErrorSite = async ({ url, line, column }) => {
|
|
23
|
+
const asServerUrl = (url) => {
|
|
24
|
+
if (url.startsWith("file:///")) {
|
|
25
|
+
url = `${window.origin}/@fs/${url.slice("file:///".length)}`
|
|
26
|
+
}
|
|
27
|
+
return url
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const SourceMapConsumer = await loadSourceMapConsumer()
|
|
31
|
+
const original = await remapSourcePosition({
|
|
32
|
+
source: url,
|
|
33
|
+
line,
|
|
34
|
+
column,
|
|
35
|
+
resolveFile: (specifier) => new URL(specifier, `${window.origin}/`).href,
|
|
36
|
+
urlToSourcemapConsumer: async (url) => {
|
|
37
|
+
const serverUrl = asServerUrl(url)
|
|
38
|
+
const fileResponse = await window.fetch(serverUrl)
|
|
39
|
+
const text = await fileResponse.text()
|
|
40
|
+
const jsSourcemapComment = SOURCEMAP.readComment({
|
|
41
|
+
contentType: "text/javascript",
|
|
42
|
+
content: text,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const jsSourcemapUrl = jsSourcemapComment.specifier
|
|
46
|
+
let sourcemapUrl
|
|
47
|
+
let sourcemapUrlContent
|
|
48
|
+
if (jsSourcemapUrl.startsWith("data:")) {
|
|
49
|
+
sourcemapUrl = url
|
|
50
|
+
sourcemapUrlContent = window.atob(DATA_URL.parse(jsSourcemapUrl).data)
|
|
51
|
+
} else {
|
|
52
|
+
sourcemapUrl = new URL(jsSourcemapUrl, url).href
|
|
53
|
+
const sourcemapResponse = await window.fetch(sourcemapUrl)
|
|
54
|
+
sourcemapUrlContent = await sourcemapResponse.text()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const sourceMap = JSON.parse(sourcemapUrlContent)
|
|
58
|
+
let { sourcesContent } = sourceMap
|
|
59
|
+
if (!sourcesContent) {
|
|
60
|
+
sourcesContent = []
|
|
61
|
+
sourceMap.sourcesContent = sourcesContent
|
|
62
|
+
}
|
|
63
|
+
let firstSourceMapSourceFailure = null
|
|
64
|
+
await Promise.all(
|
|
65
|
+
sourceMap.sources.map(async (source, index) => {
|
|
66
|
+
if (index in sourcesContent) return
|
|
67
|
+
let sourceUrl = new URL(source, sourcemapUrl).href
|
|
68
|
+
sourceUrl = asServerUrl(sourceUrl)
|
|
69
|
+
try {
|
|
70
|
+
const sourceResponse = await window.fetch(sourceUrl)
|
|
71
|
+
const sourceContent = await sourceResponse.text()
|
|
72
|
+
sourcesContent[index] = sourceContent
|
|
73
|
+
} catch (e) {
|
|
74
|
+
firstSourceMapSourceFailure = e
|
|
75
|
+
}
|
|
76
|
+
}),
|
|
77
|
+
)
|
|
78
|
+
if (firstSourceMapSourceFailure) {
|
|
79
|
+
return null
|
|
80
|
+
}
|
|
81
|
+
return new SourceMapConsumer(sourceMap)
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
return original
|
|
85
|
+
}
|
|
@@ -11,6 +11,7 @@ export const installHtmlSupervisor = ({
|
|
|
11
11
|
logs,
|
|
12
12
|
measurePerf,
|
|
13
13
|
errorOverlay,
|
|
14
|
+
errorBaseUrl,
|
|
14
15
|
openInEditor,
|
|
15
16
|
}) => {
|
|
16
17
|
const errorTransformer = null // could implement error stack remapping if needed
|
|
@@ -87,11 +88,15 @@ export const installHtmlSupervisor = ({
|
|
|
87
88
|
let completed
|
|
88
89
|
let result
|
|
89
90
|
let error
|
|
91
|
+
const urlObject = new URL(src, window.location)
|
|
92
|
+
if (reload) {
|
|
93
|
+
urlObject.searchParams.set("hmr", Date.now())
|
|
94
|
+
}
|
|
95
|
+
__html_supervisor__.currentExecution = {
|
|
96
|
+
type: type === "module" ? "dynamic_import" : "script_injection",
|
|
97
|
+
url: urlObject.href,
|
|
98
|
+
}
|
|
90
99
|
try {
|
|
91
|
-
const urlObject = new URL(src, window.location)
|
|
92
|
-
if (reload) {
|
|
93
|
-
urlObject.searchParams.set("hmr", Date.now())
|
|
94
|
-
}
|
|
95
100
|
result = await execute(urlObject.href)
|
|
96
101
|
completed = true
|
|
97
102
|
} catch (e) {
|
|
@@ -109,6 +114,7 @@ export const installHtmlSupervisor = ({
|
|
|
109
114
|
console.log(`${type} load ended`)
|
|
110
115
|
console.groupEnd()
|
|
111
116
|
}
|
|
117
|
+
__html_supervisor__.currentExecution = null
|
|
112
118
|
return
|
|
113
119
|
}
|
|
114
120
|
const executionResult = {
|
|
@@ -139,6 +145,7 @@ export const installHtmlSupervisor = ({
|
|
|
139
145
|
if (logs) {
|
|
140
146
|
console.groupEnd()
|
|
141
147
|
}
|
|
148
|
+
__html_supervisor__.currentExecution = null
|
|
142
149
|
}
|
|
143
150
|
|
|
144
151
|
const classicExecutionQueue = createExecutionQueue(performExecution)
|
|
@@ -219,86 +226,28 @@ export const installHtmlSupervisor = ({
|
|
|
219
226
|
})
|
|
220
227
|
|
|
221
228
|
if (errorOverlay) {
|
|
229
|
+
const onErrorReportedByBrowser = (error, { url, line, column }) => {
|
|
230
|
+
displayErrorInDocument(error, {
|
|
231
|
+
rootDirectoryUrl,
|
|
232
|
+
errorBaseUrl,
|
|
233
|
+
openInEditor,
|
|
234
|
+
url,
|
|
235
|
+
line,
|
|
236
|
+
column,
|
|
237
|
+
})
|
|
238
|
+
}
|
|
222
239
|
window.addEventListener("error", (errorEvent) => {
|
|
223
240
|
if (!errorEvent.isTrusted) {
|
|
224
241
|
// ignore custom error event (not sent by browser)
|
|
225
242
|
return
|
|
226
243
|
}
|
|
227
|
-
const { error } = errorEvent
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
line: errorEvent.lineno,
|
|
233
|
-
column: errorEvent.colno,
|
|
234
|
-
reportedBy: "browser",
|
|
244
|
+
const { error, filename, lineno, colno } = errorEvent
|
|
245
|
+
onErrorReportedByBrowser(error, {
|
|
246
|
+
url: filename,
|
|
247
|
+
line: lineno,
|
|
248
|
+
column: colno,
|
|
235
249
|
})
|
|
236
250
|
})
|
|
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
251
|
}
|
|
303
252
|
}
|
|
304
253
|
|