@jsenv/core 33.0.2 → 34.0.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 +1 -4
- package/dist/js/supervisor.js +498 -290
- package/dist/jsenv.js +938 -370
- package/package.json +2 -3
- package/src/basic_fetch.js +23 -13
- package/src/build/start_build_server.js +3 -2
- package/src/dev/file_service.js +1 -1
- package/src/dev/start_dev_server.js +9 -6
- package/src/execute/execute.js +7 -18
- package/src/execute/runtimes/browsers/from_playwright.js +168 -32
- package/src/execute/runtimes/browsers/webkit.js +1 -1
- package/src/execute/web_server_param.js +68 -0
- package/src/kitchen/compat/features_compatibility.js +3 -0
- package/src/plugins/autoreload/client/reload.js +1 -4
- package/src/plugins/inline/jsenv_plugin_html_inline_content.js +30 -18
- package/src/plugins/plugins.js +1 -1
- package/src/plugins/ribbon/jsenv_plugin_ribbon.js +3 -2
- package/src/plugins/supervisor/client/supervisor.js +467 -287
- package/src/plugins/supervisor/html_supervisor_injection.js +281 -0
- package/src/plugins/supervisor/js_supervisor_injection.js +283 -0
- package/src/plugins/supervisor/jsenv_plugin_supervisor.js +48 -233
- package/src/plugins/transpilation/as_js_classic/convert_js_module_to_js_classic.js +67 -30
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_html.js +1 -1
- package/src/plugins/transpilation/babel/jsenv_plugin_babel.js +5 -0
- package/src/plugins/transpilation/jsenv_plugin_top_level_await.js +1 -1
- package/src/test/execute_steps.js +10 -18
- package/src/test/execute_test_plan.js +12 -60
- package/src/test/logs_file_execution.js +74 -28
- package/dist/js/babel_plugin_transform_modules_systemjs.cjs +0 -392
- package/dist/js/script_type_module_supervisor.js +0 -109
- package/src/plugins/supervisor/client/script_type_module_supervisor.js +0 -98
- package/src/plugins/transpilation/as_js_classic/babel_plugin_transform_modules_systemjs.cjs +0 -608
|
@@ -2,24 +2,413 @@ window.__supervisor__ = (() => {
|
|
|
2
2
|
const notImplemented = () => {
|
|
3
3
|
throw new Error(`window.__supervisor__.setup() not called`)
|
|
4
4
|
}
|
|
5
|
-
const executionResults = {}
|
|
6
5
|
const supervisor = {
|
|
7
|
-
|
|
6
|
+
reportException: notImplemented,
|
|
8
7
|
superviseScript: notImplemented,
|
|
8
|
+
superviseScriptTypeModule: notImplemented,
|
|
9
9
|
reloadSupervisedScript: notImplemented,
|
|
10
10
|
getDocumentExecutionResult: notImplemented,
|
|
11
|
-
executionResults,
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
const executionResults = {}
|
|
14
|
+
let documentExecutionStartTime
|
|
15
15
|
try {
|
|
16
|
-
|
|
16
|
+
documentExecutionStartTime = window.performance.timing.navigationStart
|
|
17
17
|
} catch (e) {
|
|
18
|
-
|
|
18
|
+
documentExecutionStartTime = Date.now()
|
|
19
|
+
}
|
|
20
|
+
let documentExecutionEndTime
|
|
21
|
+
supervisor.setup = ({
|
|
22
|
+
rootDirectoryUrl,
|
|
23
|
+
scriptInfos,
|
|
24
|
+
serverIsJsenvDevServer,
|
|
25
|
+
logs,
|
|
26
|
+
errorOverlay,
|
|
27
|
+
errorBaseUrl,
|
|
28
|
+
openInEditor,
|
|
29
|
+
}) => {
|
|
30
|
+
const executions = {}
|
|
31
|
+
const promises = []
|
|
32
|
+
let remainingScriptCount = scriptInfos.length
|
|
33
|
+
|
|
34
|
+
// respect execution order
|
|
35
|
+
// - wait for classic scripts to be done (non async)
|
|
36
|
+
// - wait module script previous execution (non async)
|
|
37
|
+
// see https://gist.github.com/jakub-g/385ee6b41085303a53ad92c7c8afd7a6#typemodule-vs-non-module-typetextjavascript-vs-script-nomodule
|
|
38
|
+
const executionQueue = []
|
|
39
|
+
let executing = false
|
|
40
|
+
const addToExecutionQueue = async (execution) => {
|
|
41
|
+
if (execution.async) {
|
|
42
|
+
execution.execute()
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
if (executing) {
|
|
46
|
+
executionQueue.push(execution)
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
execThenDequeue(execution)
|
|
50
|
+
}
|
|
51
|
+
const execThenDequeue = async (execution) => {
|
|
52
|
+
executing = true
|
|
53
|
+
// start next js module execution as soon as current js module starts to execute
|
|
54
|
+
// (do not wait in case of top level await)
|
|
55
|
+
let resolveExecutingPromise
|
|
56
|
+
const executingPromise = new Promise((resolve) => {
|
|
57
|
+
resolveExecutingPromise = resolve
|
|
58
|
+
})
|
|
59
|
+
const promise = execution.execute({
|
|
60
|
+
onExecuting: () => resolveExecutingPromise(),
|
|
61
|
+
})
|
|
62
|
+
await Promise.race([promise, executingPromise])
|
|
63
|
+
executing = false
|
|
64
|
+
if (executionQueue.length) {
|
|
65
|
+
const nextExecution = executionQueue.shift()
|
|
66
|
+
execThenDequeue(nextExecution)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const asExecutionId = (src) => {
|
|
71
|
+
const url = new URL(src, window.location).href
|
|
72
|
+
if (url.startsWith(window.location.origin)) {
|
|
73
|
+
return src
|
|
74
|
+
}
|
|
75
|
+
return url
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const createExecutionController = (src, type) => {
|
|
79
|
+
const result = {
|
|
80
|
+
status: "pending",
|
|
81
|
+
duration: null,
|
|
82
|
+
coverage: null,
|
|
83
|
+
exception: null,
|
|
84
|
+
value: null,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let resolve
|
|
88
|
+
const promise = new Promise((_resolve) => {
|
|
89
|
+
resolve = _resolve
|
|
90
|
+
})
|
|
91
|
+
promises.push(promise)
|
|
92
|
+
executionResults[src] = result
|
|
93
|
+
|
|
94
|
+
const start = () => {
|
|
95
|
+
result.duration = null
|
|
96
|
+
result.coverage = null
|
|
97
|
+
result.status = "started"
|
|
98
|
+
result.exception = null
|
|
99
|
+
if (logs) {
|
|
100
|
+
console.group(`[jsenv] ${src} execution started (${type})`)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const end = () => {
|
|
104
|
+
const now = Date.now()
|
|
105
|
+
remainingScriptCount--
|
|
106
|
+
result.duration = now - documentExecutionStartTime
|
|
107
|
+
result.coverage = window.__coverage__
|
|
108
|
+
if (logs) {
|
|
109
|
+
console.log(`execution ${result.status}`)
|
|
110
|
+
console.groupEnd()
|
|
111
|
+
}
|
|
112
|
+
if (remainingScriptCount === 0) {
|
|
113
|
+
documentExecutionEndTime = now
|
|
114
|
+
}
|
|
115
|
+
resolve()
|
|
116
|
+
}
|
|
117
|
+
const complete = () => {
|
|
118
|
+
result.status = "completed"
|
|
119
|
+
end()
|
|
120
|
+
}
|
|
121
|
+
const fail = (error, info) => {
|
|
122
|
+
result.status = "failed"
|
|
123
|
+
const exception = supervisor.createException(error, info)
|
|
124
|
+
result.exception = exception
|
|
125
|
+
end()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { result, start, complete, fail }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const prepareJsClassicRemoteExecution = (src) => {
|
|
132
|
+
const urlObject = new URL(src, window.location)
|
|
133
|
+
const url = urlObject.href
|
|
134
|
+
const { result, start, complete, fail } = createExecutionController(
|
|
135
|
+
src,
|
|
136
|
+
"js_classic",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
let parentNode
|
|
140
|
+
let currentScript
|
|
141
|
+
let nodeToReplace
|
|
142
|
+
let currentScriptClone
|
|
143
|
+
const init = () => {
|
|
144
|
+
currentScript = document.currentScript
|
|
145
|
+
parentNode = currentScript.parentNode
|
|
146
|
+
executions[src].async = currentScript.async
|
|
147
|
+
}
|
|
148
|
+
const execute = async ({ isReload } = {}) => {
|
|
149
|
+
start()
|
|
150
|
+
currentScriptClone = prepareScriptToLoad(currentScript)
|
|
151
|
+
if (isReload) {
|
|
152
|
+
urlObject.searchParams.set("hmr", Date.now())
|
|
153
|
+
nodeToReplace = currentScriptClone
|
|
154
|
+
currentScriptClone.src = urlObject.href
|
|
155
|
+
} else {
|
|
156
|
+
nodeToReplace = currentScript
|
|
157
|
+
currentScriptClone.src = url
|
|
158
|
+
}
|
|
159
|
+
const scriptLoadPromise = getScriptLoadPromise(currentScriptClone)
|
|
160
|
+
parentNode.replaceChild(currentScriptClone, nodeToReplace)
|
|
161
|
+
const { detectedBy, failed, error } = await scriptLoadPromise
|
|
162
|
+
if (failed) {
|
|
163
|
+
if (detectedBy === "script_error_event") {
|
|
164
|
+
// window.error won't be dispatched for this error
|
|
165
|
+
reportErrorBackToBrowser(error)
|
|
166
|
+
}
|
|
167
|
+
fail(error, {
|
|
168
|
+
message: `Error while loading script: ${urlObject.href}`,
|
|
169
|
+
reportedBy: "script_error_event",
|
|
170
|
+
url: urlObject.href,
|
|
171
|
+
})
|
|
172
|
+
if (detectedBy === "script_error_event") {
|
|
173
|
+
supervisor.reportException(result.exception)
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
complete()
|
|
177
|
+
}
|
|
178
|
+
return result
|
|
179
|
+
}
|
|
180
|
+
executions[src] = { init, execute }
|
|
181
|
+
}
|
|
182
|
+
const prepareJsClassicInlineExecution = (src) => {
|
|
183
|
+
const { start, complete, fail } = createExecutionController(
|
|
184
|
+
src,
|
|
185
|
+
"js_classic",
|
|
186
|
+
)
|
|
187
|
+
const end = complete
|
|
188
|
+
const error = (e) => {
|
|
189
|
+
reportErrorBackToBrowser(e) // supervision shallowed the error, report back to browser
|
|
190
|
+
fail(e)
|
|
191
|
+
}
|
|
192
|
+
executions[src] = { isInline: true, start, end, error }
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const isWebkitOrSafari =
|
|
196
|
+
typeof window.webkitConvertPointFromNodeToPage === "function"
|
|
197
|
+
// https://twitter.com/damienmaillard/status/1554752482273787906
|
|
198
|
+
const prepareJsModuleExecutionWithDynamicImport = (src) => {
|
|
199
|
+
const urlObject = new URL(src, window.location)
|
|
200
|
+
const { result, start, complete, fail } = createExecutionController(
|
|
201
|
+
src,
|
|
202
|
+
"js_classic",
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
let importFn
|
|
206
|
+
let currentScript
|
|
207
|
+
const init = (_importFn) => {
|
|
208
|
+
importFn = _importFn
|
|
209
|
+
currentScript = document.querySelector(
|
|
210
|
+
`script[type="module"][inlined-from-src="${src}"]`,
|
|
211
|
+
)
|
|
212
|
+
executions[src].async = currentScript.async
|
|
213
|
+
}
|
|
214
|
+
const execute = async ({ isReload } = {}) => {
|
|
215
|
+
start()
|
|
216
|
+
if (isReload) {
|
|
217
|
+
urlObject.searchParams.set("hmr", Date.now())
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
const namespace = await importFn(urlObject.href)
|
|
221
|
+
complete(namespace)
|
|
222
|
+
return result
|
|
223
|
+
} catch (e) {
|
|
224
|
+
fail(e, {
|
|
225
|
+
message: `Error while importing module: ${urlObject.href}`,
|
|
226
|
+
reportedBy: "dynamic_import",
|
|
227
|
+
url: urlObject.href,
|
|
228
|
+
})
|
|
229
|
+
if (isWebkitOrSafari) {
|
|
230
|
+
supervisor.reportException(result.exception)
|
|
231
|
+
}
|
|
232
|
+
return result
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
executions[src] = { init, execute }
|
|
236
|
+
}
|
|
237
|
+
const prepareJsModuleExecutionWithScriptThenDynamicImport = (src) => {
|
|
238
|
+
const urlObject = new URL(src, window.location)
|
|
239
|
+
const { result, start, complete, fail } = createExecutionController(
|
|
240
|
+
src,
|
|
241
|
+
"js_module",
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
let importFn
|
|
245
|
+
let currentScript
|
|
246
|
+
let parentNode
|
|
247
|
+
let nodeToReplace
|
|
248
|
+
let currentScriptClone
|
|
249
|
+
const init = (_importFn) => {
|
|
250
|
+
importFn = _importFn
|
|
251
|
+
currentScript = document.querySelector(
|
|
252
|
+
`script[type="module"][inlined-from-src="${src}"]`,
|
|
253
|
+
)
|
|
254
|
+
parentNode = currentScript.parentNode
|
|
255
|
+
executions[src].async = currentScript.async
|
|
256
|
+
}
|
|
257
|
+
const execute = async ({ isReload, onExecuting = () => {} } = {}) => {
|
|
258
|
+
start()
|
|
259
|
+
currentScriptClone = prepareScriptToLoad(currentScript)
|
|
260
|
+
if (isReload) {
|
|
261
|
+
urlObject.searchParams.set("hmr", Date.now())
|
|
262
|
+
nodeToReplace = currentScriptClone
|
|
263
|
+
currentScriptClone.src = urlObject.href
|
|
264
|
+
} else {
|
|
265
|
+
nodeToReplace = currentScript
|
|
266
|
+
currentScriptClone.src = urlObject.href
|
|
267
|
+
}
|
|
268
|
+
const scriptLoadResultPromise = getScriptLoadPromise(currentScriptClone)
|
|
269
|
+
parentNode.replaceChild(currentScriptClone, nodeToReplace)
|
|
270
|
+
const { detectedBy, failed, error } = await scriptLoadResultPromise
|
|
271
|
+
|
|
272
|
+
if (failed) {
|
|
273
|
+
// if (detectedBy === "script_error_event") {
|
|
274
|
+
// reportErrorBackToBrowser(error)
|
|
275
|
+
// }
|
|
276
|
+
fail(error, {
|
|
277
|
+
message: `Error while loading module: ${urlObject.href}`,
|
|
278
|
+
reportedBy: "script_error_event",
|
|
279
|
+
url: urlObject.href,
|
|
280
|
+
})
|
|
281
|
+
if (detectedBy === "script_error_event") {
|
|
282
|
+
supervisor.reportException(result.exception)
|
|
283
|
+
}
|
|
284
|
+
return result
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
onExecuting()
|
|
288
|
+
result.status = "executing"
|
|
289
|
+
if (logs) {
|
|
290
|
+
console.log(`load ended`)
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
const namespace = await importFn(urlObject.href)
|
|
294
|
+
complete(namespace)
|
|
295
|
+
return result
|
|
296
|
+
} catch (e) {
|
|
297
|
+
fail(e, {
|
|
298
|
+
message: `Error while importing module: ${urlObject.href}`,
|
|
299
|
+
reportedBy: "dynamic_import",
|
|
300
|
+
url: urlObject.href,
|
|
301
|
+
})
|
|
302
|
+
return result
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
executions[src] = { init, execute }
|
|
306
|
+
}
|
|
307
|
+
const prepareJsModuleRemoteExecution = isWebkitOrSafari
|
|
308
|
+
? prepareJsModuleExecutionWithDynamicImport
|
|
309
|
+
: prepareJsModuleExecutionWithScriptThenDynamicImport
|
|
310
|
+
const prepareJsModuleInlineExecution = (src) => {
|
|
311
|
+
const { start, complete, fail } = createExecutionController(
|
|
312
|
+
src,
|
|
313
|
+
"js_module",
|
|
314
|
+
)
|
|
315
|
+
const end = complete
|
|
316
|
+
const error = (e) => {
|
|
317
|
+
// supervision shallowed the error, report back to browser
|
|
318
|
+
reportErrorBackToBrowser(e)
|
|
319
|
+
fail(e)
|
|
320
|
+
}
|
|
321
|
+
executions[src] = { isInline: true, start, end, error }
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
supervisor.setupReportException({
|
|
325
|
+
logs,
|
|
326
|
+
serverIsJsenvDevServer,
|
|
327
|
+
rootDirectoryUrl,
|
|
328
|
+
errorOverlay,
|
|
329
|
+
errorBaseUrl,
|
|
330
|
+
openInEditor,
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
scriptInfos.forEach((scriptInfo) => {
|
|
334
|
+
const { type, src, isInline } = scriptInfo
|
|
335
|
+
if (type === "js_module") {
|
|
336
|
+
if (isInline) {
|
|
337
|
+
prepareJsModuleInlineExecution(src)
|
|
338
|
+
} else {
|
|
339
|
+
prepareJsModuleRemoteExecution(src)
|
|
340
|
+
}
|
|
341
|
+
} else if (isInline) {
|
|
342
|
+
prepareJsClassicInlineExecution(src)
|
|
343
|
+
} else {
|
|
344
|
+
prepareJsClassicRemoteExecution(src)
|
|
345
|
+
}
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
// js classic
|
|
349
|
+
supervisor.jsClassicStart = (inlineSrc) => {
|
|
350
|
+
executions[inlineSrc].start()
|
|
351
|
+
}
|
|
352
|
+
supervisor.jsClassicEnd = (inlineSrc) => {
|
|
353
|
+
executions[inlineSrc].end()
|
|
354
|
+
}
|
|
355
|
+
supervisor.jsClassicError = (inlineSrc, e) => {
|
|
356
|
+
executions[inlineSrc].error(e)
|
|
357
|
+
}
|
|
358
|
+
supervisor.superviseScript = (src) => {
|
|
359
|
+
const execution = executions[asExecutionId(src)]
|
|
360
|
+
execution.init()
|
|
361
|
+
return addToExecutionQueue(execution)
|
|
362
|
+
}
|
|
363
|
+
// js module
|
|
364
|
+
supervisor.jsModuleStart = (inlineSrc) => {
|
|
365
|
+
executions[inlineSrc].start()
|
|
366
|
+
}
|
|
367
|
+
supervisor.jsModuleEnd = (inlineSrc) => {
|
|
368
|
+
executions[inlineSrc].end()
|
|
369
|
+
}
|
|
370
|
+
supervisor.jsModuleError = (inlineSrc, e) => {
|
|
371
|
+
executions[inlineSrc].error(e)
|
|
372
|
+
}
|
|
373
|
+
supervisor.superviseScriptTypeModule = (src, importFn) => {
|
|
374
|
+
const execution = executions[asExecutionId(src)]
|
|
375
|
+
execution.init(importFn)
|
|
376
|
+
return addToExecutionQueue(execution)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
supervisor.reloadSupervisedScript = (src) => {
|
|
380
|
+
const execution = executions[src]
|
|
381
|
+
if (!execution) {
|
|
382
|
+
throw new Error(`no execution for ${src}`)
|
|
383
|
+
}
|
|
384
|
+
if (execution.isInline) {
|
|
385
|
+
throw new Error(`cannot reload inline script ${src}`)
|
|
386
|
+
}
|
|
387
|
+
return execution.execute({ isReload: true })
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
supervisor.getDocumentExecutionResult = async () => {
|
|
391
|
+
await Promise.all(promises)
|
|
392
|
+
return {
|
|
393
|
+
startTime: documentExecutionStartTime,
|
|
394
|
+
endTime: documentExecutionEndTime,
|
|
395
|
+
status: "completed",
|
|
396
|
+
executionResults,
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
const reportErrorBackToBrowser = (error) => {
|
|
401
|
+
if (typeof window.reportError === "function") {
|
|
402
|
+
window.reportError(error)
|
|
403
|
+
} else {
|
|
404
|
+
console.error(error)
|
|
405
|
+
}
|
|
19
406
|
}
|
|
20
407
|
|
|
21
408
|
supervisor.setupReportException = ({
|
|
409
|
+
logs,
|
|
22
410
|
rootDirectoryUrl,
|
|
411
|
+
serverIsJsenvDevServer,
|
|
23
412
|
errorNotification,
|
|
24
413
|
errorOverlay,
|
|
25
414
|
errorBaseUrl,
|
|
@@ -29,17 +418,14 @@ window.__supervisor__ = (() => {
|
|
|
29
418
|
const DYNAMIC_IMPORT_EXPORT_MISSING = "dynamic_import_export_missing"
|
|
30
419
|
const DYNAMIC_IMPORT_SYNTAX_ERROR = "dynamic_import_syntax_error"
|
|
31
420
|
|
|
32
|
-
const createException = (
|
|
33
|
-
reason,
|
|
34
|
-
reportedBy,
|
|
35
|
-
|
|
36
|
-
line,
|
|
37
|
-
column,
|
|
38
|
-
} = {}) => {
|
|
421
|
+
const createException = (
|
|
422
|
+
reason, // can be error, string, object
|
|
423
|
+
{ message, reportedBy, url, line, column } = {},
|
|
424
|
+
) => {
|
|
39
425
|
const exception = {
|
|
40
426
|
reason,
|
|
41
|
-
reportedBy,
|
|
42
427
|
isError: false, // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw
|
|
428
|
+
reportedBy,
|
|
43
429
|
code: null,
|
|
44
430
|
message: null,
|
|
45
431
|
stack: null,
|
|
@@ -91,9 +477,6 @@ window.__supervisor__ = (() => {
|
|
|
91
477
|
if (error.url) {
|
|
92
478
|
Object.assign(exception.site, resolveUrlSite({ url: error.url }))
|
|
93
479
|
}
|
|
94
|
-
if (error.needsReport) {
|
|
95
|
-
exception.needsReport = true
|
|
96
|
-
}
|
|
97
480
|
export_missing: {
|
|
98
481
|
// chrome
|
|
99
482
|
if (message.includes("does not provide an export named")) {
|
|
@@ -113,6 +496,10 @@ window.__supervisor__ = (() => {
|
|
|
113
496
|
exception.code = DYNAMIC_IMPORT_EXPORT_MISSING
|
|
114
497
|
return
|
|
115
498
|
}
|
|
499
|
+
if (message.includes("Importing a module script failed")) {
|
|
500
|
+
exception.code = DYNAMIC_IMPORT_FETCH_ERROR
|
|
501
|
+
return
|
|
502
|
+
}
|
|
116
503
|
}
|
|
117
504
|
js_syntax_error: {
|
|
118
505
|
if (error.name === "SyntaxError" && typeof line === "number") {
|
|
@@ -123,8 +510,9 @@ window.__supervisor__ = (() => {
|
|
|
123
510
|
return
|
|
124
511
|
}
|
|
125
512
|
if (typeof reason === "object") {
|
|
513
|
+
// happens when reason is an Event for instance
|
|
126
514
|
exception.code = reason.code
|
|
127
|
-
exception.message = reason.message
|
|
515
|
+
exception.message = reason.message || message
|
|
128
516
|
exception.stack = reason.stack
|
|
129
517
|
if (reason.reportedBy) {
|
|
130
518
|
exception.reportedBy = reason.reportedBy
|
|
@@ -132,9 +520,6 @@ window.__supervisor__ = (() => {
|
|
|
132
520
|
if (reason.url) {
|
|
133
521
|
Object.assign(exception.site, resolveUrlSite({ url: reason.url }))
|
|
134
522
|
}
|
|
135
|
-
if (reason.needsReport) {
|
|
136
|
-
exception.needsReport = true
|
|
137
|
-
}
|
|
138
523
|
return
|
|
139
524
|
}
|
|
140
525
|
exception.message = JSON.stringify(reason)
|
|
@@ -170,12 +555,7 @@ window.__supervisor__ = (() => {
|
|
|
170
555
|
exception.code === DYNAMIC_IMPORT_SYNTAX_ERROR
|
|
171
556
|
) {
|
|
172
557
|
// syntax error on inline script need line-1 for some reason
|
|
173
|
-
|
|
174
|
-
fileUrlSite.line--
|
|
175
|
-
} else {
|
|
176
|
-
// firefox and safari need line-2
|
|
177
|
-
fileUrlSite.line -= 2
|
|
178
|
-
}
|
|
558
|
+
fileUrlSite.line = fileUrlSite.line - 1
|
|
179
559
|
}
|
|
180
560
|
Object.assign(exception.site, fileUrlSite)
|
|
181
561
|
}
|
|
@@ -216,10 +596,7 @@ window.__supervisor__ = (() => {
|
|
|
216
596
|
const extension = inlineUrlMatch[5]
|
|
217
597
|
url = htmlUrl
|
|
218
598
|
line = tagLineStart + (typeof line === "number" ? line : 0)
|
|
219
|
-
//
|
|
220
|
-
if (Error.captureStackTrace) {
|
|
221
|
-
line--
|
|
222
|
-
}
|
|
599
|
+
line = line - 1 // sauf pour les erreur de syntaxe
|
|
223
600
|
column = tagColumnStart + (typeof column === "number" ? column : 0)
|
|
224
601
|
const fileUrl = resolveFileUrl(url)
|
|
225
602
|
return {
|
|
@@ -255,7 +632,7 @@ window.__supervisor__ = (() => {
|
|
|
255
632
|
}
|
|
256
633
|
|
|
257
634
|
const resolveFileUrl = (url) => {
|
|
258
|
-
let urlObject = new URL(url)
|
|
635
|
+
let urlObject = new URL(url, window.origin)
|
|
259
636
|
if (urlObject.origin === window.origin) {
|
|
260
637
|
urlObject = new URL(
|
|
261
638
|
`${urlObject.pathname.slice(1)}${urlObject.search}`,
|
|
@@ -375,6 +752,9 @@ window.__supervisor__ = (() => {
|
|
|
375
752
|
})
|
|
376
753
|
if (exceptionInfo.site.url) {
|
|
377
754
|
errorParts.errorDetailsPromise = (async () => {
|
|
755
|
+
if (!serverIsJsenvDevServer) {
|
|
756
|
+
return null
|
|
757
|
+
}
|
|
378
758
|
try {
|
|
379
759
|
if (
|
|
380
760
|
exceptionInfo.code === DYNAMIC_IMPORT_FETCH_ERROR ||
|
|
@@ -408,13 +788,7 @@ window.__supervisor__ = (() => {
|
|
|
408
788
|
cause: causeText,
|
|
409
789
|
}
|
|
410
790
|
}
|
|
411
|
-
if (
|
|
412
|
-
exceptionInfo.site.line !== undefined &&
|
|
413
|
-
// code frame showing internal window.reportError is pointless
|
|
414
|
-
!exceptionInfo.site.url.endsWith(
|
|
415
|
-
`script_type_module_supervisor.js`,
|
|
416
|
-
)
|
|
417
|
-
) {
|
|
791
|
+
if (exceptionInfo.site.line !== undefined) {
|
|
418
792
|
const urlToFetch = new URL(
|
|
419
793
|
`/__get_code_frame__/${encodeURIComponent(
|
|
420
794
|
stringifyUrlSite(exceptionInfo.site),
|
|
@@ -494,6 +868,9 @@ window.__supervisor__ = (() => {
|
|
|
494
868
|
let displayJsenvErrorOverlay
|
|
495
869
|
error_overlay: {
|
|
496
870
|
displayJsenvErrorOverlay = (params) => {
|
|
871
|
+
if (logs) {
|
|
872
|
+
console.log("display jsenv error overlay", params)
|
|
873
|
+
}
|
|
497
874
|
let jsenvErrorOverlay = new JsenvErrorOverlay(params)
|
|
498
875
|
document
|
|
499
876
|
.querySelectorAll(JSENV_ERROR_OVERLAY_TAGNAME)
|
|
@@ -684,13 +1061,18 @@ window.__supervisor__ = (() => {
|
|
|
684
1061
|
window.addEventListener("error", (errorEvent) => {
|
|
685
1062
|
if (!errorEvent.isTrusted) {
|
|
686
1063
|
// ignore custom error event (not sent by browser)
|
|
1064
|
+
if (logs) {
|
|
1065
|
+
console.log("ignore non trusted error event", errorEvent)
|
|
1066
|
+
}
|
|
687
1067
|
return
|
|
688
1068
|
}
|
|
1069
|
+
if (logs) {
|
|
1070
|
+
console.log('window "error" event received', errorEvent)
|
|
1071
|
+
}
|
|
689
1072
|
const { error, message, filename, lineno, colno } = errorEvent
|
|
690
|
-
const exception = supervisor.createException({
|
|
1073
|
+
const exception = supervisor.createException(error || message, {
|
|
691
1074
|
// when error is reported within a worker error is null
|
|
692
1075
|
// but there is a message property on errorEvent
|
|
693
|
-
reason: error || message,
|
|
694
1076
|
reportedBy: "window_error_event",
|
|
695
1077
|
url: filename,
|
|
696
1078
|
line: lineno,
|
|
@@ -702,266 +1084,64 @@ window.__supervisor__ = (() => {
|
|
|
702
1084
|
if (event.defaultPrevented) {
|
|
703
1085
|
return
|
|
704
1086
|
}
|
|
705
|
-
const exception = supervisor.createException({
|
|
706
|
-
reason: event.reason,
|
|
1087
|
+
const exception = supervisor.createException(event.reason, {
|
|
707
1088
|
reportedBy: "window_unhandledrejection_event",
|
|
708
1089
|
})
|
|
709
1090
|
supervisor.reportException(exception)
|
|
710
1091
|
})
|
|
711
1092
|
}
|
|
712
1093
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
errorOverlay,
|
|
723
|
-
errorBaseUrl,
|
|
724
|
-
openInEditor,
|
|
1094
|
+
const prepareScriptToLoad = (script) => {
|
|
1095
|
+
// do not use script.cloneNode()
|
|
1096
|
+
// bcause https://stackoverflow.com/questions/28771542/why-dont-clonenode-script-tags-execute
|
|
1097
|
+
const scriptClone = document.createElement("script")
|
|
1098
|
+
// browsers set async by default when creating script(s)
|
|
1099
|
+
// we want an exact copy to preserves how code is executed
|
|
1100
|
+
scriptClone.async = false
|
|
1101
|
+
Array.from(script.attributes).forEach((attribute) => {
|
|
1102
|
+
scriptClone.setAttribute(attribute.nodeName, attribute.nodeValue)
|
|
725
1103
|
})
|
|
1104
|
+
scriptClone.removeAttribute("jsenv-cooked-by")
|
|
1105
|
+
scriptClone.removeAttribute("jsenv-inlined-by")
|
|
1106
|
+
scriptClone.removeAttribute("jsenv-injected-by")
|
|
1107
|
+
scriptClone.removeAttribute("inlined-from-src")
|
|
1108
|
+
scriptClone.removeAttribute("original-position")
|
|
1109
|
+
scriptClone.removeAttribute("original-src-position")
|
|
726
1110
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
// respect execution order
|
|
730
|
-
// - wait for classic scripts to be done (non async)
|
|
731
|
-
// - wait module script previous execution (non async)
|
|
732
|
-
// see https://gist.github.com/jakub-g/385ee6b41085303a53ad92c7c8afd7a6#typemodule-vs-non-module-typetextjavascript-vs-script-nomodule
|
|
733
|
-
const executionQueue = []
|
|
734
|
-
let executing = false
|
|
735
|
-
const addToExecutionQueue = async (execution) => {
|
|
736
|
-
if (execution.async) {
|
|
737
|
-
execution.start()
|
|
738
|
-
return
|
|
739
|
-
}
|
|
740
|
-
if (executing) {
|
|
741
|
-
executionQueue.push(execution)
|
|
742
|
-
return
|
|
743
|
-
}
|
|
744
|
-
startThenDequeue(execution)
|
|
745
|
-
}
|
|
746
|
-
const startThenDequeue = async (execution) => {
|
|
747
|
-
executing = true
|
|
748
|
-
const promise = execution.start()
|
|
749
|
-
await promise
|
|
750
|
-
executing = false
|
|
751
|
-
if (executionQueue.length) {
|
|
752
|
-
const nextExecution = executionQueue.shift()
|
|
753
|
-
startThenDequeue(nextExecution)
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
supervisor.addExecution = ({ type, src, async, execute }) => {
|
|
757
|
-
const execution = {
|
|
758
|
-
type,
|
|
759
|
-
src,
|
|
760
|
-
async,
|
|
761
|
-
execute,
|
|
762
|
-
}
|
|
763
|
-
execution.start = () => {
|
|
764
|
-
return superviseExecution(execution, { isReload: false })
|
|
765
|
-
}
|
|
766
|
-
execution.reload = () => {
|
|
767
|
-
return superviseExecution(execution, { isReload: true })
|
|
768
|
-
}
|
|
769
|
-
supervisedScripts.push(execution)
|
|
770
|
-
return addToExecutionQueue(execution)
|
|
771
|
-
}
|
|
772
|
-
const superviseExecution = async (execution, { isReload }) => {
|
|
773
|
-
if (logs) {
|
|
774
|
-
console.group(`[jsenv] loading ${execution.type} ${execution.src}`)
|
|
775
|
-
}
|
|
776
|
-
const executionResult = {
|
|
777
|
-
status: "pending",
|
|
778
|
-
loadDuration: null,
|
|
779
|
-
executionDuration: null,
|
|
780
|
-
duration: null,
|
|
781
|
-
exception: null,
|
|
782
|
-
namespace: null,
|
|
783
|
-
coverage: null,
|
|
784
|
-
}
|
|
785
|
-
executionResults[execution.src] = executionResult
|
|
786
|
-
|
|
787
|
-
const monitorScriptLoad = () => {
|
|
788
|
-
const loadStartTime = Date.now()
|
|
789
|
-
let resolveScriptLoadPromise
|
|
790
|
-
const scriptLoadPromise = new Promise((resolve) => {
|
|
791
|
-
resolveScriptLoadPromise = resolve
|
|
792
|
-
})
|
|
793
|
-
pendingPromises.push(scriptLoadPromise)
|
|
794
|
-
return () => {
|
|
795
|
-
const loadEndTime = Date.now()
|
|
796
|
-
executionResult.loadDuration = loadEndTime - loadStartTime
|
|
797
|
-
resolveScriptLoadPromise()
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
const monitorScriptExecution = () => {
|
|
801
|
-
const executionStartTime = Date.now()
|
|
802
|
-
let resolveExecutionPromise
|
|
803
|
-
const executionPromise = new Promise((resolve) => {
|
|
804
|
-
resolveExecutionPromise = resolve
|
|
805
|
-
})
|
|
806
|
-
pendingPromises.push(executionPromise)
|
|
807
|
-
return () => {
|
|
808
|
-
executionResult.coverage = window.__coverage__
|
|
809
|
-
executionResult.executionDuration = Date.now() - executionStartTime
|
|
810
|
-
executionResult.duration =
|
|
811
|
-
executionResult.loadDuration + executionResult.executionDuration
|
|
812
|
-
resolveExecutionPromise()
|
|
813
|
-
}
|
|
814
|
-
}
|
|
1111
|
+
return scriptClone
|
|
1112
|
+
}
|
|
815
1113
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
if (
|
|
820
|
-
|
|
1114
|
+
const getScriptLoadPromise = async (script) => {
|
|
1115
|
+
return new Promise((resolve) => {
|
|
1116
|
+
const windowErrorEventCallback = (errorEvent) => {
|
|
1117
|
+
if (errorEvent.filename === script.src) {
|
|
1118
|
+
removeWindowErrorEventCallback()
|
|
1119
|
+
resolve({
|
|
1120
|
+
detectedBy: "window_error_event",
|
|
1121
|
+
failed: true,
|
|
1122
|
+
error: errorEvent,
|
|
1123
|
+
})
|
|
821
1124
|
}
|
|
822
|
-
executionResult.exception = exception
|
|
823
1125
|
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
try {
|
|
827
|
-
const result = await execution.execute({ isReload })
|
|
828
|
-
if (logs) {
|
|
829
|
-
console.log(`${execution.type} load ended`)
|
|
830
|
-
console.groupEnd()
|
|
831
|
-
}
|
|
832
|
-
executionResult.status = "loaded"
|
|
833
|
-
scriptLoadDone()
|
|
834
|
-
|
|
835
|
-
const scriptExecutionDone = monitorScriptExecution()
|
|
836
|
-
if (execution.type === "js_classic") {
|
|
837
|
-
executionResult.status = "completed"
|
|
838
|
-
scriptExecutionDone()
|
|
839
|
-
} else if (execution.type === "js_module") {
|
|
840
|
-
result.executionPromise.then(
|
|
841
|
-
(namespace) => {
|
|
842
|
-
executionResult.status = "completed"
|
|
843
|
-
executionResult.namespace = namespace
|
|
844
|
-
scriptExecutionDone()
|
|
845
|
-
},
|
|
846
|
-
(e) => {
|
|
847
|
-
onError(e)
|
|
848
|
-
scriptExecutionDone()
|
|
849
|
-
},
|
|
850
|
-
)
|
|
851
|
-
}
|
|
852
|
-
} catch (e) {
|
|
853
|
-
if (logs) {
|
|
854
|
-
console.groupEnd()
|
|
855
|
-
}
|
|
856
|
-
onError(e)
|
|
857
|
-
scriptLoadDone()
|
|
1126
|
+
const removeWindowErrorEventCallback = () => {
|
|
1127
|
+
window.removeEventListener("error", windowErrorEventCallback)
|
|
858
1128
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
src,
|
|
868
|
-
type: "js_classic",
|
|
869
|
-
async,
|
|
870
|
-
execute: async ({ isReload }) => {
|
|
871
|
-
const urlObject = new URL(src, window.location)
|
|
872
|
-
const loadPromise = new Promise((resolve, reject) => {
|
|
873
|
-
// do not use script.cloneNode()
|
|
874
|
-
// bcause https://stackoverflow.com/questions/28771542/why-dont-clonenode-script-tags-execute
|
|
875
|
-
currentScriptClone = document.createElement("script")
|
|
876
|
-
// browsers set async by default when creating script(s)
|
|
877
|
-
// we want an exact copy to preserves how code is executed
|
|
878
|
-
currentScriptClone.async = false
|
|
879
|
-
Array.from(currentScript.attributes).forEach((attribute) => {
|
|
880
|
-
currentScriptClone.setAttribute(
|
|
881
|
-
attribute.nodeName,
|
|
882
|
-
attribute.nodeValue,
|
|
883
|
-
)
|
|
884
|
-
})
|
|
885
|
-
if (isReload) {
|
|
886
|
-
urlObject.searchParams.set("hmr", Date.now())
|
|
887
|
-
nodeToReplace = currentScriptClone
|
|
888
|
-
currentScriptClone.src = urlObject.href
|
|
889
|
-
} else {
|
|
890
|
-
currentScriptClone.removeAttribute("jsenv-cooked-by")
|
|
891
|
-
currentScriptClone.removeAttribute("jsenv-inlined-by")
|
|
892
|
-
currentScriptClone.removeAttribute("jsenv-injected-by")
|
|
893
|
-
currentScriptClone.removeAttribute("inlined-from-src")
|
|
894
|
-
currentScriptClone.removeAttribute("original-position")
|
|
895
|
-
currentScriptClone.removeAttribute("original-src-position")
|
|
896
|
-
nodeToReplace = currentScript
|
|
897
|
-
currentScriptClone.src = src
|
|
898
|
-
}
|
|
899
|
-
currentScriptClone.addEventListener("error", reject)
|
|
900
|
-
currentScriptClone.addEventListener("load", resolve)
|
|
901
|
-
parentNode.replaceChild(currentScriptClone, nodeToReplace)
|
|
902
|
-
})
|
|
903
|
-
try {
|
|
904
|
-
await loadPromise
|
|
905
|
-
} catch (e) {
|
|
906
|
-
// eslint-disable-next-line no-throw-literal
|
|
907
|
-
throw {
|
|
908
|
-
message: `Failed to fetch script: ${urlObject.href}`,
|
|
909
|
-
reportedBy: "script_error_event",
|
|
910
|
-
url: urlObject.href,
|
|
911
|
-
// window.error won't be dispatched for this error
|
|
912
|
-
needsReport: true,
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
},
|
|
1129
|
+
window.addEventListener("error", windowErrorEventCallback)
|
|
1130
|
+
script.addEventListener("error", (errorEvent) => {
|
|
1131
|
+
removeWindowErrorEventCallback()
|
|
1132
|
+
resolve({
|
|
1133
|
+
detectedBy: "script_error_event",
|
|
1134
|
+
failed: true,
|
|
1135
|
+
error: errorEvent,
|
|
1136
|
+
})
|
|
916
1137
|
})
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
return false
|
|
923
|
-
}
|
|
924
|
-
return supervisedScriptCandidate.src === src
|
|
925
|
-
},
|
|
926
|
-
)
|
|
927
|
-
if (supervisedScript) {
|
|
928
|
-
supervisedScript.reload()
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
supervisor.getDocumentExecutionResult = async () => {
|
|
932
|
-
// just to be super safe and ensure any <script type="module"> got a chance to execute
|
|
933
|
-
const documentReadyPromise = new Promise((resolve) => {
|
|
934
|
-
if (document.readyState === "complete") {
|
|
935
|
-
resolve()
|
|
936
|
-
return
|
|
937
|
-
}
|
|
938
|
-
const loadCallback = () => {
|
|
939
|
-
window.removeEventListener("load", loadCallback)
|
|
940
|
-
resolve()
|
|
941
|
-
}
|
|
942
|
-
window.addEventListener("load", loadCallback)
|
|
1138
|
+
script.addEventListener("load", () => {
|
|
1139
|
+
removeWindowErrorEventCallback()
|
|
1140
|
+
resolve({
|
|
1141
|
+
detectedBy: "script_load_event",
|
|
1142
|
+
})
|
|
943
1143
|
})
|
|
944
|
-
|
|
945
|
-
const waitScriptExecutions = async () => {
|
|
946
|
-
const numberOfPromises = pendingPromises.length
|
|
947
|
-
await Promise.all(pendingPromises)
|
|
948
|
-
// new scripts added while the other where executing
|
|
949
|
-
// (should happen only on webkit where
|
|
950
|
-
// script might be added after window load event)
|
|
951
|
-
await new Promise((resolve) => setTimeout(resolve))
|
|
952
|
-
if (pendingPromises.length > numberOfPromises) {
|
|
953
|
-
await waitScriptExecutions()
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
await waitScriptExecutions()
|
|
957
|
-
|
|
958
|
-
return {
|
|
959
|
-
status: "completed",
|
|
960
|
-
executionResults,
|
|
961
|
-
startTime: navigationStartTime,
|
|
962
|
-
endTime: Date.now(),
|
|
963
|
-
}
|
|
964
|
-
}
|
|
1144
|
+
})
|
|
965
1145
|
}
|
|
966
1146
|
|
|
967
1147
|
return supervisor
|