@jsenv/core 39.0.4 → 39.1.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 +152 -146
- package/dist/js/ws.js +246 -61
- package/dist/jsenv_core.js +164 -218
- package/package.json +7 -7
- package/src/build/version_mappings_injection.js +1 -1
- package/src/dev/start_dev_server.js +1 -2
- package/src/helpers/command/command.js +2 -2
- package/src/kitchen/kitchen.js +2 -0
- package/src/kitchen/prepend_content.js +1 -1
- package/src/plugins/autoreload/client/autoreload.js +155 -6
- package/src/plugins/autoreload/jsenv_plugin_autoreload_client.js +12 -24
- package/src/plugins/clean_html/jsenv_plugin_clean_html.js +2 -1
- package/src/plugins/html_syntax_error_fallback/jsenv_plugin_html_syntax_error_fallback.js +93 -0
- package/src/plugins/injections/internal/inject_globals.js +5 -14
- package/src/plugins/plugin_controller.js +33 -96
- package/src/plugins/reference_analysis/html/jsenv_plugin_html_reference_analysis.js +4 -84
- package/src/plugins/ribbon/jsenv_plugin_ribbon.js +13 -23
- package/src/plugins/server_events/jsenv_plugin_server_events_client_injection.js +17 -13
- package/src/plugins/autoreload/client/reload.js +0 -136
- package/src/plugins/autoreload/client/url_helpers.js +0 -23
- /package/src/plugins/{reference_analysis/html → html_syntax_error_fallback/client}/html_syntax_error.html +0 -0
package/dist/js/autoreload.js
CHANGED
|
@@ -1,150 +1,5 @@
|
|
|
1
|
-
import { urlHotMetas } from "./import_meta_hot.js";
|
|
2
1
|
import { parseSrcSet, stringifySrcSet } from "@jsenv/ast/src/html/html_src_set.js";
|
|
3
|
-
|
|
4
|
-
const compareTwoUrlPaths = (url, otherUrl) => {
|
|
5
|
-
if (url === otherUrl) {
|
|
6
|
-
return true;
|
|
7
|
-
}
|
|
8
|
-
const urlObject = new URL(url);
|
|
9
|
-
const otherUrlObject = new URL(otherUrl);
|
|
10
|
-
if (urlObject.origin !== otherUrlObject.origin) {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
if (urlObject.pathname !== otherUrlObject.pathname) {
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
return true;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const injectQuery = (url, query) => {
|
|
20
|
-
const urlObject = new URL(url);
|
|
21
|
-
const { searchParams } = urlObject;
|
|
22
|
-
Object.keys(query).forEach((key) => {
|
|
23
|
-
searchParams.set(key, query[key]);
|
|
24
|
-
});
|
|
25
|
-
return String(urlObject);
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const reloadHtmlPage = () => {
|
|
29
|
-
window.location.reload(true);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// This function can consider everything as hot reloadable:
|
|
33
|
-
// - no need to check [hot-accept]and [hot-decline] attributes for instance
|
|
34
|
-
// This is because if something should full reload, we receive "full_reload"
|
|
35
|
-
// from server and this function is not called
|
|
36
|
-
const getDOMNodesUsingUrl = (urlToReload) => {
|
|
37
|
-
const nodes = [];
|
|
38
|
-
const shouldReloadUrl = (urlCandidate) => {
|
|
39
|
-
return compareTwoUrlPaths(urlCandidate, urlToReload);
|
|
40
|
-
};
|
|
41
|
-
const visitNodeAttributeAsUrl = (node, attributeName) => {
|
|
42
|
-
let attribute = node[attributeName];
|
|
43
|
-
if (!attribute) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
if (SVGAnimatedString && attribute instanceof SVGAnimatedString) {
|
|
47
|
-
attribute = attribute.animVal;
|
|
48
|
-
}
|
|
49
|
-
if (!shouldReloadUrl(attribute)) {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
nodes.push({
|
|
53
|
-
node,
|
|
54
|
-
reload: (hot) => {
|
|
55
|
-
if (node.nodeName === "SCRIPT") {
|
|
56
|
-
const copy = document.createElement("script");
|
|
57
|
-
Array.from(node.attributes).forEach((attribute) => {
|
|
58
|
-
copy.setAttribute(attribute.nodeName, attribute.nodeValue);
|
|
59
|
-
});
|
|
60
|
-
copy.src = injectQuery(node.src, { hot });
|
|
61
|
-
if (node.parentNode) {
|
|
62
|
-
node.parentNode.replaceChild(copy, node);
|
|
63
|
-
} else {
|
|
64
|
-
document.body.appendChild(copy);
|
|
65
|
-
}
|
|
66
|
-
} else {
|
|
67
|
-
node[attributeName] = injectQuery(attribute, { hot });
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
};
|
|
72
|
-
Array.from(document.querySelectorAll(`link[rel="stylesheet"]`)).forEach(
|
|
73
|
-
(link) => {
|
|
74
|
-
visitNodeAttributeAsUrl(link, "href");
|
|
75
|
-
},
|
|
76
|
-
);
|
|
77
|
-
Array.from(document.querySelectorAll(`link[rel="icon"]`)).forEach((link) => {
|
|
78
|
-
visitNodeAttributeAsUrl(link, "href");
|
|
79
|
-
});
|
|
80
|
-
Array.from(document.querySelectorAll("script")).forEach((script) => {
|
|
81
|
-
visitNodeAttributeAsUrl(script, "src");
|
|
82
|
-
const inlinedFromSrc = script.getAttribute("inlined-from-src");
|
|
83
|
-
if (inlinedFromSrc) {
|
|
84
|
-
const inlinedFromUrl = new URL(inlinedFromSrc, window.location.origin)
|
|
85
|
-
.href;
|
|
86
|
-
if (shouldReloadUrl(inlinedFromUrl)) {
|
|
87
|
-
nodes.push({
|
|
88
|
-
node: script,
|
|
89
|
-
reload: () =>
|
|
90
|
-
window.__supervisor__.reloadSupervisedScript(inlinedFromSrc),
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
// There is no real need to update a.href because the resource will be fetched when clicked.
|
|
96
|
-
// But in a scenario where the resource was already visited and is in browser cache, adding
|
|
97
|
-
// the dynamic query param ensure the cache is invalidated
|
|
98
|
-
Array.from(document.querySelectorAll("a")).forEach((a) => {
|
|
99
|
-
visitNodeAttributeAsUrl(a, "href");
|
|
100
|
-
});
|
|
101
|
-
// About iframes:
|
|
102
|
-
// - By default iframe itself and everything inside trigger a parent page full-reload
|
|
103
|
-
// - Adding [hot-accept] on the iframe means parent page won't reload when iframe full/hot reload
|
|
104
|
-
// In that case and if there is code in the iframe and parent doing post message communication:
|
|
105
|
-
// you must put import.meta.hot.decline() for code involved in communication.
|
|
106
|
-
// (both in parent and iframe)
|
|
107
|
-
Array.from(document.querySelectorAll("img")).forEach((img) => {
|
|
108
|
-
visitNodeAttributeAsUrl(img, "src");
|
|
109
|
-
const srcset = img.srcset;
|
|
110
|
-
if (srcset) {
|
|
111
|
-
nodes.push({
|
|
112
|
-
node: img,
|
|
113
|
-
reload: (hot) => {
|
|
114
|
-
const srcCandidates = parseSrcSet(srcset);
|
|
115
|
-
srcCandidates.forEach((srcCandidate) => {
|
|
116
|
-
const url = new URL(
|
|
117
|
-
srcCandidate.specifier,
|
|
118
|
-
`${window.location.href}`,
|
|
119
|
-
);
|
|
120
|
-
if (shouldReloadUrl(url)) {
|
|
121
|
-
srcCandidate.specifier = injectQuery(url, { hot });
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
img.srcset = stringifySrcSet(srcCandidates);
|
|
125
|
-
},
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
Array.from(document.querySelectorAll("source")).forEach((source) => {
|
|
130
|
-
visitNodeAttributeAsUrl(source, "src");
|
|
131
|
-
});
|
|
132
|
-
// svg image tag
|
|
133
|
-
Array.from(document.querySelectorAll("image")).forEach((image) => {
|
|
134
|
-
visitNodeAttributeAsUrl(image, "href");
|
|
135
|
-
});
|
|
136
|
-
// svg use
|
|
137
|
-
Array.from(document.querySelectorAll("use")).forEach((use) => {
|
|
138
|
-
visitNodeAttributeAsUrl(use, "href");
|
|
139
|
-
});
|
|
140
|
-
return nodes;
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const reloadJsImport = async (url, hot) => {
|
|
144
|
-
const urlWithHotSearchParam = injectQuery(url, { hot });
|
|
145
|
-
const namespace = await import(urlWithHotSearchParam);
|
|
146
|
-
return namespace;
|
|
147
|
-
};
|
|
2
|
+
import { urlHotMetas } from "./import_meta_hot.js";
|
|
148
3
|
|
|
149
4
|
const initAutoreload = ({ mainFilePath }) => {
|
|
150
5
|
const reloader = {
|
|
@@ -366,4 +221,155 @@ This could be due to syntax errors or importing non-existent modules (see errors
|
|
|
366
221
|
});
|
|
367
222
|
};
|
|
368
223
|
|
|
224
|
+
const reloadHtmlPage = () => {
|
|
225
|
+
window.location.reload(true);
|
|
226
|
+
};
|
|
227
|
+
// This function can consider everything as hot reloadable:
|
|
228
|
+
// - no need to check [hot-accept]and [hot-decline] attributes for instance
|
|
229
|
+
// This is because if something should full reload, we receive "full_reload"
|
|
230
|
+
// from server and this function is not called
|
|
231
|
+
const getDOMNodesUsingUrl = (urlToReload) => {
|
|
232
|
+
const nodes = [];
|
|
233
|
+
const shouldReloadUrl = (urlCandidate) => {
|
|
234
|
+
return compareTwoUrlPaths(urlCandidate, urlToReload);
|
|
235
|
+
};
|
|
236
|
+
const visitNodeAttributeAsUrl = (node, attributeName) => {
|
|
237
|
+
let attribute = node[attributeName];
|
|
238
|
+
if (!attribute) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (SVGAnimatedString && attribute instanceof SVGAnimatedString) {
|
|
242
|
+
attribute = attribute.animVal;
|
|
243
|
+
}
|
|
244
|
+
if (!shouldReloadUrl(attribute)) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
nodes.push({
|
|
248
|
+
node,
|
|
249
|
+
reload: (hot) => {
|
|
250
|
+
if (node.nodeName === "SCRIPT") {
|
|
251
|
+
const copy = document.createElement("script");
|
|
252
|
+
Array.from(node.attributes).forEach((attribute) => {
|
|
253
|
+
copy.setAttribute(attribute.nodeName, attribute.nodeValue);
|
|
254
|
+
});
|
|
255
|
+
copy.src = injectQuery(node.src, { hot });
|
|
256
|
+
if (node.parentNode) {
|
|
257
|
+
node.parentNode.replaceChild(copy, node);
|
|
258
|
+
} else {
|
|
259
|
+
document.body.appendChild(copy);
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
node[attributeName] = injectQuery(attribute, { hot });
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
Array.from(document.querySelectorAll(`link[rel="stylesheet"]`)).forEach(
|
|
268
|
+
(link) => {
|
|
269
|
+
visitNodeAttributeAsUrl(link, "href");
|
|
270
|
+
},
|
|
271
|
+
);
|
|
272
|
+
Array.from(document.querySelectorAll(`link[rel="icon"]`)).forEach((link) => {
|
|
273
|
+
visitNodeAttributeAsUrl(link, "href");
|
|
274
|
+
});
|
|
275
|
+
Array.from(document.querySelectorAll("script")).forEach((script) => {
|
|
276
|
+
visitNodeAttributeAsUrl(script, "src");
|
|
277
|
+
const inlinedFromSrc = script.getAttribute("inlined-from-src");
|
|
278
|
+
if (inlinedFromSrc) {
|
|
279
|
+
const inlinedFromUrl = new URL(inlinedFromSrc, window.location.origin)
|
|
280
|
+
.href;
|
|
281
|
+
if (shouldReloadUrl(inlinedFromUrl)) {
|
|
282
|
+
nodes.push({
|
|
283
|
+
node: script,
|
|
284
|
+
reload: () =>
|
|
285
|
+
window.__supervisor__.reloadSupervisedScript(inlinedFromSrc),
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
// There is no real need to update a.href because the resource will be fetched when clicked.
|
|
291
|
+
// But in a scenario where the resource was already visited and is in browser cache, adding
|
|
292
|
+
// the dynamic query param ensure the cache is invalidated
|
|
293
|
+
Array.from(document.querySelectorAll("a")).forEach((a) => {
|
|
294
|
+
visitNodeAttributeAsUrl(a, "href");
|
|
295
|
+
});
|
|
296
|
+
// About iframes:
|
|
297
|
+
// - By default iframe itself and everything inside trigger a parent page full-reload
|
|
298
|
+
// - Adding [hot-accept] on the iframe means parent page won't reload when iframe full/hot reload
|
|
299
|
+
// In that case and if there is code in the iframe and parent doing post message communication:
|
|
300
|
+
// you must put import.meta.hot.decline() for code involved in communication.
|
|
301
|
+
// (both in parent and iframe)
|
|
302
|
+
Array.from(document.querySelectorAll("img")).forEach((img) => {
|
|
303
|
+
visitNodeAttributeAsUrl(img, "src");
|
|
304
|
+
const srcset = img.srcset;
|
|
305
|
+
if (srcset) {
|
|
306
|
+
nodes.push({
|
|
307
|
+
node: img,
|
|
308
|
+
reload: (hot) => {
|
|
309
|
+
const srcCandidates = parseSrcSet(srcset);
|
|
310
|
+
srcCandidates.forEach((srcCandidate) => {
|
|
311
|
+
const url = new URL(
|
|
312
|
+
srcCandidate.specifier,
|
|
313
|
+
`${window.location.href}`,
|
|
314
|
+
);
|
|
315
|
+
if (shouldReloadUrl(url)) {
|
|
316
|
+
srcCandidate.specifier = injectQuery(url, { hot });
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
img.srcset = stringifySrcSet(srcCandidates);
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
Array.from(document.querySelectorAll("source")).forEach((source) => {
|
|
325
|
+
visitNodeAttributeAsUrl(source, "src");
|
|
326
|
+
});
|
|
327
|
+
// svg image tag
|
|
328
|
+
Array.from(document.querySelectorAll("image")).forEach((image) => {
|
|
329
|
+
visitNodeAttributeAsUrl(image, "href");
|
|
330
|
+
});
|
|
331
|
+
// svg use
|
|
332
|
+
Array.from(document.querySelectorAll("use")).forEach((use) => {
|
|
333
|
+
visitNodeAttributeAsUrl(use, "href");
|
|
334
|
+
});
|
|
335
|
+
return nodes;
|
|
336
|
+
};
|
|
337
|
+
const reloadJsImport = async (url, hot) => {
|
|
338
|
+
const urlWithHotSearchParam = injectQuery(url, { hot });
|
|
339
|
+
const namespace = await import(urlWithHotSearchParam);
|
|
340
|
+
return namespace;
|
|
341
|
+
};
|
|
342
|
+
// const reloadAllCss = () => {
|
|
343
|
+
// const links = Array.from(document.getElementsByTagName("link"));
|
|
344
|
+
// links.forEach((link) => {
|
|
345
|
+
// if (link.rel === "stylesheet") {
|
|
346
|
+
// link.href = injectQuery(link.href, { hot: Date.now() });
|
|
347
|
+
// }
|
|
348
|
+
// });
|
|
349
|
+
// };
|
|
350
|
+
|
|
351
|
+
const compareTwoUrlPaths = (url, otherUrl) => {
|
|
352
|
+
if (url === otherUrl) {
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
const urlObject = new URL(url);
|
|
356
|
+
const otherUrlObject = new URL(otherUrl);
|
|
357
|
+
if (urlObject.origin !== otherUrlObject.origin) {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
if (urlObject.pathname !== otherUrlObject.pathname) {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
return true;
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const injectQuery = (url, query) => {
|
|
367
|
+
const urlObject = new URL(url);
|
|
368
|
+
const { searchParams } = urlObject;
|
|
369
|
+
Object.keys(query).forEach((key) => {
|
|
370
|
+
searchParams.set(key, query[key]);
|
|
371
|
+
});
|
|
372
|
+
return String(urlObject);
|
|
373
|
+
};
|
|
374
|
+
|
|
369
375
|
export { initAutoreload };
|