@jsenv/core 33.0.1 → 34.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/js/autoreload.js +1 -4
- package/dist/js/supervisor.js +498 -290
- package/dist/jsenv.js +874 -347
- 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 +281 -0
- package/src/plugins/supervisor/jsenv_plugin_supervisor.js +48 -233
- 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 +16 -61
- package/src/test/logs_file_execution.js +74 -28
- package/dist/js/script_type_module_supervisor.js +0 -109
- package/src/plugins/supervisor/client/script_type_module_supervisor.js +0 -98
package/dist/js/supervisor.js
CHANGED
|
@@ -2,22 +2,441 @@ 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
|
-
getDocumentExecutionResult: notImplemented
|
|
11
|
-
executionResults
|
|
10
|
+
getDocumentExecutionResult: notImplemented
|
|
12
11
|
};
|
|
13
|
-
|
|
12
|
+
const executionResults = {};
|
|
13
|
+
let documentExecutionStartTime;
|
|
14
14
|
try {
|
|
15
|
-
|
|
15
|
+
documentExecutionStartTime = window.performance.timing.navigationStart;
|
|
16
16
|
} catch (e) {
|
|
17
|
-
|
|
17
|
+
documentExecutionStartTime = Date.now();
|
|
18
18
|
}
|
|
19
|
+
let documentExecutionEndTime;
|
|
20
|
+
supervisor.setup = ({
|
|
21
|
+
rootDirectoryUrl,
|
|
22
|
+
scriptInfos,
|
|
23
|
+
serverIsJsenvDevServer,
|
|
24
|
+
logs,
|
|
25
|
+
errorOverlay,
|
|
26
|
+
errorBaseUrl,
|
|
27
|
+
openInEditor
|
|
28
|
+
}) => {
|
|
29
|
+
const executions = {};
|
|
30
|
+
const promises = [];
|
|
31
|
+
let remainingScriptCount = scriptInfos.length;
|
|
32
|
+
|
|
33
|
+
// respect execution order
|
|
34
|
+
// - wait for classic scripts to be done (non async)
|
|
35
|
+
// - wait module script previous execution (non async)
|
|
36
|
+
// see https://gist.github.com/jakub-g/385ee6b41085303a53ad92c7c8afd7a6#typemodule-vs-non-module-typetextjavascript-vs-script-nomodule
|
|
37
|
+
const executionQueue = [];
|
|
38
|
+
let executing = false;
|
|
39
|
+
const addToExecutionQueue = async execution => {
|
|
40
|
+
if (execution.async) {
|
|
41
|
+
execution.execute();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (executing) {
|
|
45
|
+
executionQueue.push(execution);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
execThenDequeue(execution);
|
|
49
|
+
};
|
|
50
|
+
const execThenDequeue = async execution => {
|
|
51
|
+
executing = true;
|
|
52
|
+
// start next js module execution as soon as current js module starts to execute
|
|
53
|
+
// (do not wait in case of top level await)
|
|
54
|
+
let resolveExecutingPromise;
|
|
55
|
+
const executingPromise = new Promise(resolve => {
|
|
56
|
+
resolveExecutingPromise = resolve;
|
|
57
|
+
});
|
|
58
|
+
const promise = execution.execute({
|
|
59
|
+
onExecuting: () => resolveExecutingPromise()
|
|
60
|
+
});
|
|
61
|
+
await Promise.race([promise, executingPromise]);
|
|
62
|
+
executing = false;
|
|
63
|
+
if (executionQueue.length) {
|
|
64
|
+
const nextExecution = executionQueue.shift();
|
|
65
|
+
execThenDequeue(nextExecution);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const asExecutionId = src => {
|
|
69
|
+
const url = new URL(src, window.location).href;
|
|
70
|
+
if (url.startsWith(window.location.origin)) {
|
|
71
|
+
return src;
|
|
72
|
+
}
|
|
73
|
+
return url;
|
|
74
|
+
};
|
|
75
|
+
const createExecutionController = (src, type) => {
|
|
76
|
+
const result = {
|
|
77
|
+
status: "pending",
|
|
78
|
+
duration: null,
|
|
79
|
+
coverage: null,
|
|
80
|
+
exception: null,
|
|
81
|
+
value: null
|
|
82
|
+
};
|
|
83
|
+
let resolve;
|
|
84
|
+
const promise = new Promise(_resolve => {
|
|
85
|
+
resolve = _resolve;
|
|
86
|
+
});
|
|
87
|
+
promises.push(promise);
|
|
88
|
+
executionResults[src] = result;
|
|
89
|
+
const start = () => {
|
|
90
|
+
result.duration = null;
|
|
91
|
+
result.coverage = null;
|
|
92
|
+
result.status = "started";
|
|
93
|
+
result.exception = null;
|
|
94
|
+
if (logs) {
|
|
95
|
+
console.group(`[jsenv] ${src} execution started (${type})`);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const end = () => {
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
remainingScriptCount--;
|
|
101
|
+
result.duration = now - documentExecutionStartTime;
|
|
102
|
+
result.coverage = window.__coverage__;
|
|
103
|
+
if (logs) {
|
|
104
|
+
console.log(`execution ${result.status}`);
|
|
105
|
+
console.groupEnd();
|
|
106
|
+
}
|
|
107
|
+
if (remainingScriptCount === 0) {
|
|
108
|
+
documentExecutionEndTime = now;
|
|
109
|
+
}
|
|
110
|
+
resolve();
|
|
111
|
+
};
|
|
112
|
+
const complete = () => {
|
|
113
|
+
result.status = "completed";
|
|
114
|
+
end();
|
|
115
|
+
};
|
|
116
|
+
const fail = (error, info) => {
|
|
117
|
+
result.status = "failed";
|
|
118
|
+
const exception = supervisor.createException(error, info);
|
|
119
|
+
result.exception = exception;
|
|
120
|
+
end();
|
|
121
|
+
};
|
|
122
|
+
return {
|
|
123
|
+
result,
|
|
124
|
+
start,
|
|
125
|
+
complete,
|
|
126
|
+
fail
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
const prepareJsClassicRemoteExecution = src => {
|
|
130
|
+
const urlObject = new URL(src, window.location);
|
|
131
|
+
const url = urlObject.href;
|
|
132
|
+
const {
|
|
133
|
+
result,
|
|
134
|
+
start,
|
|
135
|
+
complete,
|
|
136
|
+
fail
|
|
137
|
+
} = createExecutionController(src, "js_classic");
|
|
138
|
+
let parentNode;
|
|
139
|
+
let currentScript;
|
|
140
|
+
let nodeToReplace;
|
|
141
|
+
let currentScriptClone;
|
|
142
|
+
const init = () => {
|
|
143
|
+
currentScript = document.currentScript;
|
|
144
|
+
parentNode = currentScript.parentNode;
|
|
145
|
+
executions[src].async = currentScript.async;
|
|
146
|
+
};
|
|
147
|
+
const execute = async ({
|
|
148
|
+
isReload
|
|
149
|
+
} = {}) => {
|
|
150
|
+
start();
|
|
151
|
+
currentScriptClone = prepareScriptToLoad(currentScript);
|
|
152
|
+
if (isReload) {
|
|
153
|
+
urlObject.searchParams.set("hmr", Date.now());
|
|
154
|
+
nodeToReplace = currentScriptClone;
|
|
155
|
+
currentScriptClone.src = urlObject.href;
|
|
156
|
+
} else {
|
|
157
|
+
nodeToReplace = currentScript;
|
|
158
|
+
currentScriptClone.src = url;
|
|
159
|
+
}
|
|
160
|
+
const scriptLoadPromise = getScriptLoadPromise(currentScriptClone);
|
|
161
|
+
parentNode.replaceChild(currentScriptClone, nodeToReplace);
|
|
162
|
+
const {
|
|
163
|
+
detectedBy,
|
|
164
|
+
failed,
|
|
165
|
+
error
|
|
166
|
+
} = await scriptLoadPromise;
|
|
167
|
+
if (failed) {
|
|
168
|
+
if (detectedBy === "script_error_event") {
|
|
169
|
+
// window.error won't be dispatched for this error
|
|
170
|
+
reportErrorBackToBrowser(error);
|
|
171
|
+
}
|
|
172
|
+
fail(error, {
|
|
173
|
+
message: `Error while loading script: ${urlObject.href}`,
|
|
174
|
+
reportedBy: "script_error_event",
|
|
175
|
+
url: urlObject.href
|
|
176
|
+
});
|
|
177
|
+
if (detectedBy === "script_error_event") {
|
|
178
|
+
supervisor.reportException(result.exception);
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
complete();
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
};
|
|
185
|
+
executions[src] = {
|
|
186
|
+
init,
|
|
187
|
+
execute
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
const prepareJsClassicInlineExecution = src => {
|
|
191
|
+
const {
|
|
192
|
+
start,
|
|
193
|
+
complete,
|
|
194
|
+
fail
|
|
195
|
+
} = createExecutionController(src, "js_classic");
|
|
196
|
+
const end = complete;
|
|
197
|
+
const error = e => {
|
|
198
|
+
reportErrorBackToBrowser(e); // supervision shallowed the error, report back to browser
|
|
199
|
+
fail(e);
|
|
200
|
+
};
|
|
201
|
+
executions[src] = {
|
|
202
|
+
isInline: true,
|
|
203
|
+
start,
|
|
204
|
+
end,
|
|
205
|
+
error
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
const isWebkitOrSafari = typeof window.webkitConvertPointFromNodeToPage === "function";
|
|
209
|
+
// https://twitter.com/damienmaillard/status/1554752482273787906
|
|
210
|
+
const prepareJsModuleExecutionWithDynamicImport = src => {
|
|
211
|
+
const urlObject = new URL(src, window.location);
|
|
212
|
+
const {
|
|
213
|
+
result,
|
|
214
|
+
start,
|
|
215
|
+
complete,
|
|
216
|
+
fail
|
|
217
|
+
} = createExecutionController(src, "js_classic");
|
|
218
|
+
let importFn;
|
|
219
|
+
let currentScript;
|
|
220
|
+
const init = _importFn => {
|
|
221
|
+
importFn = _importFn;
|
|
222
|
+
currentScript = document.querySelector(`script[type="module"][inlined-from-src="${src}"]`);
|
|
223
|
+
executions[src].async = currentScript.async;
|
|
224
|
+
};
|
|
225
|
+
const execute = async ({
|
|
226
|
+
isReload
|
|
227
|
+
} = {}) => {
|
|
228
|
+
start();
|
|
229
|
+
if (isReload) {
|
|
230
|
+
urlObject.searchParams.set("hmr", Date.now());
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const namespace = await importFn(urlObject.href);
|
|
234
|
+
complete(namespace);
|
|
235
|
+
return result;
|
|
236
|
+
} catch (e) {
|
|
237
|
+
fail(e, {
|
|
238
|
+
message: `Error while importing module: ${urlObject.href}`,
|
|
239
|
+
reportedBy: "dynamic_import",
|
|
240
|
+
url: urlObject.href
|
|
241
|
+
});
|
|
242
|
+
if (isWebkitOrSafari) {
|
|
243
|
+
supervisor.reportException(result.exception);
|
|
244
|
+
}
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
executions[src] = {
|
|
249
|
+
init,
|
|
250
|
+
execute
|
|
251
|
+
};
|
|
252
|
+
};
|
|
253
|
+
const prepareJsModuleExecutionWithScriptThenDynamicImport = src => {
|
|
254
|
+
const urlObject = new URL(src, window.location);
|
|
255
|
+
const {
|
|
256
|
+
result,
|
|
257
|
+
start,
|
|
258
|
+
complete,
|
|
259
|
+
fail
|
|
260
|
+
} = createExecutionController(src, "js_module");
|
|
261
|
+
let importFn;
|
|
262
|
+
let currentScript;
|
|
263
|
+
let parentNode;
|
|
264
|
+
let nodeToReplace;
|
|
265
|
+
let currentScriptClone;
|
|
266
|
+
const init = _importFn => {
|
|
267
|
+
importFn = _importFn;
|
|
268
|
+
currentScript = document.querySelector(`script[type="module"][inlined-from-src="${src}"]`);
|
|
269
|
+
parentNode = currentScript.parentNode;
|
|
270
|
+
executions[src].async = currentScript.async;
|
|
271
|
+
};
|
|
272
|
+
const execute = async ({
|
|
273
|
+
isReload,
|
|
274
|
+
onExecuting = () => {}
|
|
275
|
+
} = {}) => {
|
|
276
|
+
start();
|
|
277
|
+
currentScriptClone = prepareScriptToLoad(currentScript);
|
|
278
|
+
if (isReload) {
|
|
279
|
+
urlObject.searchParams.set("hmr", Date.now());
|
|
280
|
+
nodeToReplace = currentScriptClone;
|
|
281
|
+
currentScriptClone.src = urlObject.href;
|
|
282
|
+
} else {
|
|
283
|
+
nodeToReplace = currentScript;
|
|
284
|
+
currentScriptClone.src = urlObject.href;
|
|
285
|
+
}
|
|
286
|
+
const scriptLoadResultPromise = getScriptLoadPromise(currentScriptClone);
|
|
287
|
+
parentNode.replaceChild(currentScriptClone, nodeToReplace);
|
|
288
|
+
const {
|
|
289
|
+
detectedBy,
|
|
290
|
+
failed,
|
|
291
|
+
error
|
|
292
|
+
} = await scriptLoadResultPromise;
|
|
293
|
+
if (failed) {
|
|
294
|
+
// if (detectedBy === "script_error_event") {
|
|
295
|
+
// reportErrorBackToBrowser(error)
|
|
296
|
+
// }
|
|
297
|
+
fail(error, {
|
|
298
|
+
message: `Error while loading module: ${urlObject.href}`,
|
|
299
|
+
reportedBy: "script_error_event",
|
|
300
|
+
url: urlObject.href
|
|
301
|
+
});
|
|
302
|
+
if (detectedBy === "script_error_event") {
|
|
303
|
+
supervisor.reportException(result.exception);
|
|
304
|
+
}
|
|
305
|
+
return result;
|
|
306
|
+
}
|
|
307
|
+
onExecuting();
|
|
308
|
+
result.status = "executing";
|
|
309
|
+
if (logs) {
|
|
310
|
+
console.log(`load ended`);
|
|
311
|
+
}
|
|
312
|
+
try {
|
|
313
|
+
const namespace = await importFn(urlObject.href);
|
|
314
|
+
complete(namespace);
|
|
315
|
+
return result;
|
|
316
|
+
} catch (e) {
|
|
317
|
+
fail(e, {
|
|
318
|
+
message: `Error while importing module: ${urlObject.href}`,
|
|
319
|
+
reportedBy: "dynamic_import",
|
|
320
|
+
url: urlObject.href
|
|
321
|
+
});
|
|
322
|
+
return result;
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
executions[src] = {
|
|
326
|
+
init,
|
|
327
|
+
execute
|
|
328
|
+
};
|
|
329
|
+
};
|
|
330
|
+
const prepareJsModuleRemoteExecution = isWebkitOrSafari ? prepareJsModuleExecutionWithDynamicImport : prepareJsModuleExecutionWithScriptThenDynamicImport;
|
|
331
|
+
const prepareJsModuleInlineExecution = src => {
|
|
332
|
+
const {
|
|
333
|
+
start,
|
|
334
|
+
complete,
|
|
335
|
+
fail
|
|
336
|
+
} = createExecutionController(src, "js_module");
|
|
337
|
+
const end = complete;
|
|
338
|
+
const error = e => {
|
|
339
|
+
// supervision shallowed the error, report back to browser
|
|
340
|
+
reportErrorBackToBrowser(e);
|
|
341
|
+
fail(e);
|
|
342
|
+
};
|
|
343
|
+
executions[src] = {
|
|
344
|
+
isInline: true,
|
|
345
|
+
start,
|
|
346
|
+
end,
|
|
347
|
+
error
|
|
348
|
+
};
|
|
349
|
+
};
|
|
350
|
+
supervisor.setupReportException({
|
|
351
|
+
logs,
|
|
352
|
+
serverIsJsenvDevServer,
|
|
353
|
+
rootDirectoryUrl,
|
|
354
|
+
errorOverlay,
|
|
355
|
+
errorBaseUrl,
|
|
356
|
+
openInEditor
|
|
357
|
+
});
|
|
358
|
+
scriptInfos.forEach(scriptInfo => {
|
|
359
|
+
const {
|
|
360
|
+
type,
|
|
361
|
+
src,
|
|
362
|
+
isInline
|
|
363
|
+
} = scriptInfo;
|
|
364
|
+
if (type === "js_module") {
|
|
365
|
+
if (isInline) {
|
|
366
|
+
prepareJsModuleInlineExecution(src);
|
|
367
|
+
} else {
|
|
368
|
+
prepareJsModuleRemoteExecution(src);
|
|
369
|
+
}
|
|
370
|
+
} else if (isInline) {
|
|
371
|
+
prepareJsClassicInlineExecution(src);
|
|
372
|
+
} else {
|
|
373
|
+
prepareJsClassicRemoteExecution(src);
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// js classic
|
|
378
|
+
supervisor.jsClassicStart = inlineSrc => {
|
|
379
|
+
executions[inlineSrc].start();
|
|
380
|
+
};
|
|
381
|
+
supervisor.jsClassicEnd = inlineSrc => {
|
|
382
|
+
executions[inlineSrc].end();
|
|
383
|
+
};
|
|
384
|
+
supervisor.jsClassicError = (inlineSrc, e) => {
|
|
385
|
+
executions[inlineSrc].error(e);
|
|
386
|
+
};
|
|
387
|
+
supervisor.superviseScript = src => {
|
|
388
|
+
const execution = executions[asExecutionId(src)];
|
|
389
|
+
execution.init();
|
|
390
|
+
return addToExecutionQueue(execution);
|
|
391
|
+
};
|
|
392
|
+
// js module
|
|
393
|
+
supervisor.jsModuleStart = inlineSrc => {
|
|
394
|
+
executions[inlineSrc].start();
|
|
395
|
+
};
|
|
396
|
+
supervisor.jsModuleEnd = inlineSrc => {
|
|
397
|
+
executions[inlineSrc].end();
|
|
398
|
+
};
|
|
399
|
+
supervisor.jsModuleError = (inlineSrc, e) => {
|
|
400
|
+
executions[inlineSrc].error(e);
|
|
401
|
+
};
|
|
402
|
+
supervisor.superviseScriptTypeModule = (src, importFn) => {
|
|
403
|
+
const execution = executions[asExecutionId(src)];
|
|
404
|
+
execution.init(importFn);
|
|
405
|
+
return addToExecutionQueue(execution);
|
|
406
|
+
};
|
|
407
|
+
supervisor.reloadSupervisedScript = src => {
|
|
408
|
+
const execution = executions[src];
|
|
409
|
+
if (!execution) {
|
|
410
|
+
throw new Error(`no execution for ${src}`);
|
|
411
|
+
}
|
|
412
|
+
if (execution.isInline) {
|
|
413
|
+
throw new Error(`cannot reload inline script ${src}`);
|
|
414
|
+
}
|
|
415
|
+
return execution.execute({
|
|
416
|
+
isReload: true
|
|
417
|
+
});
|
|
418
|
+
};
|
|
419
|
+
supervisor.getDocumentExecutionResult = async () => {
|
|
420
|
+
await Promise.all(promises);
|
|
421
|
+
return {
|
|
422
|
+
startTime: documentExecutionStartTime,
|
|
423
|
+
endTime: documentExecutionEndTime,
|
|
424
|
+
status: "completed",
|
|
425
|
+
executionResults
|
|
426
|
+
};
|
|
427
|
+
};
|
|
428
|
+
};
|
|
429
|
+
const reportErrorBackToBrowser = error => {
|
|
430
|
+
if (typeof window.reportError === "function") {
|
|
431
|
+
window.reportError(error);
|
|
432
|
+
} else {
|
|
433
|
+
console.error(error);
|
|
434
|
+
}
|
|
435
|
+
};
|
|
19
436
|
supervisor.setupReportException = ({
|
|
437
|
+
logs,
|
|
20
438
|
rootDirectoryUrl,
|
|
439
|
+
serverIsJsenvDevServer,
|
|
21
440
|
errorNotification,
|
|
22
441
|
errorOverlay,
|
|
23
442
|
errorBaseUrl,
|
|
@@ -26,8 +445,10 @@ window.__supervisor__ = (() => {
|
|
|
26
445
|
const DYNAMIC_IMPORT_FETCH_ERROR = "dynamic_import_fetch_error";
|
|
27
446
|
const DYNAMIC_IMPORT_EXPORT_MISSING = "dynamic_import_export_missing";
|
|
28
447
|
const DYNAMIC_IMPORT_SYNTAX_ERROR = "dynamic_import_syntax_error";
|
|
29
|
-
const createException = (
|
|
30
|
-
|
|
448
|
+
const createException = (reason,
|
|
449
|
+
// can be error, string, object
|
|
450
|
+
{
|
|
451
|
+
message,
|
|
31
452
|
reportedBy,
|
|
32
453
|
url,
|
|
33
454
|
line,
|
|
@@ -35,9 +456,9 @@ window.__supervisor__ = (() => {
|
|
|
35
456
|
} = {}) => {
|
|
36
457
|
const exception = {
|
|
37
458
|
reason,
|
|
38
|
-
reportedBy,
|
|
39
459
|
isError: false,
|
|
40
460
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw
|
|
461
|
+
reportedBy,
|
|
41
462
|
code: null,
|
|
42
463
|
message: null,
|
|
43
464
|
stack: null,
|
|
@@ -90,9 +511,6 @@ window.__supervisor__ = (() => {
|
|
|
90
511
|
url: error.url
|
|
91
512
|
}));
|
|
92
513
|
}
|
|
93
|
-
if (error.needsReport) {
|
|
94
|
-
exception.needsReport = true;
|
|
95
|
-
}
|
|
96
514
|
{
|
|
97
515
|
// chrome
|
|
98
516
|
if (message.includes("does not provide an export named")) {
|
|
@@ -109,6 +527,10 @@ window.__supervisor__ = (() => {
|
|
|
109
527
|
exception.code = DYNAMIC_IMPORT_EXPORT_MISSING;
|
|
110
528
|
return;
|
|
111
529
|
}
|
|
530
|
+
if (message.includes("Importing a module script failed")) {
|
|
531
|
+
exception.code = DYNAMIC_IMPORT_FETCH_ERROR;
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
112
534
|
}
|
|
113
535
|
{
|
|
114
536
|
if (error.name === "SyntaxError" && typeof line === "number") {
|
|
@@ -119,8 +541,9 @@ window.__supervisor__ = (() => {
|
|
|
119
541
|
return;
|
|
120
542
|
}
|
|
121
543
|
if (typeof reason === "object") {
|
|
544
|
+
// happens when reason is an Event for instance
|
|
122
545
|
exception.code = reason.code;
|
|
123
|
-
exception.message = reason.message;
|
|
546
|
+
exception.message = reason.message || message;
|
|
124
547
|
exception.stack = reason.stack;
|
|
125
548
|
if (reason.reportedBy) {
|
|
126
549
|
exception.reportedBy = reason.reportedBy;
|
|
@@ -130,9 +553,6 @@ window.__supervisor__ = (() => {
|
|
|
130
553
|
url: reason.url
|
|
131
554
|
}));
|
|
132
555
|
}
|
|
133
|
-
if (reason.needsReport) {
|
|
134
|
-
exception.needsReport = true;
|
|
135
|
-
}
|
|
136
556
|
return;
|
|
137
557
|
}
|
|
138
558
|
exception.message = JSON.stringify(reason);
|
|
@@ -166,12 +586,7 @@ window.__supervisor__ = (() => {
|
|
|
166
586
|
});
|
|
167
587
|
if (fileUrlSite.isInline && exception.code === DYNAMIC_IMPORT_SYNTAX_ERROR) {
|
|
168
588
|
// syntax error on inline script need line-1 for some reason
|
|
169
|
-
|
|
170
|
-
fileUrlSite.line--;
|
|
171
|
-
} else {
|
|
172
|
-
// firefox and safari need line-2
|
|
173
|
-
fileUrlSite.line -= 2;
|
|
174
|
-
}
|
|
589
|
+
fileUrlSite.line = fileUrlSite.line - 1;
|
|
175
590
|
}
|
|
176
591
|
Object.assign(exception.site, fileUrlSite);
|
|
177
592
|
}
|
|
@@ -218,10 +633,7 @@ window.__supervisor__ = (() => {
|
|
|
218
633
|
const extension = inlineUrlMatch[5];
|
|
219
634
|
url = htmlUrl;
|
|
220
635
|
line = tagLineStart + (typeof line === "number" ? line : 0);
|
|
221
|
-
//
|
|
222
|
-
if (Error.captureStackTrace) {
|
|
223
|
-
line--;
|
|
224
|
-
}
|
|
636
|
+
line = line - 1; // sauf pour les erreur de syntaxe
|
|
225
637
|
column = tagColumnStart + (typeof column === "number" ? column : 0);
|
|
226
638
|
const fileUrl = resolveFileUrl(url);
|
|
227
639
|
return {
|
|
@@ -255,7 +667,7 @@ window.__supervisor__ = (() => {
|
|
|
255
667
|
return stack;
|
|
256
668
|
};
|
|
257
669
|
const resolveFileUrl = url => {
|
|
258
|
-
let urlObject = new URL(url);
|
|
670
|
+
let urlObject = new URL(url, window.origin);
|
|
259
671
|
if (urlObject.origin === window.origin) {
|
|
260
672
|
urlObject = new URL(`${urlObject.pathname.slice(1)}${urlObject.search}`, rootDirectoryUrl);
|
|
261
673
|
}
|
|
@@ -365,6 +777,9 @@ window.__supervisor__ = (() => {
|
|
|
365
777
|
});
|
|
366
778
|
if (exceptionInfo.site.url) {
|
|
367
779
|
errorParts.errorDetailsPromise = (async () => {
|
|
780
|
+
if (!serverIsJsenvDevServer) {
|
|
781
|
+
return null;
|
|
782
|
+
}
|
|
368
783
|
try {
|
|
369
784
|
if (exceptionInfo.code === DYNAMIC_IMPORT_FETCH_ERROR || exceptionInfo.reportedBy === "script_error_event") {
|
|
370
785
|
const response = await window.fetch(`/__get_error_cause__/${encodeURIComponent(exceptionInfo.site.isInline ? exceptionInfo.site.originalUrl : exceptionInfo.site.url)}`);
|
|
@@ -386,9 +801,7 @@ window.__supervisor__ = (() => {
|
|
|
386
801
|
cause: causeText
|
|
387
802
|
};
|
|
388
803
|
}
|
|
389
|
-
if (exceptionInfo.site.line !== undefined
|
|
390
|
-
// code frame showing internal window.reportError is pointless
|
|
391
|
-
!exceptionInfo.site.url.endsWith(`script_type_module_supervisor.js`)) {
|
|
804
|
+
if (exceptionInfo.site.line !== undefined) {
|
|
392
805
|
const urlToFetch = new URL(`/__get_code_frame__/${encodeURIComponent(stringifyUrlSite(exceptionInfo.site))}`, window.origin);
|
|
393
806
|
if (!exceptionInfo.stackSourcemapped) {
|
|
394
807
|
urlToFetch.searchParams.set("remap", "");
|
|
@@ -474,6 +887,9 @@ window.__supervisor__ = (() => {
|
|
|
474
887
|
let displayJsenvErrorOverlay;
|
|
475
888
|
{
|
|
476
889
|
displayJsenvErrorOverlay = params => {
|
|
890
|
+
if (logs) {
|
|
891
|
+
console.log("display jsenv error overlay", params);
|
|
892
|
+
}
|
|
477
893
|
let jsenvErrorOverlay = new JsenvErrorOverlay(params);
|
|
478
894
|
document.querySelectorAll(JSENV_ERROR_OVERLAY_TAGNAME).forEach(node => {
|
|
479
895
|
node.parentNode.removeChild(node);
|
|
@@ -670,8 +1086,14 @@ window.__supervisor__ = (() => {
|
|
|
670
1086
|
window.addEventListener("error", errorEvent => {
|
|
671
1087
|
if (!errorEvent.isTrusted) {
|
|
672
1088
|
// ignore custom error event (not sent by browser)
|
|
1089
|
+
if (logs) {
|
|
1090
|
+
console.log("ignore non trusted error event", errorEvent);
|
|
1091
|
+
}
|
|
673
1092
|
return;
|
|
674
1093
|
}
|
|
1094
|
+
if (logs) {
|
|
1095
|
+
console.log('window "error" event received', errorEvent);
|
|
1096
|
+
}
|
|
675
1097
|
const {
|
|
676
1098
|
error,
|
|
677
1099
|
message,
|
|
@@ -679,10 +1101,9 @@ window.__supervisor__ = (() => {
|
|
|
679
1101
|
lineno,
|
|
680
1102
|
colno
|
|
681
1103
|
} = errorEvent;
|
|
682
|
-
const exception = supervisor.createException({
|
|
1104
|
+
const exception = supervisor.createException(error || message, {
|
|
683
1105
|
// when error is reported within a worker error is null
|
|
684
1106
|
// but there is a message property on errorEvent
|
|
685
|
-
reason: error || message,
|
|
686
1107
|
reportedBy: "window_error_event",
|
|
687
1108
|
url: filename,
|
|
688
1109
|
line: lineno,
|
|
@@ -694,274 +1115,61 @@ window.__supervisor__ = (() => {
|
|
|
694
1115
|
if (event.defaultPrevented) {
|
|
695
1116
|
return;
|
|
696
1117
|
}
|
|
697
|
-
const exception = supervisor.createException({
|
|
698
|
-
reason: event.reason,
|
|
1118
|
+
const exception = supervisor.createException(event.reason, {
|
|
699
1119
|
reportedBy: "window_unhandledrejection_event"
|
|
700
1120
|
});
|
|
701
1121
|
supervisor.reportException(exception);
|
|
702
1122
|
});
|
|
703
1123
|
};
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
errorOverlay,
|
|
714
|
-
errorBaseUrl,
|
|
715
|
-
openInEditor
|
|
1124
|
+
const prepareScriptToLoad = script => {
|
|
1125
|
+
// do not use script.cloneNode()
|
|
1126
|
+
// bcause https://stackoverflow.com/questions/28771542/why-dont-clonenode-script-tags-execute
|
|
1127
|
+
const scriptClone = document.createElement("script");
|
|
1128
|
+
// browsers set async by default when creating script(s)
|
|
1129
|
+
// we want an exact copy to preserves how code is executed
|
|
1130
|
+
scriptClone.async = false;
|
|
1131
|
+
Array.from(script.attributes).forEach(attribute => {
|
|
1132
|
+
scriptClone.setAttribute(attribute.nodeName, attribute.nodeValue);
|
|
716
1133
|
});
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
};
|
|
736
|
-
const startThenDequeue = async execution => {
|
|
737
|
-
executing = true;
|
|
738
|
-
const promise = execution.start();
|
|
739
|
-
await promise;
|
|
740
|
-
executing = false;
|
|
741
|
-
if (executionQueue.length) {
|
|
742
|
-
const nextExecution = executionQueue.shift();
|
|
743
|
-
startThenDequeue(nextExecution);
|
|
744
|
-
}
|
|
745
|
-
};
|
|
746
|
-
supervisor.addExecution = ({
|
|
747
|
-
type,
|
|
748
|
-
src,
|
|
749
|
-
async,
|
|
750
|
-
execute
|
|
751
|
-
}) => {
|
|
752
|
-
const execution = {
|
|
753
|
-
type,
|
|
754
|
-
src,
|
|
755
|
-
async,
|
|
756
|
-
execute
|
|
757
|
-
};
|
|
758
|
-
execution.start = () => {
|
|
759
|
-
return superviseExecution(execution, {
|
|
760
|
-
isReload: false
|
|
761
|
-
});
|
|
762
|
-
};
|
|
763
|
-
execution.reload = () => {
|
|
764
|
-
return superviseExecution(execution, {
|
|
765
|
-
isReload: true
|
|
766
|
-
});
|
|
767
|
-
};
|
|
768
|
-
supervisedScripts.push(execution);
|
|
769
|
-
return addToExecutionQueue(execution);
|
|
770
|
-
};
|
|
771
|
-
const superviseExecution = async (execution, {
|
|
772
|
-
isReload
|
|
773
|
-
}) => {
|
|
774
|
-
if (logs) {
|
|
775
|
-
console.group(`[jsenv] loading ${execution.type} ${execution.src}`);
|
|
776
|
-
}
|
|
777
|
-
const executionResult = {
|
|
778
|
-
status: "pending",
|
|
779
|
-
loadDuration: null,
|
|
780
|
-
executionDuration: null,
|
|
781
|
-
duration: null,
|
|
782
|
-
exception: null,
|
|
783
|
-
namespace: null,
|
|
784
|
-
coverage: null
|
|
785
|
-
};
|
|
786
|
-
executionResults[execution.src] = executionResult;
|
|
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 = executionResult.loadDuration + executionResult.executionDuration;
|
|
811
|
-
resolveExecutionPromise();
|
|
812
|
-
};
|
|
813
|
-
};
|
|
814
|
-
const onError = e => {
|
|
815
|
-
executionResult.status = "failed";
|
|
816
|
-
const exception = supervisor.createException({
|
|
817
|
-
reason: e
|
|
818
|
-
});
|
|
819
|
-
if (exception.needsReport) {
|
|
820
|
-
supervisor.reportException(exception);
|
|
1134
|
+
scriptClone.removeAttribute("jsenv-cooked-by");
|
|
1135
|
+
scriptClone.removeAttribute("jsenv-inlined-by");
|
|
1136
|
+
scriptClone.removeAttribute("jsenv-injected-by");
|
|
1137
|
+
scriptClone.removeAttribute("inlined-from-src");
|
|
1138
|
+
scriptClone.removeAttribute("original-position");
|
|
1139
|
+
scriptClone.removeAttribute("original-src-position");
|
|
1140
|
+
return scriptClone;
|
|
1141
|
+
};
|
|
1142
|
+
const getScriptLoadPromise = async script => {
|
|
1143
|
+
return new Promise(resolve => {
|
|
1144
|
+
const windowErrorEventCallback = errorEvent => {
|
|
1145
|
+
if (errorEvent.filename === script.src) {
|
|
1146
|
+
removeWindowErrorEventCallback();
|
|
1147
|
+
resolve({
|
|
1148
|
+
detectedBy: "window_error_event",
|
|
1149
|
+
failed: true,
|
|
1150
|
+
error: errorEvent
|
|
1151
|
+
});
|
|
821
1152
|
}
|
|
822
|
-
executionResult.exception = exception;
|
|
823
1153
|
};
|
|
824
|
-
const
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
1154
|
+
const removeWindowErrorEventCallback = () => {
|
|
1155
|
+
window.removeEventListener("error", windowErrorEventCallback);
|
|
1156
|
+
};
|
|
1157
|
+
window.addEventListener("error", windowErrorEventCallback);
|
|
1158
|
+
script.addEventListener("error", errorEvent => {
|
|
1159
|
+
removeWindowErrorEventCallback();
|
|
1160
|
+
resolve({
|
|
1161
|
+
detectedBy: "script_error_event",
|
|
1162
|
+
failed: true,
|
|
1163
|
+
error: errorEvent
|
|
828
1164
|
});
|
|
829
|
-
if (logs) {
|
|
830
|
-
console.log(`${execution.type} load ended`);
|
|
831
|
-
console.groupEnd();
|
|
832
|
-
}
|
|
833
|
-
executionResult.status = "loaded";
|
|
834
|
-
scriptLoadDone();
|
|
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(namespace => {
|
|
841
|
-
executionResult.status = "completed";
|
|
842
|
-
executionResult.namespace = namespace;
|
|
843
|
-
scriptExecutionDone();
|
|
844
|
-
}, e => {
|
|
845
|
-
onError(e);
|
|
846
|
-
scriptExecutionDone();
|
|
847
|
-
});
|
|
848
|
-
}
|
|
849
|
-
} catch (e) {
|
|
850
|
-
if (logs) {
|
|
851
|
-
console.groupEnd();
|
|
852
|
-
}
|
|
853
|
-
onError(e);
|
|
854
|
-
scriptLoadDone();
|
|
855
|
-
}
|
|
856
|
-
};
|
|
857
|
-
supervisor.superviseScript = async ({
|
|
858
|
-
src,
|
|
859
|
-
async
|
|
860
|
-
}) => {
|
|
861
|
-
const {
|
|
862
|
-
currentScript
|
|
863
|
-
} = document;
|
|
864
|
-
const parentNode = currentScript.parentNode;
|
|
865
|
-
let nodeToReplace;
|
|
866
|
-
let currentScriptClone;
|
|
867
|
-
return supervisor.addExecution({
|
|
868
|
-
src,
|
|
869
|
-
type: "js_classic",
|
|
870
|
-
async,
|
|
871
|
-
execute: async ({
|
|
872
|
-
isReload
|
|
873
|
-
}) => {
|
|
874
|
-
const urlObject = new URL(src, window.location);
|
|
875
|
-
const loadPromise = new Promise((resolve, reject) => {
|
|
876
|
-
// do not use script.cloneNode()
|
|
877
|
-
// bcause https://stackoverflow.com/questions/28771542/why-dont-clonenode-script-tags-execute
|
|
878
|
-
currentScriptClone = document.createElement("script");
|
|
879
|
-
// browsers set async by default when creating script(s)
|
|
880
|
-
// we want an exact copy to preserves how code is executed
|
|
881
|
-
currentScriptClone.async = false;
|
|
882
|
-
Array.from(currentScript.attributes).forEach(attribute => {
|
|
883
|
-
currentScriptClone.setAttribute(attribute.nodeName, attribute.nodeValue);
|
|
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
|
-
}
|
|
916
|
-
});
|
|
917
|
-
};
|
|
918
|
-
supervisor.reloadSupervisedScript = ({
|
|
919
|
-
type,
|
|
920
|
-
src
|
|
921
|
-
}) => {
|
|
922
|
-
const supervisedScript = supervisedScripts.find(supervisedScriptCandidate => {
|
|
923
|
-
if (type && supervisedScriptCandidate.type !== type) {
|
|
924
|
-
return false;
|
|
925
|
-
}
|
|
926
|
-
return supervisedScriptCandidate.src === src;
|
|
927
1165
|
});
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
// just to be super safe and ensure any <script type="module"> got a chance to execute
|
|
934
|
-
const documentReadyPromise = new Promise(resolve => {
|
|
935
|
-
if (document.readyState === "complete") {
|
|
936
|
-
resolve();
|
|
937
|
-
return;
|
|
938
|
-
}
|
|
939
|
-
const loadCallback = () => {
|
|
940
|
-
window.removeEventListener("load", loadCallback);
|
|
941
|
-
resolve();
|
|
942
|
-
};
|
|
943
|
-
window.addEventListener("load", loadCallback);
|
|
1166
|
+
script.addEventListener("load", () => {
|
|
1167
|
+
removeWindowErrorEventCallback();
|
|
1168
|
+
resolve({
|
|
1169
|
+
detectedBy: "script_load_event"
|
|
1170
|
+
});
|
|
944
1171
|
});
|
|
945
|
-
|
|
946
|
-
const waitScriptExecutions = async () => {
|
|
947
|
-
const numberOfPromises = pendingPromises.length;
|
|
948
|
-
await Promise.all(pendingPromises);
|
|
949
|
-
// new scripts added while the other where executing
|
|
950
|
-
// (should happen only on webkit where
|
|
951
|
-
// script might be added after window load event)
|
|
952
|
-
await new Promise(resolve => setTimeout(resolve));
|
|
953
|
-
if (pendingPromises.length > numberOfPromises) {
|
|
954
|
-
await waitScriptExecutions();
|
|
955
|
-
}
|
|
956
|
-
};
|
|
957
|
-
await waitScriptExecutions();
|
|
958
|
-
return {
|
|
959
|
-
status: "completed",
|
|
960
|
-
executionResults,
|
|
961
|
-
startTime: navigationStartTime,
|
|
962
|
-
endTime: Date.now()
|
|
963
|
-
};
|
|
964
|
-
};
|
|
1172
|
+
});
|
|
965
1173
|
};
|
|
966
1174
|
return supervisor;
|
|
967
1175
|
})();
|