@jsenv/core 27.3.4 → 27.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/js/autoreload.js +359 -0
  2. package/dist/js/execute_using_dynamic_import.js +1 -1
  3. package/dist/js/html_supervisor_installer.js +524 -147
  4. package/dist/js/html_supervisor_setup.js +3 -4
  5. package/dist/js/new_stylesheet.js +26 -58
  6. package/dist/js/server_events_client.js +307 -0
  7. package/dist/main.js +7709 -7324
  8. package/package.json +15 -15
  9. package/{README.md → readme.md} +18 -7
  10. package/src/build/build.js +16 -18
  11. package/src/build/start_build_server.js +24 -28
  12. package/src/dev/start_dev_server.js +34 -96
  13. package/src/execute/execute.js +17 -35
  14. package/src/omega/errors.js +43 -9
  15. package/src/omega/kitchen.js +42 -25
  16. package/src/omega/omega_server.js +96 -74
  17. package/src/omega/server/file_service.js +256 -28
  18. package/src/omega/url_graph.js +33 -20
  19. package/src/plugins/autoreload/client/autoreload.js +201 -0
  20. package/src/plugins/autoreload/{dev_sse/client → client}/autoreload_preference.js +0 -0
  21. package/src/plugins/autoreload/{dev_sse/client → client}/reload.js +29 -10
  22. package/src/plugins/autoreload/{dev_sse/client → client}/url_helpers.js +0 -0
  23. package/src/plugins/autoreload/jsenv_plugin_autoreload.js +4 -8
  24. package/src/plugins/autoreload/{dev_sse/jsenv_plugin_dev_sse_client.js → jsenv_plugin_autoreload_client.js} +8 -8
  25. package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +196 -0
  26. package/src/{dev/plugins → plugins}/explorer/client/explorer.html +0 -0
  27. package/src/{dev/plugins → plugins}/explorer/client/jsenv.png +0 -0
  28. package/src/{dev/plugins → plugins}/explorer/jsenv_plugin_explorer.js +1 -3
  29. package/src/plugins/html_supervisor/client/error_overlay.js +401 -0
  30. package/src/plugins/html_supervisor/client/html_supervisor_installer.js +138 -23
  31. package/src/plugins/html_supervisor/client/html_supervisor_setup.js +3 -4
  32. package/src/plugins/html_supervisor/jsenv_plugin_html_supervisor.js +55 -23
  33. package/src/plugins/inline/jsenv_plugin_html_inline_content.js +97 -117
  34. package/src/plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +66 -58
  35. package/src/plugins/plugin_controller.js +102 -67
  36. package/src/plugins/plugins.js +10 -10
  37. package/src/{helpers/event_source/event_source.js → plugins/server_events/client/event_source_connection.js} +125 -33
  38. package/src/plugins/server_events/client/server_events_client.js +17 -0
  39. package/src/plugins/server_events/jsenv_plugin_server_events_client_injection.js +48 -0
  40. package/src/plugins/server_events/server_events_dispatcher.js +69 -0
  41. package/src/{dev/plugins → plugins}/toolbar/client/animation/toolbar_animation.js +0 -0
  42. package/src/{dev/plugins → plugins}/toolbar/client/eventsource/eventsource.css +0 -0
  43. package/src/{dev/plugins → plugins}/toolbar/client/eventsource/toolbar_eventsource.js +0 -0
  44. package/src/{dev/plugins → plugins}/toolbar/client/execution/execution.css +0 -0
  45. package/src/{dev/plugins → plugins}/toolbar/client/execution/toolbar_execution.js +0 -0
  46. package/src/{dev/plugins → plugins}/toolbar/client/focus/focus.css +0 -0
  47. package/src/{dev/plugins → plugins}/toolbar/client/focus/toolbar_focus.js +0 -0
  48. package/src/{dev/plugins → plugins}/toolbar/client/jsenv_logo.svg +0 -0
  49. package/src/{dev/plugins → plugins}/toolbar/client/notification/toolbar_notification.js +0 -0
  50. package/src/{dev/plugins → plugins}/toolbar/client/responsive/overflow_menu.css +0 -0
  51. package/src/{dev/plugins → plugins}/toolbar/client/responsive/toolbar_responsive.js +0 -0
  52. package/src/{dev/plugins → plugins}/toolbar/client/settings/settings.css +0 -0
  53. package/src/{dev/plugins → plugins}/toolbar/client/settings/toolbar_settings.js +0 -0
  54. package/src/{dev/plugins → plugins}/toolbar/client/theme/jsenv_theme.css +0 -0
  55. package/src/{dev/plugins → plugins}/toolbar/client/theme/light_theme.css +0 -0
  56. package/src/{dev/plugins → plugins}/toolbar/client/theme/toolbar_theme.js +0 -0
  57. package/src/{dev/plugins → plugins}/toolbar/client/toolbar.html +0 -0
  58. package/src/{dev/plugins → plugins}/toolbar/client/toolbar_injector.js +0 -0
  59. package/src/{dev/plugins → plugins}/toolbar/client/toolbar_main.css +0 -0
  60. package/src/{dev/plugins → plugins}/toolbar/client/toolbar_main.js +0 -0
  61. package/src/{dev/plugins → plugins}/toolbar/client/tooltip/tooltip.css +0 -0
  62. package/src/{dev/plugins → plugins}/toolbar/client/tooltip/tooltip.js +0 -0
  63. package/src/{dev/plugins → plugins}/toolbar/client/util/animation.js +0 -0
  64. package/src/{dev/plugins → plugins}/toolbar/client/util/dom.js +0 -0
  65. package/src/{dev/plugins → plugins}/toolbar/client/util/fetch_using_xhr.js +0 -0
  66. package/src/{dev/plugins → plugins}/toolbar/client/util/fetching.js +0 -0
  67. package/src/{dev/plugins → plugins}/toolbar/client/util/iframe_to_parent_href.js +0 -0
  68. package/src/{dev/plugins → plugins}/toolbar/client/util/jsenv_logger.js +0 -0
  69. package/src/{dev/plugins → plugins}/toolbar/client/util/preferences.js +0 -0
  70. package/src/{dev/plugins → plugins}/toolbar/client/util/responsive.js +0 -0
  71. package/src/{dev/plugins → plugins}/toolbar/client/util/util.js +0 -0
  72. package/src/{dev/plugins → plugins}/toolbar/client/variant/variant.js +0 -0
  73. package/src/{dev/plugins → plugins}/toolbar/jsenv_plugin_toolbar.js +0 -0
  74. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_html.js +4 -3
  75. package/src/plugins/transpilation/babel/new_stylesheet/client/new_stylesheet.js +25 -55
  76. package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +44 -24
  77. package/src/plugins/transpilation/jsenv_plugin_transpilation.js +6 -1
  78. package/src/plugins/url_analysis/html/html_urls.js +8 -8
  79. package/src/plugins/url_analysis/jsenv_plugin_url_analysis.js +3 -1
  80. package/src/test/execute_plan.js +36 -54
  81. package/src/test/execute_test_plan.js +2 -2
  82. package/src/test/logs_file_execution.js +60 -27
  83. package/src/test/logs_file_execution.test.mjs +41 -0
  84. package/dist/js/event_source_client.js +0 -528
  85. package/src/helpers/event_source/sse_service.js +0 -53
  86. package/src/plugins/autoreload/dev_sse/client/event_source_client.js +0 -193
  87. package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_server.js +0 -203
  88. package/src/plugins/html_supervisor/client/error_in_document.js +0 -198
@@ -0,0 +1,359 @@
1
+ import { urlHotMetas } from "./import_meta_hot.js";
2
+ import { parseSrcSet, stringifySrcSet } from "@jsenv/ast/src/html/html_src_set.js";
3
+
4
+ const isAutoreloadEnabled = () => {
5
+ const value = window.localStorage.getItem("autoreload");
6
+
7
+ if (value === "0") {
8
+ return false;
9
+ }
10
+
11
+ return true;
12
+ };
13
+ const setAutoreloadPreference = value => {
14
+ window.localStorage.setItem("autoreload", value ? "1" : "0");
15
+ };
16
+
17
+ const compareTwoUrlPaths = (url, otherUrl) => {
18
+ if (url === otherUrl) {
19
+ return true;
20
+ }
21
+
22
+ const urlObject = new URL(url);
23
+ const otherUrlObject = new URL(otherUrl);
24
+ return urlObject.origin === otherUrlObject.origin && urlObject.pathname === otherUrlObject.pathname;
25
+ };
26
+ const injectQuery = (url, query) => {
27
+ const urlObject = new URL(url);
28
+ const {
29
+ searchParams
30
+ } = urlObject;
31
+ Object.keys(query).forEach(key => {
32
+ searchParams.set(key, query[key]);
33
+ });
34
+ return String(urlObject);
35
+ };
36
+
37
+ const reloadHtmlPage = () => {
38
+ window.location.reload(true);
39
+ }; // This function can consider everything as hot reloadable:
40
+ // - no need to check [hot-accept]and [hot-decline] attributes for instance
41
+ // This is because if something should full reload, we receive "full_reload"
42
+ // from server and this function is not called
43
+
44
+ const getDOMNodesUsingUrl = urlToReload => {
45
+ const nodes = [];
46
+
47
+ const shouldReloadUrl = urlCandidate => {
48
+ return compareTwoUrlPaths(urlCandidate, urlToReload);
49
+ };
50
+
51
+ const visitNodeAttributeAsUrl = (node, attributeName) => {
52
+ let attribute = node[attributeName];
53
+
54
+ if (!attribute) {
55
+ return;
56
+ }
57
+
58
+ if (SVGAnimatedString && attribute instanceof SVGAnimatedString) {
59
+ attribute = attribute.animVal;
60
+ }
61
+
62
+ if (!shouldReloadUrl(attribute)) {
63
+ return;
64
+ }
65
+
66
+ nodes.push({
67
+ node,
68
+ reload: () => {
69
+ node[attributeName] = injectQuery(attribute, {
70
+ hmr: Date.now()
71
+ });
72
+ }
73
+ });
74
+ };
75
+
76
+ Array.from(document.querySelectorAll(`link[rel="stylesheet"]`)).forEach(link => {
77
+ visitNodeAttributeAsUrl(link, "href");
78
+ });
79
+ Array.from(document.querySelectorAll(`link[rel="icon"]`)).forEach(link => {
80
+ visitNodeAttributeAsUrl(link, "href");
81
+ });
82
+ Array.from(document.querySelectorAll("script")).forEach(script => {
83
+ visitNodeAttributeAsUrl(script, "src");
84
+ const generatedFromSrc = script.getAttribute("generated-from-src");
85
+
86
+ if (generatedFromSrc) {
87
+ const generatedFromUrl = new URL(generatedFromSrc, window.location.origin).href;
88
+
89
+ if (shouldReloadUrl(generatedFromUrl)) {
90
+ nodes.push({
91
+ node: script,
92
+ reload: () => window.__html_supervisor__.reloadSupervisedScript({
93
+ type: script.type,
94
+ src: generatedFromSrc
95
+ })
96
+ });
97
+ }
98
+ }
99
+ }); // There is no real need to update a.href because the ressource will be fetched when clicked.
100
+ // But in a scenario where the ressource was already visited and is in browser cache, adding
101
+ // the dynamic query param ensure the cache is invalidated
102
+
103
+ Array.from(document.querySelectorAll("a")).forEach(a => {
104
+ visitNodeAttributeAsUrl(a, "href");
105
+ }); // About iframes:
106
+ // - By default iframe itself and everything inside trigger a parent page full-reload
107
+ // - Adding [hot-accept] on the iframe means parent page won't reload when iframe full/hot reload
108
+ // In that case and if there is code in the iframe and parent doing post message communication:
109
+ // you must put import.meta.hot.decline() for code involved in communication.
110
+ // (both in parent and iframe)
111
+
112
+ Array.from(document.querySelectorAll("img")).forEach(img => {
113
+ visitNodeAttributeAsUrl(img, "src");
114
+ const srcset = img.srcset;
115
+
116
+ if (srcset) {
117
+ const srcCandidates = parseSrcSet(srcset);
118
+ srcCandidates.forEach(srcCandidate => {
119
+ const url = new URL(srcCandidate.specifier, `${window.location.href}`);
120
+
121
+ if (shouldReloadUrl(url)) {
122
+ srcCandidate.specifier = injectQuery(url, {
123
+ hmr: Date.now()
124
+ });
125
+ }
126
+ });
127
+ nodes.push({
128
+ node: img,
129
+ reload: () => {
130
+ img.srcset = stringifySrcSet(srcCandidates);
131
+ }
132
+ });
133
+ }
134
+ });
135
+ Array.from(document.querySelectorAll("source")).forEach(source => {
136
+ visitNodeAttributeAsUrl(source, "src");
137
+ }); // svg image tag
138
+
139
+ Array.from(document.querySelectorAll("image")).forEach(image => {
140
+ visitNodeAttributeAsUrl(image, "href");
141
+ }); // svg use
142
+
143
+ Array.from(document.querySelectorAll("use")).forEach(use => {
144
+ visitNodeAttributeAsUrl(use, "href");
145
+ });
146
+ return nodes;
147
+ };
148
+ const reloadJsImport = async url => {
149
+ const urlWithHmr = injectQuery(url, {
150
+ hmr: Date.now()
151
+ });
152
+ const namespace = await import(urlWithHmr);
153
+ return namespace;
154
+ };
155
+
156
+ const reloader = {
157
+ urlHotMetas,
158
+ isAutoreloadEnabled,
159
+ setAutoreloadPreference,
160
+ status: "idle",
161
+ onstatuschange: () => {},
162
+ setStatus: status => {
163
+ reloader.status = status;
164
+ reloader.onstatuschange();
165
+ },
166
+ messages: [],
167
+ addMessage: reloadMessage => {
168
+ reloader.messages.push(reloadMessage);
169
+
170
+ if (isAutoreloadEnabled()) {
171
+ reloader.reload();
172
+ } else {
173
+ reloader.setStatus("can_reload");
174
+ }
175
+ },
176
+ reload: () => {
177
+ const someEffectIsFullReload = reloader.messages.some(reloadMessage => reloadMessage.type === "full");
178
+
179
+ if (someEffectIsFullReload) {
180
+ reloadHtmlPage();
181
+ return;
182
+ }
183
+
184
+ reloader.setStatus("reloading");
185
+
186
+ const onApplied = reloadMessage => {
187
+ const index = reloader.messages.indexOf(reloadMessage);
188
+ reloader.messages.splice(index, 1);
189
+
190
+ if (reloader.messages.length === 0) {
191
+ reloader.setStatus("idle");
192
+ }
193
+ };
194
+
195
+ const setReloadMessagePromise = (reloadMessage, promise) => {
196
+ promise.then(() => {
197
+ onApplied(reloadMessage);
198
+ }, e => {
199
+ reloader.setStatus("failed");
200
+
201
+ if (typeof window.reportError === "function") {
202
+ window.reportError(e);
203
+ } else {
204
+ console.error(e);
205
+ }
206
+
207
+ console.error(`[jsenv] Hot reload failed after ${reloadMessage.reason}.
208
+ This could be due to syntax errors or importing non-existent modules (see errors in console)`);
209
+ });
210
+ };
211
+
212
+ reloader.messages.forEach(reloadMessage => {
213
+ if (reloadMessage.type === "hot") {
214
+ const promise = addToHotQueue(() => {
215
+ return applyHotReload(reloadMessage);
216
+ });
217
+ setReloadMessagePromise(reloadMessage, promise);
218
+ } else {
219
+ setReloadMessagePromise(reloadMessage, Promise.resolve());
220
+ }
221
+ });
222
+ }
223
+ };
224
+ let pendingCallbacks = [];
225
+ let running = false;
226
+
227
+ const addToHotQueue = async callback => {
228
+ pendingCallbacks.push(callback);
229
+ dequeue();
230
+ };
231
+
232
+ const dequeue = async () => {
233
+ if (running) {
234
+ return;
235
+ }
236
+
237
+ const callbacks = pendingCallbacks.slice();
238
+ pendingCallbacks = [];
239
+ running = true;
240
+
241
+ try {
242
+ await callbacks.reduce(async (previous, callback) => {
243
+ await previous;
244
+ await callback();
245
+ }, Promise.resolve());
246
+ } finally {
247
+ running = false;
248
+
249
+ if (pendingCallbacks.length) {
250
+ dequeue();
251
+ }
252
+ }
253
+ };
254
+
255
+ const applyHotReload = async ({
256
+ hotInstructions
257
+ }) => {
258
+ await hotInstructions.reduce(async (previous, {
259
+ type,
260
+ boundary,
261
+ acceptedBy
262
+ }) => {
263
+ await previous;
264
+ const urlToFetch = new URL(boundary, `${window.location.origin}/`).href;
265
+ const urlHotMeta = urlHotMetas[urlToFetch]; // there is no url hot meta when:
266
+ // - code was not executed (code splitting with dynamic import)
267
+ // - import.meta.hot.accept() is not called (happens for HTML and CSS)
268
+
269
+ if (type === "prune") {
270
+ if (urlHotMeta) {
271
+ delete urlHotMetas[urlToFetch];
272
+
273
+ if (urlHotMeta.disposeCallback) {
274
+ console.groupCollapsed(`[jsenv] cleanup ${boundary} (previously used in ${acceptedBy})`);
275
+ console.log(`call dispose callback`);
276
+ await urlHotMeta.disposeCallback();
277
+ console.groupEnd();
278
+ }
279
+ }
280
+
281
+ return null;
282
+ }
283
+
284
+ if (acceptedBy === boundary) {
285
+ console.groupCollapsed(`[jsenv] hot reloading ${boundary}`);
286
+ } else {
287
+ console.groupCollapsed(`[jsenv] hot reloading ${acceptedBy} usage in ${boundary}`);
288
+ }
289
+
290
+ if (type === "js_module") {
291
+ if (!urlHotMeta) {
292
+ // code was not executed, no need to re-execute it
293
+ return null;
294
+ }
295
+
296
+ if (urlHotMeta.disposeCallback) {
297
+ console.log(`call dispose callback`);
298
+ await urlHotMeta.disposeCallback();
299
+ }
300
+
301
+ console.log(`importing js module`);
302
+ const namespace = await reloadJsImport(urlToFetch);
303
+
304
+ if (urlHotMeta.acceptCallback) {
305
+ await urlHotMeta.acceptCallback(namespace);
306
+ }
307
+
308
+ console.log(`js module import done`);
309
+ console.groupEnd();
310
+ return namespace;
311
+ }
312
+
313
+ if (type === "html") {
314
+ if (!compareTwoUrlPaths(urlToFetch, window.location.href)) {
315
+ // we are not in that HTML page
316
+ return null;
317
+ }
318
+
319
+ const urlToReload = new URL(acceptedBy, `${window.location.origin}/`).href;
320
+ const domNodesUsingUrl = getDOMNodesUsingUrl(urlToReload);
321
+ const domNodesCount = domNodesUsingUrl.length;
322
+
323
+ if (domNodesCount === 0) {
324
+ console.log(`no dom node using ${acceptedBy}`);
325
+ } else if (domNodesCount === 1) {
326
+ console.log(`reloading`, domNodesUsingUrl[0].node);
327
+ domNodesUsingUrl[0].reload();
328
+ } else {
329
+ console.log(`reloading ${domNodesCount} nodes using ${acceptedBy}`);
330
+ domNodesUsingUrl.forEach(domNodesUsingUrl => {
331
+ domNodesUsingUrl.reload();
332
+ });
333
+ }
334
+
335
+ console.groupEnd();
336
+ return null;
337
+ }
338
+
339
+ console.warn(`unknown update type: "${type}"`);
340
+ return null;
341
+ }, Promise.resolve());
342
+ };
343
+
344
+ window.__reloader__ = reloader;
345
+
346
+ window.__server_events__.addEventCallbacks({
347
+ reload: ({
348
+ data
349
+ }) => {
350
+ const reloadMessage = JSON.parse(data);
351
+ reloader.addMessage(reloadMessage);
352
+ }
353
+ }); // const findHotMetaUrl = (originalFileRelativeUrl) => {
354
+ // return Object.keys(urlHotMetas).find((compileUrl) => {
355
+ // return (
356
+ // parseCompiledUrl(compileUrl).fileRelativeUrl === originalFileRelativeUrl
357
+ // )
358
+ // })
359
+ // }
@@ -1,6 +1,6 @@
1
1
  import { writeFileSync } from "node:fs";
2
2
  import { Session } from "node:inspector";
3
- import { performance, PerformanceObserver } from "node:perf_hooks";
3
+ import { PerformanceObserver, performance } from "node:perf_hooks";
4
4
 
5
5
  /*
6
6
  * Calling Profiler.startPreciseCoverage DO NOT propagate to