@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.
- package/dist/js/autoreload.js +359 -0
- package/dist/js/execute_using_dynamic_import.js +1 -1
- package/dist/js/html_supervisor_installer.js +469 -254
- package/dist/js/html_supervisor_setup.js +3 -4
- package/dist/js/new_stylesheet.js +26 -58
- package/dist/js/server_events_client.js +307 -0
- package/dist/main.js +7558 -7311
- package/package.json +12 -10
- package/{README.md → readme.md} +8 -9
- package/src/build/build.js +12 -16
- package/src/build/start_build_server.js +24 -28
- package/src/dev/start_dev_server.js +34 -96
- package/src/execute/execute.js +17 -35
- package/src/omega/errors.js +20 -18
- package/src/omega/kitchen.js +7 -6
- package/src/omega/omega_server.js +96 -127
- package/src/omega/server/file_service.js +247 -46
- package/src/omega/url_graph.js +33 -20
- package/src/plugins/autoreload/client/autoreload.js +201 -0
- package/src/plugins/autoreload/{dev_sse/client → client}/autoreload_preference.js +0 -0
- package/src/plugins/autoreload/{dev_sse/client → client}/reload.js +29 -10
- package/src/plugins/autoreload/{dev_sse/client → client}/url_helpers.js +0 -0
- package/src/plugins/autoreload/jsenv_plugin_autoreload.js +4 -4
- package/src/plugins/autoreload/{dev_sse/jsenv_plugin_dev_sse_client.js → jsenv_plugin_autoreload_client.js} +8 -8
- package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +196 -0
- package/src/{dev/plugins → plugins}/explorer/client/explorer.html +0 -0
- package/src/{dev/plugins → plugins}/explorer/client/jsenv.png +0 -0
- package/src/{dev/plugins → plugins}/explorer/jsenv_plugin_explorer.js +1 -3
- package/src/plugins/html_supervisor/client/error_formatter.js +300 -0
- package/src/plugins/html_supervisor/client/error_overlay.js +172 -0
- package/src/plugins/html_supervisor/client/html_supervisor_installer.js +124 -54
- package/src/plugins/html_supervisor/client/html_supervisor_setup.js +3 -4
- package/src/plugins/html_supervisor/jsenv_plugin_html_supervisor.js +72 -27
- package/src/plugins/inline/jsenv_plugin_html_inline_content.js +97 -117
- package/src/plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +66 -58
- package/src/plugins/plugin_controller.js +102 -67
- package/src/plugins/plugins.js +10 -8
- package/src/{helpers/event_source/event_source.js → plugins/server_events/client/event_source_connection.js} +102 -31
- package/src/plugins/server_events/client/server_events_client.js +17 -0
- package/src/plugins/server_events/jsenv_plugin_server_events_client_injection.js +48 -0
- package/src/plugins/server_events/server_events_dispatcher.js +69 -0
- package/src/{dev/plugins → plugins}/toolbar/client/animation/toolbar_animation.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/eventsource/eventsource.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/eventsource/toolbar_eventsource.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/execution/execution.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/execution/toolbar_execution.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/focus/focus.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/focus/toolbar_focus.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/jsenv_logo.svg +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/notification/toolbar_notification.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/responsive/overflow_menu.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/responsive/toolbar_responsive.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/settings/settings.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/settings/toolbar_settings.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/theme/jsenv_theme.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/theme/light_theme.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/theme/toolbar_theme.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/toolbar.html +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/toolbar_injector.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/toolbar_main.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/toolbar_main.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/tooltip/tooltip.css +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/tooltip/tooltip.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/animation.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/dom.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/fetch_using_xhr.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/fetching.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/iframe_to_parent_href.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/jsenv_logger.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/preferences.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/responsive.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/util/util.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/client/variant/variant.js +0 -0
- package/src/{dev/plugins → plugins}/toolbar/jsenv_plugin_toolbar.js +0 -0
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_html.js +4 -3
- package/src/plugins/transpilation/babel/new_stylesheet/client/new_stylesheet.js +25 -55
- package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +44 -24
- package/src/plugins/transpilation/jsenv_plugin_transpilation.js +6 -1
- package/src/plugins/url_analysis/html/html_urls.js +8 -8
- package/src/test/execute_plan.js +36 -54
- package/src/test/execute_test_plan.js +2 -2
- package/dist/js/event_source_client.js +0 -549
- package/src/helpers/event_source/sse_service.js +0 -53
- package/src/plugins/autoreload/dev_sse/client/event_source_client.js +0 -193
- package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_server.js +0 -192
- 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, "&")
|
|
235
|
+
.replace(/</g, "<")
|
|
236
|
+
.replace(/>/g, ">")
|
|
237
|
+
.replace(/"/g, """)
|
|
238
|
+
.replace(/'/g, "'")
|
|
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 "./
|
|
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
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
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 === "
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
{
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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: "
|
|
322
|
+
type: "module",
|
|
253
323
|
isInline,
|
|
254
|
-
execute: () => import(
|
|
324
|
+
execute: (url) => import(url),
|
|
255
325
|
})
|
|
256
326
|
}
|
|
257
327
|
|