@jsenv/core 28.1.1 → 28.2.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.
Files changed (51) hide show
  1. package/dist/js/script_type_module_supervisor.js +8 -13
  2. package/dist/js/supervisor.js +702 -534
  3. package/dist/main.js +13275 -13164
  4. package/package.json +5 -5
  5. package/readme.md +3 -3
  6. package/src/build/build.js +960 -712
  7. package/src/build/inject_global_version_mappings.js +5 -20
  8. package/src/build/start_build_server.js +2 -2
  9. package/src/dev/start_dev_server.js +3 -2
  10. package/src/execute/run.js +1 -1
  11. package/src/execute/runtimes/browsers/from_playwright.js +1 -1
  12. package/src/omega/compat/runtime_compat.js +9 -6
  13. package/src/omega/errors.js +3 -0
  14. package/src/omega/fetched_content_compliance.js +2 -2
  15. package/src/omega/kitchen.js +189 -145
  16. package/src/omega/server/file_service.js +104 -71
  17. package/src/omega/url_graph/url_graph_loader.js +77 -0
  18. package/src/omega/url_graph/url_info_transformations.js +12 -15
  19. package/src/omega/url_graph.js +115 -101
  20. package/src/plugins/autoreload/jsenv_plugin_autoreload_client.js +1 -0
  21. package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +34 -36
  22. package/src/plugins/autoreload/jsenv_plugin_hmr.js +3 -2
  23. package/src/plugins/bundling/js_module/{bundle_js_module.js → bundle_js_modules.js} +51 -14
  24. package/src/plugins/bundling/jsenv_plugin_bundling.js +2 -2
  25. package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +11 -0
  26. package/src/plugins/inline/jsenv_plugin_html_inline_content.js +73 -62
  27. package/src/plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +77 -89
  28. package/src/plugins/plugin_controller.js +26 -22
  29. package/src/plugins/server_events/jsenv_plugin_server_events_client_injection.js +1 -0
  30. package/src/plugins/supervisor/client/script_type_module_supervisor.js +7 -9
  31. package/src/plugins/supervisor/client/supervisor.js +125 -96
  32. package/src/plugins/supervisor/jsenv_plugin_supervisor.js +2 -4
  33. package/src/plugins/toolbar/client/execution/toolbar_execution.js +1 -1
  34. package/src/plugins/transpilation/as_js_classic/async-to-promises.js +16 -0
  35. package/src/plugins/transpilation/as_js_classic/convert_js_module_to_js_classic.js +85 -0
  36. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +48 -190
  37. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_conversion.js +102 -0
  38. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_html.js +161 -240
  39. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_library.js +84 -0
  40. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_workers.js +19 -12
  41. package/src/plugins/transpilation/babel/jsenv_plugin_babel.js +1 -24
  42. package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +82 -52
  43. package/src/plugins/transpilation/jsenv_plugin_transpilation.js +12 -13
  44. package/src/plugins/url_analysis/html/html_urls.js +91 -34
  45. package/src/plugins/url_analysis/js/js_urls.js +5 -4
  46. package/src/plugins/url_resolution/jsenv_plugin_url_resolution.js +1 -0
  47. package/src/test/execute_plan.js +3 -8
  48. package/src/test/execute_test_plan.js +1 -1
  49. package/src/build/inject_service_worker_urls.js +0 -78
  50. package/src/build/resync_resource_hints.js +0 -112
  51. package/src/omega/url_graph/url_graph_load.js +0 -74
@@ -1,31 +1,22 @@
1
1
  window.__supervisor__ = (() => {
2
2
  const notImplemented = () => {
3
- throw new Error(`window.__supervisor__.setup() not called`)
4
- }
5
- const executionResults = {}
3
+ throw new Error(`window.__supervisor__.setup() not called`);
4
+ };
5
+
6
+ const executionResults = {};
6
7
  const supervisor = {
7
8
  reportError: notImplemented,
8
9
  superviseScript: notImplemented,
9
10
  reloadSupervisedScript: notImplemented,
10
- collectScriptResults: notImplemented,
11
- getScriptExecutionResults: () => {
12
- // wait for page to load before collecting script execution results
13
- const htmlReadyPromise = new Promise((resolve) => {
14
- if (document.readyState === "complete") {
15
- resolve()
16
- return
17
- }
18
- const loadCallback = () => {
19
- window.removeEventListener("load", loadCallback)
20
- resolve()
21
- }
22
- window.addEventListener("load", loadCallback)
23
- })
24
- return htmlReadyPromise.then(() => {
25
- return supervisor.collectScriptResults()
26
- })
27
- },
28
- executionResults,
11
+ getDocumentExecutionResult: notImplemented,
12
+ executionResults
13
+ };
14
+ let navigationStartTime;
15
+
16
+ try {
17
+ navigationStartTime = window.performance.timing.navigationStart;
18
+ } catch (e) {
19
+ navigationStartTime = Date.now();
29
20
  }
30
21
 
31
22
  supervisor.setupReportException = ({
@@ -33,23 +24,24 @@ window.__supervisor__ = (() => {
33
24
  errorNotification,
34
25
  errorOverlay,
35
26
  errorBaseUrl,
36
- openInEditor,
27
+ openInEditor
37
28
  }) => {
38
- const DYNAMIC_IMPORT_FETCH_ERROR = "dynamic_import_fetch_error"
39
- const DYNAMIC_IMPORT_EXPORT_MISSING = "dynamic_import_export_missing"
40
- const DYNAMIC_IMPORT_SYNTAX_ERROR = "dynamic_import_syntax_error"
29
+ const DYNAMIC_IMPORT_FETCH_ERROR = "dynamic_import_fetch_error";
30
+ const DYNAMIC_IMPORT_EXPORT_MISSING = "dynamic_import_export_missing";
31
+ const DYNAMIC_IMPORT_SYNTAX_ERROR = "dynamic_import_syntax_error";
41
32
 
42
33
  const createException = ({
43
34
  reason,
44
35
  reportedBy,
45
36
  url,
46
37
  line,
47
- column,
38
+ column
48
39
  } = {}) => {
49
40
  const exception = {
50
41
  reason,
51
42
  reportedBy,
52
- isError: false, // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw
43
+ isError: false,
44
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw
53
45
  code: null,
54
46
  message: null,
55
47
  stack: null,
@@ -62,462 +54,526 @@ window.__supervisor__ = (() => {
62
54
  url: null,
63
55
  line: null,
64
56
  column: null,
65
- originalUrl: null,
66
- },
67
- }
57
+ originalUrl: null
58
+ }
59
+ };
68
60
 
69
61
  const writeBasicProperties = () => {
70
62
  if (reason === undefined) {
71
- exception.message = "undefined"
72
- return
63
+ exception.message = "undefined";
64
+ return;
73
65
  }
66
+
74
67
  if (reason === null) {
75
- exception.message = "null"
76
- return
68
+ exception.message = "null";
69
+ return;
77
70
  }
71
+
78
72
  if (typeof reason === "string") {
79
- exception.message = reason
80
- return
73
+ exception.message = reason;
74
+ return;
81
75
  }
76
+
82
77
  if (reason instanceof Error) {
83
- const error = reason
84
- let message = error.message
85
- exception.isError = true
78
+ const error = reason;
79
+ let message = error.message;
80
+ exception.isError = true;
81
+
86
82
  if (Error.captureStackTrace) {
87
83
  // stackTrace formatted by V8
88
- exception.message = message
89
- exception.stack = getErrorStackWithoutErrorMessage(error)
90
- exception.stackFormatIsV8 = true
91
- exception.stackSourcemapped = true
84
+ exception.message = message;
85
+ exception.stack = getErrorStackWithoutErrorMessage(error);
86
+ exception.stackFormatIsV8 = true;
87
+ exception.stackSourcemapped = true;
92
88
  } else {
93
- exception.message = message
94
- exception.stack = error.stack ? ` ${error.stack}` : null
95
- exception.stackFormatIsV8 = false
96
- exception.stackSourcemapped = false
89
+ exception.message = message;
90
+ exception.stack = error.stack ? ` ${error.stack}` : null;
91
+ exception.stackFormatIsV8 = false;
92
+ exception.stackSourcemapped = false;
97
93
  }
94
+
98
95
  if (error.reportedBy) {
99
- exception.reportedBy = error.reportedBy
96
+ exception.reportedBy = error.reportedBy;
100
97
  }
98
+
101
99
  if (error.url) {
102
- Object.assign(exception.site, resolveUrlSite({ url: error.url }))
100
+ Object.assign(exception.site, resolveUrlSite({
101
+ url: error.url
102
+ }));
103
103
  }
104
+
104
105
  if (error.needsReport) {
105
- exception.needsReport = true
106
+ exception.needsReport = true;
106
107
  }
107
- export_missing: {
108
+
109
+ {
108
110
  // chrome
109
111
  if (message.includes("does not provide an export named")) {
110
- exception.code = DYNAMIC_IMPORT_EXPORT_MISSING
111
- return
112
- }
113
- // firefox
112
+ exception.code = DYNAMIC_IMPORT_EXPORT_MISSING;
113
+ return;
114
+ } // firefox
115
+
116
+
114
117
  if (message.startsWith("import not found:")) {
115
- exception.code = DYNAMIC_IMPORT_EXPORT_MISSING
116
- return
117
- }
118
- // safari
118
+ exception.code = DYNAMIC_IMPORT_EXPORT_MISSING;
119
+ return;
120
+ } // safari
121
+
122
+
119
123
  if (message.startsWith("import binding name")) {
120
- exception.code = DYNAMIC_IMPORT_EXPORT_MISSING
121
- return
124
+ exception.code = DYNAMIC_IMPORT_EXPORT_MISSING;
125
+ return;
122
126
  }
123
127
  }
124
- js_syntax_error: {
128
+
129
+ {
125
130
  if (error.name === "SyntaxError" && typeof line === "number") {
126
- exception.code = DYNAMIC_IMPORT_SYNTAX_ERROR
127
- return
131
+ exception.code = DYNAMIC_IMPORT_SYNTAX_ERROR;
132
+ return;
128
133
  }
129
134
  }
130
- return
135
+
136
+ return;
131
137
  }
138
+
132
139
  if (typeof reason === "object") {
133
- exception.code = reason.code
134
- exception.message = reason.message
135
- exception.stack = reason.stack
140
+ exception.code = reason.code;
141
+ exception.message = reason.message;
142
+ exception.stack = reason.stack;
143
+
136
144
  if (reason.reportedBy) {
137
- exception.reportedBy = reason.reportedBy
145
+ exception.reportedBy = reason.reportedBy;
138
146
  }
147
+
139
148
  if (reason.url) {
140
- Object.assign(exception.site, resolveUrlSite({ url: reason.url }))
149
+ Object.assign(exception.site, resolveUrlSite({
150
+ url: reason.url
151
+ }));
141
152
  }
153
+
142
154
  if (reason.needsReport) {
143
- exception.needsReport = true
155
+ exception.needsReport = true;
144
156
  }
145
- return
157
+
158
+ return;
146
159
  }
147
- exception.message = JSON.stringify(reason)
148
- }
149
- writeBasicProperties()
150
160
 
151
- // first create a version of the stack with file://
161
+ exception.message = JSON.stringify(reason);
162
+ };
163
+
164
+ writeBasicProperties(); // first create a version of the stack with file://
152
165
  // (and use it to locate exception url+line+column)
166
+
153
167
  if (exception.stack) {
154
- exception.originalStack = exception.stack
155
- exception.stack = replaceUrls(
156
- exception.originalStack,
157
- (serverUrlSite) => {
158
- const fileUrlSite = resolveUrlSite(serverUrlSite)
159
- if (exception.site.url === null) {
160
- Object.assign(exception.site, fileUrlSite)
161
- }
162
- return stringifyUrlSite(fileUrlSite)
163
- },
164
- )
165
- }
166
- // then if it fails, use url+line+column passed
168
+ exception.originalStack = exception.stack;
169
+ exception.stack = replaceUrls(exception.originalStack, serverUrlSite => {
170
+ const fileUrlSite = resolveUrlSite(serverUrlSite);
171
+
172
+ if (exception.site.url === null) {
173
+ Object.assign(exception.site, fileUrlSite);
174
+ }
175
+
176
+ return stringifyUrlSite(fileUrlSite);
177
+ });
178
+ } // then if it fails, use url+line+column passed
179
+
180
+
167
181
  if (exception.site.url === null && url) {
168
182
  if (typeof line === "string") {
169
- line = parseInt(line)
183
+ line = parseInt(line);
170
184
  }
185
+
171
186
  if (typeof column === "string") {
172
- column = parseInt(column)
187
+ column = parseInt(column);
173
188
  }
174
- const fileUrlSite = resolveUrlSite({ url, line, column })
175
- if (
176
- fileUrlSite.isInline &&
177
- exception.code === DYNAMIC_IMPORT_SYNTAX_ERROR
178
- ) {
189
+
190
+ const fileUrlSite = resolveUrlSite({
191
+ url,
192
+ line,
193
+ column
194
+ });
195
+
196
+ if (fileUrlSite.isInline && exception.code === DYNAMIC_IMPORT_SYNTAX_ERROR) {
179
197
  // syntax error on inline script need line-1 for some reason
180
198
  if (Error.captureStackTrace) {
181
- fileUrlSite.line--
199
+ fileUrlSite.line--;
182
200
  } else {
183
201
  // firefox and safari need line-2
184
- fileUrlSite.line -= 2
202
+ fileUrlSite.line -= 2;
185
203
  }
186
204
  }
187
- Object.assign(exception.site, fileUrlSite)
205
+
206
+ Object.assign(exception.site, fileUrlSite);
188
207
  }
189
- exception.text = stringifyMessageAndStack(exception)
190
- return exception
191
- }
192
208
 
193
- const stringifyMessageAndStack = ({ message, stack }) => {
209
+ exception.text = stringifyMessageAndStack(exception);
210
+ return exception;
211
+ };
212
+
213
+ const stringifyMessageAndStack = ({
214
+ message,
215
+ stack
216
+ }) => {
194
217
  if (message && stack) {
195
- return `${message}\n${stack}`
218
+ return `${message}\n${stack}`;
196
219
  }
220
+
197
221
  if (stack) {
198
- return stack
222
+ return stack;
199
223
  }
200
- return message
201
- }
202
224
 
203
- const stringifyUrlSite = ({ url, line, column }) => {
204
- return typeof line === "number" && typeof column === "number"
205
- ? `${url}:${line}:${column}`
206
- : typeof line === "number"
207
- ? `${url}:${line}`
208
- : url
209
- }
225
+ return message;
226
+ };
227
+
228
+ const stringifyUrlSite = ({
229
+ url,
230
+ line,
231
+ column
232
+ }) => {
233
+ return typeof line === "number" && typeof column === "number" ? `${url}:${line}:${column}` : typeof line === "number" ? `${url}:${line}` : url;
234
+ };
235
+
236
+ const resolveUrlSite = ({
237
+ url,
238
+ line,
239
+ column
240
+ }) => {
241
+ const inlineUrlMatch = url.match(/@L([0-9]+)C([0-9]+)\-L([0-9]+)C([0-9]+)(\.[\w]+)$/);
210
242
 
211
- const resolveUrlSite = ({ url, line, column }) => {
212
- const inlineUrlMatch = url.match(
213
- /@L([0-9]+)C([0-9]+)\-L([0-9]+)C([0-9]+)(\.[\w]+)$/,
214
- )
215
243
  if (inlineUrlMatch) {
216
- const htmlUrl = url.slice(0, inlineUrlMatch.index)
217
- const tagLineStart = parseInt(inlineUrlMatch[1])
218
- const tagColumnStart = parseInt(inlineUrlMatch[2])
219
- const tagLineEnd = parseInt(inlineUrlMatch[3])
220
- const tagColumnEnd = parseInt(inlineUrlMatch[4])
221
- const extension = inlineUrlMatch[5]
222
- url = htmlUrl
223
- line = tagLineStart + (typeof line === "number" ? line : 0)
224
- // stackTrace formatted by V8 (chrome)
244
+ const htmlUrl = url.slice(0, inlineUrlMatch.index);
245
+ const tagLineStart = parseInt(inlineUrlMatch[1]);
246
+ const tagColumnStart = parseInt(inlineUrlMatch[2]);
247
+ const tagLineEnd = parseInt(inlineUrlMatch[3]);
248
+ const tagColumnEnd = parseInt(inlineUrlMatch[4]);
249
+ const extension = inlineUrlMatch[5];
250
+ url = htmlUrl;
251
+ line = tagLineStart + (typeof line === "number" ? line : 0); // stackTrace formatted by V8 (chrome)
252
+
225
253
  if (Error.captureStackTrace) {
226
- line--
254
+ line--;
227
255
  }
228
- column = tagColumnStart + (typeof column === "number" ? column : 0)
229
- const fileUrl = resolveFileUrl(url)
256
+
257
+ column = tagColumnStart + (typeof column === "number" ? column : 0);
258
+ const fileUrl = resolveFileUrl(url);
230
259
  return {
231
260
  isInline: true,
232
261
  serverUrl: url,
233
262
  originalUrl: `${fileUrl}@L${tagLineStart}C${tagColumnStart}-L${tagLineEnd}C${tagColumnEnd}${extension}`,
234
263
  url: fileUrl,
235
264
  line,
236
- column,
237
- }
265
+ column
266
+ };
238
267
  }
268
+
239
269
  return {
240
270
  isInline: false,
241
271
  serverUrl: url,
242
272
  url: resolveFileUrl(url),
243
273
  line,
244
- column,
245
- }
246
- }
274
+ column
275
+ };
276
+ };
277
+
278
+ const getErrorStackWithoutErrorMessage = error => {
279
+ let stack = error.stack;
280
+ const messageInStack = `${error.name}: ${error.message}`;
247
281
 
248
- const getErrorStackWithoutErrorMessage = (error) => {
249
- let stack = error.stack
250
- const messageInStack = `${error.name}: ${error.message}`
251
282
  if (stack.startsWith(messageInStack)) {
252
- stack = stack.slice(messageInStack.length)
283
+ stack = stack.slice(messageInStack.length);
253
284
  }
254
- const nextLineIndex = stack.indexOf("\n")
285
+
286
+ const nextLineIndex = stack.indexOf("\n");
287
+
255
288
  if (nextLineIndex > -1) {
256
- stack = stack.slice(nextLineIndex + 1)
289
+ stack = stack.slice(nextLineIndex + 1);
257
290
  }
258
- return stack
259
- }
260
291
 
261
- const resolveFileUrl = (url) => {
262
- let urlObject = new URL(url)
292
+ return stack;
293
+ };
294
+
295
+ const resolveFileUrl = url => {
296
+ let urlObject = new URL(url);
297
+
263
298
  if (urlObject.origin === window.origin) {
264
- urlObject = new URL(
265
- `${urlObject.pathname.slice(1)}${urlObject.search}`,
266
- rootDirectoryUrl,
267
- )
299
+ urlObject = new URL(`${urlObject.pathname.slice(1)}${urlObject.search}`, rootDirectoryUrl);
268
300
  }
301
+
269
302
  if (urlObject.href.startsWith("file:")) {
270
- const atFsIndex = urlObject.pathname.indexOf("/@fs/")
303
+ const atFsIndex = urlObject.pathname.indexOf("/@fs/");
304
+
271
305
  if (atFsIndex > -1) {
272
- const afterAtFs = urlObject.pathname.slice(atFsIndex + "/@fs/".length)
273
- return new URL(afterAtFs, "file:///").href
306
+ const afterAtFs = urlObject.pathname.slice(atFsIndex + "/@fs/".length);
307
+ return new URL(afterAtFs, "file:///").href;
274
308
  }
275
309
  }
276
- return urlObject.href
277
- }
278
310
 
279
- // `Error: yo
311
+ return urlObject.href;
312
+ }; // `Error: yo
280
313
  // at Object.execute (http://127.0.0.1:57300/build/src/__test__/file-throw.js:9:13)
281
314
  // at doExec (http://127.0.0.1:3000/src/__test__/file-throw.js:452:38)
282
315
  // at postOrderExec (http://127.0.0.1:3000/src/__test__/file-throw.js:448:16)
283
316
  // at http://127.0.0.1:3000/src/__test__/file-throw.js:399:18`.replace(/(?:https?|ftp|file):\/\/(.*+)$/gm, (...args) => {
284
317
  // debugger
285
318
  // })
319
+
320
+
286
321
  const replaceUrls = (source, replace) => {
287
- return source.replace(/(?:https?|ftp|file):\/\/\S+/gm, (match) => {
288
- let replacement = ""
289
- const lastChar = match[match.length - 1]
322
+ return source.replace(/(?:https?|ftp|file):\/\/\S+/gm, match => {
323
+ let replacement = "";
324
+ const lastChar = match[match.length - 1]; // hotfix because our url regex sucks a bit
325
+
326
+ const endsWithSeparationChar = lastChar === ")" || lastChar === ":";
290
327
 
291
- // hotfix because our url regex sucks a bit
292
- const endsWithSeparationChar = lastChar === ")" || lastChar === ":"
293
328
  if (endsWithSeparationChar) {
294
- match = match.slice(0, -1)
329
+ match = match.slice(0, -1);
295
330
  }
296
331
 
297
- const lineAndColumnPattern = /:([0-9]+):([0-9]+)$/
298
- const lineAndColumMatch = match.match(lineAndColumnPattern)
332
+ const lineAndColumnPattern = /:([0-9]+):([0-9]+)$/;
333
+ const lineAndColumMatch = match.match(lineAndColumnPattern);
334
+
299
335
  if (lineAndColumMatch) {
300
- const lineAndColumnString = lineAndColumMatch[0]
301
- const lineString = lineAndColumMatch[1]
302
- const columnString = lineAndColumMatch[2]
336
+ const lineAndColumnString = lineAndColumMatch[0];
337
+ const lineString = lineAndColumMatch[1];
338
+ const columnString = lineAndColumMatch[2];
303
339
  replacement = replace({
304
340
  url: match.slice(0, -lineAndColumnString.length),
305
341
  line: lineString ? parseInt(lineString) : null,
306
- column: columnString ? parseInt(columnString) : null,
307
- })
342
+ column: columnString ? parseInt(columnString) : null
343
+ });
308
344
  } else {
309
- const linePattern = /:([0-9]+)$/
310
- const lineMatch = match.match(linePattern)
345
+ const linePattern = /:([0-9]+)$/;
346
+ const lineMatch = match.match(linePattern);
347
+
311
348
  if (lineMatch) {
312
- const lineString = lineMatch[0]
349
+ const lineString = lineMatch[0];
313
350
  replacement = replace({
314
351
  url: match.slice(0, -lineString.length),
315
- line: lineString ? parseInt(lineString) : null,
316
- })
352
+ line: lineString ? parseInt(lineString) : null
353
+ });
317
354
  } else {
318
355
  replacement = replace({
319
- url: match,
320
- })
356
+ url: match
357
+ });
321
358
  }
322
359
  }
360
+
323
361
  if (endsWithSeparationChar) {
324
- return `${replacement}${lastChar}`
362
+ return `${replacement}${lastChar}`;
325
363
  }
326
- return replacement
327
- })
328
- }
329
364
 
330
- let formatError
331
- error_formatter: {
332
- formatError = (exceptionInfo) => {
365
+ return replacement;
366
+ });
367
+ };
368
+
369
+ let formatError;
370
+
371
+ {
372
+ formatError = exceptionInfo => {
333
373
  const errorParts = {
334
374
  theme: "dark",
335
375
  title: "An error occured",
336
376
  text: "",
337
377
  tip: "",
338
- errorDetailsPromise: null,
339
- }
340
- const tips = []
341
- tips.push("Click outside to close.")
342
- errorParts.tip = tips.join(`\n <br />\n `)
378
+ errorDetailsPromise: null
379
+ };
380
+ const tips = [];
381
+ tips.push("Click outside to close.");
382
+ errorParts.tip = tips.join(`\n <br />\n `);
343
383
 
344
- const generateClickableText = (text) => {
384
+ const generateClickableText = text => {
345
385
  const textWithHtmlLinks = makeLinksClickable(text, {
346
- createLink: ({ url, line, column }) => {
347
- const urlSite = resolveUrlSite({ url, line, column })
386
+ createLink: ({
387
+ url,
388
+ line,
389
+ column
390
+ }) => {
391
+ const urlSite = resolveUrlSite({
392
+ url,
393
+ line,
394
+ column
395
+ });
396
+
348
397
  if (errorBaseUrl) {
349
398
  if (urlSite.url.startsWith(rootDirectoryUrl)) {
350
- urlSite.url = `${errorBaseUrl}${urlSite.url.slice(
351
- rootDirectoryUrl.length,
352
- )}`
399
+ urlSite.url = `${errorBaseUrl}${urlSite.url.slice(rootDirectoryUrl.length)}`;
353
400
  } else {
354
- urlSite.url = "file:///mocked_for_snapshots"
401
+ urlSite.url = "file:///mocked_for_snapshots";
355
402
  }
356
403
  }
357
- const urlWithLineAndColumn = stringifyUrlSite(urlSite)
404
+
405
+ const urlWithLineAndColumn = stringifyUrlSite(urlSite);
358
406
  return {
359
- href:
360
- urlSite.url.startsWith("file:") && openInEditor
361
- ? `javascript:window.fetch('/__open_in_editor__/${encodeURIComponent(
362
- urlWithLineAndColumn,
363
- )}')`
364
- : urlSite.url,
365
- text: urlWithLineAndColumn,
366
- }
367
- },
368
- })
369
- return textWithHtmlLinks
370
- }
407
+ href: urlSite.url.startsWith("file:") && openInEditor ? `javascript:window.fetch('/__open_in_editor__/${encodeURIComponent(urlWithLineAndColumn)}')` : urlSite.url,
408
+ text: urlWithLineAndColumn
409
+ };
410
+ }
411
+ });
412
+ return textWithHtmlLinks;
413
+ };
371
414
 
372
415
  errorParts.text = stringifyMessageAndStack({
373
- message: exceptionInfo.message
374
- ? generateClickableText(exceptionInfo.message)
375
- : "",
376
- stack: exceptionInfo.stack
377
- ? generateClickableText(exceptionInfo.stack)
378
- : "",
379
- })
416
+ message: exceptionInfo.message ? generateClickableText(exceptionInfo.message) : "",
417
+ stack: exceptionInfo.stack ? generateClickableText(exceptionInfo.stack) : ""
418
+ });
419
+
380
420
  if (exceptionInfo.site.url) {
381
421
  errorParts.errorDetailsPromise = (async () => {
382
422
  try {
383
- if (
384
- exceptionInfo.code === DYNAMIC_IMPORT_FETCH_ERROR ||
385
- exceptionInfo.reportedBy === "script_error_event"
386
- ) {
387
- const response = await window.fetch(
388
- `/__get_error_cause__/${encodeURIComponent(
389
- exceptionInfo.site.isInline
390
- ? exceptionInfo.site.originalUrl
391
- : exceptionInfo.site.url,
392
- )}`,
393
- )
423
+ if (exceptionInfo.code === DYNAMIC_IMPORT_FETCH_ERROR || exceptionInfo.reportedBy === "script_error_event") {
424
+ const response = await window.fetch(`/__get_error_cause__/${encodeURIComponent(exceptionInfo.site.isInline ? exceptionInfo.site.originalUrl : exceptionInfo.site.url)}`);
425
+
394
426
  if (response.status !== 200) {
395
- return null
427
+ return null;
396
428
  }
397
- const causeInfo = await response.json()
429
+
430
+ const causeInfo = await response.json();
431
+
398
432
  if (!causeInfo) {
399
- return null
433
+ return null;
400
434
  }
401
- const causeText =
402
- causeInfo.code === "NOT_FOUND"
403
- ? stringifyMessageAndStack({
404
- message: generateClickableText(causeInfo.reason),
405
- stack: generateClickableText(causeInfo.codeFrame),
406
- })
407
- : stringifyMessageAndStack({
408
- message: generateClickableText(causeInfo.stack),
409
- stack: generateClickableText(causeInfo.codeFrame),
410
- })
435
+
436
+ const causeText = causeInfo.code === "NOT_FOUND" ? stringifyMessageAndStack({
437
+ message: generateClickableText(causeInfo.reason),
438
+ stack: generateClickableText(causeInfo.codeFrame)
439
+ }) : stringifyMessageAndStack({
440
+ message: generateClickableText(causeInfo.stack),
441
+ stack: generateClickableText(causeInfo.codeFrame)
442
+ });
411
443
  return {
412
- cause: causeText,
413
- }
444
+ cause: causeText
445
+ };
414
446
  }
415
- if (
416
- exceptionInfo.site.line !== undefined &&
417
- // code frame showing internal window.reportError is pointless
418
- !exceptionInfo.site.url.endsWith(
419
- `script_type_module_supervisor.js`,
420
- )
421
- ) {
422
- const urlToFetch = new URL(
423
- `/__get_code_frame__/${encodeURIComponent(
424
- stringifyUrlSite(exceptionInfo.site),
425
- )}`,
426
- window.origin,
427
- )
447
+
448
+ if (exceptionInfo.site.line !== undefined && // code frame showing internal window.reportError is pointless
449
+ !exceptionInfo.site.url.endsWith(`script_type_module_supervisor.js`)) {
450
+ const urlToFetch = new URL(`/__get_code_frame__/${encodeURIComponent(stringifyUrlSite(exceptionInfo.site))}`, window.origin);
451
+
428
452
  if (!exceptionInfo.stackSourcemapped) {
429
- urlToFetch.searchParams.set("remap", "")
453
+ urlToFetch.searchParams.set("remap", "");
430
454
  }
431
- const response = await window.fetch(urlToFetch)
455
+
456
+ const response = await window.fetch(urlToFetch);
457
+
432
458
  if (response.status !== 200) {
433
- return null
459
+ return null;
434
460
  }
435
- const codeFrame = await response.text()
461
+
462
+ const codeFrame = await response.text();
436
463
  return {
437
- codeFrame: generateClickableText(codeFrame),
438
- }
464
+ codeFrame: generateClickableText(codeFrame)
465
+ };
439
466
  }
440
467
  } catch (e) {
441
468
  // happens if server is closed for instance
442
- return null
469
+ return null;
443
470
  }
444
- return null
445
- })()
471
+
472
+ return null;
473
+ })();
446
474
  }
447
- return errorParts
448
- }
449
475
 
450
- const makeLinksClickable = (
451
- string,
452
- { createLink = ({ url }) => url },
453
- ) => {
476
+ return errorParts;
477
+ };
478
+
479
+ const makeLinksClickable = (string, {
480
+ createLink = ({
481
+ url
482
+ }) => url
483
+ }) => {
454
484
  // normalize line breaks
455
- string = string.replace(/\n/g, "\n")
456
- string = escapeHtml(string)
457
- // render links
458
- string = replaceUrls(string, ({ url, line, column }) => {
459
- const { href, text } = createLink({ url, line, column })
460
- return link({ href, text })
461
- })
462
- return string
463
- }
485
+ string = string.replace(/\n/g, "\n");
486
+ string = escapeHtml(string); // render links
464
487
 
465
- const escapeHtml = (string) => {
466
- return string
467
- .replace(/&/g, "&amp;")
468
- .replace(/</g, "&lt;")
469
- .replace(/>/g, "&gt;")
470
- .replace(/"/g, "&quot;")
471
- .replace(/'/g, "&#039;")
472
- }
488
+ string = replaceUrls(string, ({
489
+ url,
490
+ line,
491
+ column
492
+ }) => {
493
+ const {
494
+ href,
495
+ text
496
+ } = createLink({
497
+ url,
498
+ line,
499
+ column
500
+ });
501
+ return link({
502
+ href,
503
+ text
504
+ });
505
+ });
506
+ return string;
507
+ };
508
+
509
+ const escapeHtml = string => {
510
+ return string.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
511
+ };
473
512
 
474
- const link = ({ href, text = href }) => `<a href="${href}">${text}</a>`
513
+ const link = ({
514
+ href,
515
+ text = href
516
+ }) => `<a href="${href}">${text}</a>`;
475
517
  }
476
518
 
477
- let displayErrorNotification
478
- error_notification: {
479
- const { Notification } = window
480
- displayErrorNotification =
481
- typeof Notification === "function"
482
- ? ({ title, text, icon }) => {
483
- if (Notification.permission === "granted") {
484
- const notification = new Notification(title, {
485
- lang: "en",
486
- body: text,
487
- icon,
488
- })
489
- notification.onclick = () => {
490
- window.focus()
491
- }
492
- }
493
- }
494
- : () => {}
519
+ let displayErrorNotification;
520
+
521
+ {
522
+ const {
523
+ Notification
524
+ } = window;
525
+ displayErrorNotification = typeof Notification === "function" ? ({
526
+ title,
527
+ text,
528
+ icon
529
+ }) => {
530
+ if (Notification.permission === "granted") {
531
+ const notification = new Notification(title, {
532
+ lang: "en",
533
+ body: text,
534
+ icon
535
+ });
536
+
537
+ notification.onclick = () => {
538
+ window.focus();
539
+ };
540
+ }
541
+ } : () => {};
495
542
  }
496
543
 
497
- const JSENV_ERROR_OVERLAY_TAGNAME = "jsenv-error-overlay"
498
- let displayJsenvErrorOverlay
499
- error_overlay: {
500
- displayJsenvErrorOverlay = (params) => {
501
- let jsenvErrorOverlay = new JsenvErrorOverlay(params)
502
- document
503
- .querySelectorAll(JSENV_ERROR_OVERLAY_TAGNAME)
504
- .forEach((node) => {
505
- node.parentNode.removeChild(node)
506
- })
507
- document.body.appendChild(jsenvErrorOverlay)
544
+ const JSENV_ERROR_OVERLAY_TAGNAME = "jsenv-error-overlay";
545
+ let displayJsenvErrorOverlay;
546
+
547
+ {
548
+ displayJsenvErrorOverlay = params => {
549
+ let jsenvErrorOverlay = new JsenvErrorOverlay(params);
550
+ document.querySelectorAll(JSENV_ERROR_OVERLAY_TAGNAME).forEach(node => {
551
+ node.parentNode.removeChild(node);
552
+ });
553
+ document.body.appendChild(jsenvErrorOverlay);
554
+
508
555
  const removeErrorOverlay = () => {
509
556
  if (jsenvErrorOverlay && jsenvErrorOverlay.parentNode) {
510
- document.body.removeChild(jsenvErrorOverlay)
511
- jsenvErrorOverlay = null
557
+ document.body.removeChild(jsenvErrorOverlay);
558
+ jsenvErrorOverlay = null;
512
559
  }
513
- }
514
- return removeErrorOverlay
515
- }
560
+ };
561
+
562
+ return removeErrorOverlay;
563
+ };
516
564
 
517
565
  class JsenvErrorOverlay extends HTMLElement {
518
- constructor({ theme, title, text, tip, errorDetailsPromise }) {
519
- super()
520
- this.root = this.attachShadow({ mode: "open" })
566
+ constructor({
567
+ theme,
568
+ title,
569
+ text,
570
+ tip,
571
+ errorDetailsPromise
572
+ }) {
573
+ super();
574
+ this.root = this.attachShadow({
575
+ mode: "open"
576
+ });
521
577
  this.root.innerHTML = `
522
578
  <style>
523
579
  ${overlayCSS}
@@ -531,50 +587,60 @@ window.__supervisor__ = (() => {
531
587
  <div class="tip">
532
588
  ${tip}
533
589
  </div>
534
- </div>`
590
+ </div>`;
591
+
535
592
  this.root.querySelector(".backdrop").onclick = () => {
536
593
  if (!this.parentNode) {
537
594
  // not in document anymore
538
- return
595
+ return;
539
596
  }
540
- this.root.querySelector(".backdrop").onclick = null
541
- this.parentNode.removeChild(this)
542
- }
597
+
598
+ this.root.querySelector(".backdrop").onclick = null;
599
+ this.parentNode.removeChild(this);
600
+ };
601
+
543
602
  if (errorDetailsPromise) {
544
- errorDetailsPromise.then((errorDetails) => {
603
+ errorDetailsPromise.then(errorDetails => {
545
604
  if (!errorDetails || !this.parentNode) {
546
- return
605
+ return;
547
606
  }
548
- const { codeFrame, cause } = errorDetails
607
+
608
+ const {
609
+ codeFrame,
610
+ cause
611
+ } = errorDetails;
612
+
549
613
  if (codeFrame) {
550
- this.root.querySelector(".text").innerHTML += `\n\n${codeFrame}`
614
+ this.root.querySelector(".text").innerHTML += `\n\n${codeFrame}`;
551
615
  }
616
+
552
617
  if (cause) {
553
- const causeIndented = prefixRemainingLines(cause, " ")
554
- this.root.querySelector(
555
- ".text",
556
- ).innerHTML += `\n [cause]: ${causeIndented}`
618
+ const causeIndented = prefixRemainingLines(cause, " ");
619
+ this.root.querySelector(".text").innerHTML += `\n [cause]: ${causeIndented}`;
557
620
  }
558
- })
621
+ });
559
622
  }
560
623
  }
624
+
561
625
  }
562
626
 
563
627
  const prefixRemainingLines = (text, prefix) => {
564
- const lines = text.split(/\r?\n/)
565
- const firstLine = lines.shift()
566
- let result = firstLine
567
- let i = 0
628
+ const lines = text.split(/\r?\n/);
629
+ const firstLine = lines.shift();
630
+ let result = firstLine;
631
+ let i = 0;
632
+
568
633
  while (i < lines.length) {
569
- const line = lines[i]
570
- i++
571
- result += line.length ? `\n${prefix}${line}` : `\n`
634
+ const line = lines[i];
635
+ i++;
636
+ result += line.length ? `\n${prefix}${line}` : `\n`;
572
637
  }
573
- return result
574
- }
638
+
639
+ return result;
640
+ };
575
641
 
576
642
  if (customElements && !customElements.get(JSENV_ERROR_OVERLAY_TAGNAME)) {
577
- customElements.define(JSENV_ERROR_OVERLAY_TAGNAME, JsenvErrorOverlay)
643
+ customElements.define(JSENV_ERROR_OVERLAY_TAGNAME, JsenvErrorOverlay);
578
644
  }
579
645
 
580
646
  const overlayCSS = `
@@ -651,13 +717,19 @@ window.__supervisor__ = (() => {
651
717
 
652
718
  pre a {
653
719
  color: inherit;
654
- }`
720
+ }`;
655
721
  }
656
722
 
657
- supervisor.createException = createException
658
- supervisor.reportException = (exception) => {
659
- const { theme, title, text, tip, errorDetailsPromise } =
660
- formatError(exception)
723
+ supervisor.createException = createException;
724
+
725
+ supervisor.reportException = exception => {
726
+ const {
727
+ theme,
728
+ title,
729
+ text,
730
+ tip,
731
+ errorDetailsPromise
732
+ } = formatError(exception);
661
733
 
662
734
  if (errorOverlay) {
663
735
  const removeErrorOverlay = displayJsenvErrorOverlay({
@@ -665,30 +737,41 @@ window.__supervisor__ = (() => {
665
737
  title,
666
738
  text,
667
739
  tip,
668
- errorDetailsPromise,
669
- })
740
+ errorDetailsPromise
741
+ });
742
+
670
743
  if (window.__reloader__) {
671
744
  window.__reloader__.onstatuschange = () => {
672
745
  if (window.__reloader__.status === "reloading") {
673
- removeErrorOverlay()
746
+ removeErrorOverlay();
674
747
  }
675
- }
748
+ };
676
749
  }
677
750
  }
751
+
678
752
  if (errorNotification) {
679
753
  displayErrorNotification({
680
754
  title,
681
- text,
682
- })
755
+ text
756
+ });
683
757
  }
684
- return exception
685
- }
686
- window.addEventListener("error", (errorEvent) => {
758
+
759
+ return exception;
760
+ };
761
+
762
+ window.addEventListener("error", errorEvent => {
687
763
  if (!errorEvent.isTrusted) {
688
764
  // ignore custom error event (not sent by browser)
689
- return
765
+ return;
690
766
  }
691
- const { error, message, filename, lineno, colno } = errorEvent
767
+
768
+ const {
769
+ error,
770
+ message,
771
+ filename,
772
+ lineno,
773
+ colno
774
+ } = errorEvent;
692
775
  const exception = supervisor.createException({
693
776
  // when error is reported within a worker error is null
694
777
  // but there is a message property on errorEvent
@@ -696,170 +779,249 @@ window.__supervisor__ = (() => {
696
779
  reportedBy: "window_error_event",
697
780
  url: filename,
698
781
  line: lineno,
699
- column: colno,
700
- })
701
- supervisor.reportException(exception)
702
- })
703
- window.addEventListener("unhandledrejection", (event) => {
782
+ column: colno
783
+ });
784
+ supervisor.reportException(exception);
785
+ });
786
+ window.addEventListener("unhandledrejection", event => {
704
787
  if (event.defaultPrevented) {
705
- return
788
+ return;
706
789
  }
790
+
707
791
  const exception = supervisor.createException({
708
792
  reason: event.reason,
709
- reportedBy: "window_unhandledrejection_event",
710
- })
711
- supervisor.reportException(exception)
712
- })
713
- }
793
+ reportedBy: "window_unhandledrejection_event"
794
+ });
795
+ supervisor.reportException(exception);
796
+ });
797
+ };
714
798
 
715
799
  supervisor.setup = ({
716
800
  rootDirectoryUrl,
717
801
  logs,
718
- measurePerf,
719
802
  errorOverlay,
720
803
  errorBaseUrl,
721
- openInEditor,
804
+ openInEditor
722
805
  }) => {
723
806
  supervisor.setupReportException({
724
807
  rootDirectoryUrl,
725
808
  errorOverlay,
726
809
  errorBaseUrl,
727
- openInEditor,
728
- })
810
+ openInEditor
811
+ });
812
+ const supervisedScripts = [];
813
+ const pendingPromises = []; // respect execution order
814
+ // - wait for classic scripts to be done (non async)
815
+ // - wait module script previous execution (non async)
816
+ // see https://gist.github.com/jakub-g/385ee6b41085303a53ad92c7c8afd7a6#typemodule-vs-non-module-typetextjavascript-vs-script-nomodule
817
+
818
+ const executionQueue = [];
819
+ let executing = false;
820
+
821
+ const addToExecutionQueue = async execution => {
822
+ if (execution.async) {
823
+ execution.start();
824
+ return;
825
+ }
729
826
 
730
- const supervisedScripts = []
731
- const executionPromises = []
732
- supervisor.createExecution = ({ type, src, async, execute }) => {
827
+ if (executing) {
828
+ executionQueue.push(execution);
829
+ return;
830
+ }
831
+
832
+ startThenDequeue(execution);
833
+ };
834
+
835
+ const startThenDequeue = async execution => {
836
+ executing = true;
837
+ const promise = execution.start();
838
+ await promise;
839
+ executing = false;
840
+
841
+ if (executionQueue.length) {
842
+ const nextExecution = executionQueue.shift();
843
+ startThenDequeue(nextExecution);
844
+ }
845
+ };
846
+
847
+ supervisor.addExecution = ({
848
+ type,
849
+ src,
850
+ async,
851
+ execute
852
+ }) => {
733
853
  const execution = {
734
854
  type,
735
855
  src,
736
856
  async,
737
- execute,
738
- }
857
+ execute
858
+ };
859
+
739
860
  execution.start = () => {
740
- return superviseExecution(execution, { isReload: false })
741
- }
861
+ return superviseExecution(execution, {
862
+ isReload: false
863
+ });
864
+ };
865
+
742
866
  execution.reload = () => {
743
- return superviseExecution(execution, { isReload: true })
744
- }
745
- supervisedScripts.push(execution)
746
- return execution
747
- }
748
- const superviseExecution = async (execution, { isReload }) => {
867
+ return superviseExecution(execution, {
868
+ isReload: true
869
+ });
870
+ };
871
+
872
+ supervisedScripts.push(execution);
873
+ return addToExecutionQueue(execution);
874
+ };
875
+
876
+ const superviseExecution = async (execution, {
877
+ isReload
878
+ }) => {
749
879
  if (logs) {
750
- console.group(`[jsenv] loading ${execution.type} ${execution.src}`)
751
- }
752
- if (measurePerf) {
753
- performance.mark(`execution_start`)
880
+ console.group(`[jsenv] loading ${execution.type} ${execution.src}`);
754
881
  }
882
+
755
883
  const executionResult = {
756
884
  status: "pending",
885
+ loadDuration: null,
886
+ executionDuration: null,
887
+ duration: null,
757
888
  exception: null,
758
889
  namespace: null,
759
- coverage: null,
760
- }
761
- executionResults[execution.src] = executionResult
762
- let resolveExecutionPromise
763
- const promise = new Promise((resolve) => {
764
- resolveExecutionPromise = () => {
765
- const index = executionPromises.indexOf(promise)
766
- if (index > -1) {
767
- executionPromises.splice(index, 1)
768
- }
769
- resolve()
890
+ coverage: null
891
+ };
892
+ executionResults[execution.src] = executionResult;
893
+
894
+ const monitorScriptLoad = () => {
895
+ const loadStartTime = Date.now();
896
+ let resolveScriptLoadPromise;
897
+ const scriptLoadPromise = new Promise(resolve => {
898
+ resolveScriptLoadPromise = resolve;
899
+ });
900
+ pendingPromises.push(scriptLoadPromise);
901
+ return () => {
902
+ const loadEndTime = Date.now();
903
+ executionResult.loadDuration = loadEndTime - loadStartTime;
904
+ resolveScriptLoadPromise();
905
+ };
906
+ };
907
+
908
+ const monitorScriptExecution = () => {
909
+ const executionStartTime = Date.now();
910
+ let resolveExecutionPromise;
911
+ const executionPromise = new Promise(resolve => {
912
+ resolveExecutionPromise = resolve;
913
+ });
914
+ pendingPromises.push(executionPromise);
915
+ return () => {
916
+ executionResult.coverage = window.__coverage__;
917
+ executionResult.executionDuration = Date.now() - executionStartTime;
918
+ executionResult.duration = executionResult.loadDuration + executionResult.executionDuration;
919
+ resolveExecutionPromise();
920
+ };
921
+ };
922
+
923
+ const onError = e => {
924
+ executionResult.status = "errored";
925
+ const exception = supervisor.createException({
926
+ reason: e
927
+ });
928
+
929
+ if (exception.needsReport) {
930
+ supervisor.reportException(exception);
770
931
  }
771
- })
772
- promise.execution = execution
773
- executionPromises.push(promise)
932
+
933
+ executionResult.exception = exception;
934
+ };
935
+
936
+ const scriptLoadDone = monitorScriptLoad();
937
+
774
938
  try {
775
- const result = await execution.execute({ isReload })
776
- if (measurePerf) {
777
- performance.measure(`execution`, `execution_start`)
778
- }
779
- executionResult.status = "completed"
780
- executionResult.namespace = result
781
- executionResult.coverage = window.__coverage__
939
+ const result = await execution.execute({
940
+ isReload
941
+ });
942
+
782
943
  if (logs) {
783
- console.log(`${execution.type} load ended`)
784
- console.groupEnd()
785
- }
786
- resolveExecutionPromise()
787
- } catch (e) {
788
- if (measurePerf) {
789
- performance.measure(`execution`, `execution_start`)
944
+ console.log(`${execution.type} load ended`);
945
+ console.groupEnd();
790
946
  }
791
- executionResult.status = "errored"
792
- const exception = supervisor.createException({
793
- reason: e,
794
- })
795
- if (exception.needsReport) {
796
- supervisor.reportException(exception)
947
+
948
+ executionResult.status = "loaded";
949
+ scriptLoadDone();
950
+ const scriptExecutionDone = monitorScriptExecution();
951
+
952
+ if (execution.type === "js_classic") {
953
+ executionResult.status = "completed";
954
+ scriptExecutionDone();
955
+ } else if (execution.type === "js_module") {
956
+ result.executionPromise.then(namespace => {
957
+ executionResult.status = "completed";
958
+ executionResult.namespace = namespace;
959
+ scriptExecutionDone();
960
+ }, e => {
961
+ onError(e);
962
+ scriptExecutionDone();
963
+ });
797
964
  }
798
- executionResult.exception = exception
799
- executionResult.coverage = window.__coverage__
965
+ } catch (e) {
800
966
  if (logs) {
801
- console.groupEnd()
967
+ console.groupEnd();
802
968
  }
803
- resolveExecutionPromise()
804
- }
805
- }
806
969
 
807
- // respect execution order
808
- // - wait for classic scripts to be done (non async)
809
- // - wait module script previous execution (non async)
810
- // see https://gist.github.com/jakub-g/385ee6b41085303a53ad92c7c8afd7a6#typemodule-vs-non-module-typetextjavascript-vs-script-nomodule
811
- supervisor.getPreviousExecutionDonePromise = async () => {
812
- const previousNonAsyncExecutions = executionPromises.filter(
813
- (promise) => !promise.execution.async,
814
- )
815
- await Promise.all(previousNonAsyncExecutions)
816
- }
817
- supervisor.superviseScript = async ({ src, async }) => {
818
- const { currentScript } = document
819
- const parentNode = currentScript.parentNode
820
- if (!async) {
821
- await supervisor.getPreviousExecutionDonePromise()
970
+ onError(e);
971
+ scriptLoadDone();
822
972
  }
823
- let nodeToReplace
824
- let currentScriptClone
825
- const execution = supervisor.createExecution({
973
+ };
974
+
975
+ supervisor.superviseScript = async ({
976
+ src,
977
+ async
978
+ }) => {
979
+ const {
980
+ currentScript
981
+ } = document;
982
+ const parentNode = currentScript.parentNode;
983
+ let nodeToReplace;
984
+ let currentScriptClone;
985
+ return supervisor.addExecution({
826
986
  src,
827
987
  type: "js_classic",
828
988
  async,
829
- execute: async ({ isReload }) => {
830
- const urlObject = new URL(src, window.location)
989
+ execute: async ({
990
+ isReload
991
+ }) => {
992
+ const urlObject = new URL(src, window.location);
831
993
  const loadPromise = new Promise((resolve, reject) => {
832
994
  // do not use script.cloneNode()
833
995
  // bcause https://stackoverflow.com/questions/28771542/why-dont-clonenode-script-tags-execute
834
- currentScriptClone = document.createElement("script")
835
- // browsers set async by default when creating script(s)
996
+ currentScriptClone = document.createElement("script"); // browsers set async by default when creating script(s)
836
997
  // we want an exact copy to preserves how code is executed
837
- currentScriptClone.async = false
838
- Array.from(currentScript.attributes).forEach((attribute) => {
839
- currentScriptClone.setAttribute(
840
- attribute.nodeName,
841
- attribute.nodeValue,
842
- )
843
- })
998
+
999
+ currentScriptClone.async = false;
1000
+ Array.from(currentScript.attributes).forEach(attribute => {
1001
+ currentScriptClone.setAttribute(attribute.nodeName, attribute.nodeValue);
1002
+ });
1003
+
844
1004
  if (isReload) {
845
- urlObject.searchParams.set("hmr", Date.now())
846
- nodeToReplace = currentScriptClone
847
- currentScriptClone.src = urlObject.href
1005
+ urlObject.searchParams.set("hmr", Date.now());
1006
+ nodeToReplace = currentScriptClone;
1007
+ currentScriptClone.src = urlObject.href;
848
1008
  } else {
849
- currentScriptClone.removeAttribute("jsenv-plugin-owner")
850
- currentScriptClone.removeAttribute("jsenv-plugin-action")
851
- currentScriptClone.removeAttribute("inlined-from-src")
852
- currentScriptClone.removeAttribute("original-position")
853
- currentScriptClone.removeAttribute("original-src-position")
854
- nodeToReplace = currentScript
855
- currentScriptClone.src = src
1009
+ currentScriptClone.removeAttribute("jsenv-plugin-owner");
1010
+ currentScriptClone.removeAttribute("jsenv-plugin-action");
1011
+ currentScriptClone.removeAttribute("inlined-from-src");
1012
+ currentScriptClone.removeAttribute("original-position");
1013
+ currentScriptClone.removeAttribute("original-src-position");
1014
+ nodeToReplace = currentScript;
1015
+ currentScriptClone.src = src;
856
1016
  }
857
- currentScriptClone.addEventListener("error", reject)
858
- currentScriptClone.addEventListener("load", resolve)
859
- parentNode.replaceChild(currentScriptClone, nodeToReplace)
860
- })
1017
+
1018
+ currentScriptClone.addEventListener("error", reject);
1019
+ currentScriptClone.addEventListener("load", resolve);
1020
+ parentNode.replaceChild(currentScriptClone, nodeToReplace);
1021
+ });
1022
+
861
1023
  try {
862
- await loadPromise
1024
+ await loadPromise;
863
1025
  } catch (e) {
864
1026
  // eslint-disable-next-line no-throw-literal
865
1027
  throw {
@@ -867,67 +1029,73 @@ window.__supervisor__ = (() => {
867
1029
  reportedBy: "script_error_event",
868
1030
  url: urlObject.href,
869
1031
  // window.error won't be dispatched for this error
870
- needsReport: true,
871
- }
872
- }
873
- },
874
- })
875
- return execution.start()
876
- }
877
- supervisor.reloadSupervisedScript = ({ type, src }) => {
878
- const supervisedScript = supervisedScripts.find(
879
- (supervisedScriptCandidate) => {
880
- if (type && supervisedScriptCandidate.type !== type) {
881
- return false
1032
+ needsReport: true
1033
+ };
882
1034
  }
883
- if (supervisedScriptCandidate.src !== src) {
884
- return false
885
- }
886
- return true
887
- },
888
- )
1035
+ }
1036
+ });
1037
+ };
1038
+
1039
+ supervisor.reloadSupervisedScript = ({
1040
+ type,
1041
+ src
1042
+ }) => {
1043
+ const supervisedScript = supervisedScripts.find(supervisedScriptCandidate => {
1044
+ if (type && supervisedScriptCandidate.type !== type) {
1045
+ return false;
1046
+ }
1047
+
1048
+ if (supervisedScriptCandidate.src !== src) {
1049
+ return false;
1050
+ }
1051
+
1052
+ return true;
1053
+ });
1054
+
889
1055
  if (supervisedScript) {
890
- supervisedScript.reload()
1056
+ supervisedScript.reload();
891
1057
  }
892
- }
893
- supervisor.collectScriptResults = async () => {
1058
+ };
1059
+
1060
+ supervisor.getDocumentExecutionResult = async () => {
894
1061
  // just to be super safe and ensure any <script type="module"> got a chance to execute
895
- const scriptTypeModuleLoaded = new Promise((resolve) => {
896
- const scriptTypeModule = document.createElement("script")
897
- scriptTypeModule.type = "module"
898
- scriptTypeModule.innerText =
899
- "window.__supervisor__.scriptModuleCallback()"
900
- window.__supervisor__.scriptModuleCallback = () => {
901
- scriptTypeModule.parentNode.removeChild(scriptTypeModule)
902
- resolve()
1062
+ const documentReadyPromise = new Promise(resolve => {
1063
+ if (document.readyState === "complete") {
1064
+ resolve();
1065
+ return;
903
1066
  }
904
- document.body.appendChild(scriptTypeModule)
905
- })
906
- await scriptTypeModuleLoaded
907
-
908
- const waitPendingExecutions = async () => {
909
- if (executionPromises.length) {
910
- await Promise.all(executionPromises)
911
- await waitPendingExecutions()
1067
+
1068
+ const loadCallback = () => {
1069
+ window.removeEventListener("load", loadCallback);
1070
+ resolve();
1071
+ };
1072
+
1073
+ window.addEventListener("load", loadCallback);
1074
+ });
1075
+ await documentReadyPromise;
1076
+
1077
+ const waitScriptExecutions = async () => {
1078
+ const numberOfPromises = pendingPromises.length;
1079
+ await Promise.all(pendingPromises); // new scripts added while the other where executing
1080
+ // (should happen only on webkit where
1081
+ // script might be added after window load event)
1082
+
1083
+ await new Promise(resolve => setTimeout(resolve));
1084
+
1085
+ if (pendingPromises.length > numberOfPromises) {
1086
+ await waitScriptExecutions();
912
1087
  }
913
- }
914
- await waitPendingExecutions()
1088
+ };
1089
+
1090
+ await waitScriptExecutions();
915
1091
  return {
916
1092
  status: "completed",
917
1093
  executionResults,
918
- startTime: getNavigationStartTime(),
919
- endTime: Date.now(),
920
- }
921
- }
922
-
923
- const getNavigationStartTime = () => {
924
- try {
925
- return window.performance.timing.navigationStart
926
- } catch (e) {
927
- return Date.now()
928
- }
929
- }
930
- }
1094
+ startTime: navigationStartTime,
1095
+ endTime: Date.now()
1096
+ };
1097
+ };
1098
+ };
931
1099
 
932
- return supervisor
933
- })()
1100
+ return supervisor;
1101
+ })();