@jsenv/core 27.3.4 → 27.5.1
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 +524 -147
- 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 +7709 -7324
- package/package.json +15 -15
- package/{README.md → readme.md} +18 -7
- package/src/build/build.js +16 -18
- 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 +43 -9
- package/src/omega/kitchen.js +42 -25
- package/src/omega/omega_server.js +96 -74
- package/src/omega/server/file_service.js +256 -28
- 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 -8
- 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_overlay.js +401 -0
- package/src/plugins/html_supervisor/client/html_supervisor_installer.js +138 -23
- package/src/plugins/html_supervisor/client/html_supervisor_setup.js +3 -4
- package/src/plugins/html_supervisor/jsenv_plugin_html_supervisor.js +55 -23
- 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 -10
- package/src/{helpers/event_source/event_source.js → plugins/server_events/client/event_source_connection.js} +125 -33
- 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/plugins/url_analysis/jsenv_plugin_url_analysis.js +3 -1
- package/src/test/execute_plan.js +36 -54
- package/src/test/execute_test_plan.js +2 -2
- package/src/test/logs_file_execution.js +60 -27
- package/src/test/logs_file_execution.test.mjs +41 -0
- package/dist/js/event_source_client.js +0 -528
- 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 -203
- 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, "&")
|
|
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>`
|
|
@@ -1,10 +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
|
-
|
|
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
|
|
45
|
-
currentScript.dispatchEvent(
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
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 === "
|
|
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: "
|
|
325
|
+
type: "module",
|
|
211
326
|
isInline,
|
|
212
|
-
execute: () => import(
|
|
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 =
|
|
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 ===
|
|
47
|
+
if (lastWindowErrorUrl === url) {
|
|
49
48
|
reject(lastWindowError)
|
|
50
49
|
} else {
|
|
51
50
|
resolve()
|