@jsenv/core 28.0.0 → 28.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/js/autoreload.js +2 -2
- package/dist/js/html_supervisor_installer.js +3 -3
- package/dist/main.js +105 -114
- package/package.json +2 -2
- package/readme.md +1 -1
- package/src/build/build.js +8 -8
- package/src/build/{resync_ressource_hints.js → resync_resource_hints.js} +10 -12
- package/src/build/start_build_server.js +4 -7
- package/src/omega/kitchen.js +5 -5
- package/src/omega/server/file_service.js +3 -3
- package/src/omega/url_graph/url_graph_load.js +4 -4
- package/src/omega/url_graph.js +3 -3
- package/src/plugins/autoreload/client/reload.js +2 -2
- package/src/plugins/autoreload/jsenv_plugin_autoreload_client.js +1 -1
- package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +3 -3
- package/src/plugins/autoreload/jsenv_plugin_hmr.js +1 -1
- package/src/plugins/explorer/jsenv_plugin_explorer.js +1 -1
- package/src/plugins/html_supervisor/client/error_formatter.js +3 -3
- package/src/plugins/html_supervisor/jsenv_plugin_html_supervisor.js +5 -5
- package/src/plugins/import_meta_hot/html_hot_dependencies.js +4 -4
- package/src/plugins/plugin_controller.js +2 -2
- package/src/plugins/toolbar/jsenv_plugin_toolbar.js +1 -1
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +2 -2
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_html.js +3 -3
- package/src/plugins/url_analysis/html/html_urls.js +2 -2
- package/src/plugins/url_analysis/jsenv_plugin_url_analysis.js +1 -1
package/dist/js/autoreload.js
CHANGED
|
@@ -96,8 +96,8 @@ const getDOMNodesUsingUrl = urlToReload => {
|
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
}); // There is no real need to update a.href because the
|
|
100
|
-
// But in a scenario where the
|
|
99
|
+
}); // There is no real need to update a.href because the resource will be fetched when clicked.
|
|
100
|
+
// But in a scenario where the resource was already visited and is in browser cache, adding
|
|
101
101
|
// the dynamic query param ensure the cache is invalidated
|
|
102
102
|
|
|
103
103
|
Array.from(document.querySelectorAll("a")).forEach(a => {
|
|
@@ -191,13 +191,13 @@ const formatError = (error, {
|
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
if (urlSite.line !== undefined) {
|
|
194
|
-
let
|
|
194
|
+
let resourceToFetch = `/__get_code_frame__/${formatUrlWithLineAndColumn(urlSite)}`;
|
|
195
195
|
|
|
196
196
|
if (!Error.captureStackTrace) {
|
|
197
|
-
|
|
197
|
+
resourceToFetch += `?remap`;
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
const response = await window.fetch(
|
|
200
|
+
const response = await window.fetch(resourceToFetch);
|
|
201
201
|
const codeFrame = await response.text();
|
|
202
202
|
return {
|
|
203
203
|
codeFrame: formatErrorText({
|
package/dist/main.js
CHANGED
|
@@ -94,7 +94,7 @@ const urlToScheme$1 = url => {
|
|
|
94
94
|
return scheme;
|
|
95
95
|
};
|
|
96
96
|
|
|
97
|
-
const
|
|
97
|
+
const urlToResource = url => {
|
|
98
98
|
const scheme = urlToScheme$1(url);
|
|
99
99
|
|
|
100
100
|
if (scheme === "file") {
|
|
@@ -115,25 +115,25 @@ const urlToRessource$1 = url => {
|
|
|
115
115
|
};
|
|
116
116
|
|
|
117
117
|
const urlToPathname$1 = url => {
|
|
118
|
-
const
|
|
119
|
-
const pathname =
|
|
118
|
+
const resource = urlToResource(url);
|
|
119
|
+
const pathname = resourceToPathname(resource);
|
|
120
120
|
return pathname;
|
|
121
121
|
};
|
|
122
122
|
|
|
123
|
-
const
|
|
124
|
-
const searchSeparatorIndex =
|
|
123
|
+
const resourceToPathname = resource => {
|
|
124
|
+
const searchSeparatorIndex = resource.indexOf("?");
|
|
125
125
|
|
|
126
126
|
if (searchSeparatorIndex > -1) {
|
|
127
|
-
return
|
|
127
|
+
return resource.slice(0, searchSeparatorIndex);
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
const hashIndex =
|
|
130
|
+
const hashIndex = resource.indexOf("#");
|
|
131
131
|
|
|
132
132
|
if (hashIndex > -1) {
|
|
133
|
-
return
|
|
133
|
+
return resource.slice(0, hashIndex);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
return
|
|
136
|
+
return resource;
|
|
137
137
|
};
|
|
138
138
|
|
|
139
139
|
const urlToFilename$1 = url => {
|
|
@@ -616,8 +616,8 @@ const urlToRelativeUrl = (url, baseUrl) => {
|
|
|
616
616
|
} = urlObject;
|
|
617
617
|
|
|
618
618
|
if (pathname === "/") {
|
|
619
|
-
const
|
|
620
|
-
return
|
|
619
|
+
const baseUrlResourceWithoutLeadingSlash = baseUrlObject.pathname.slice(1);
|
|
620
|
+
return baseUrlResourceWithoutLeadingSlash;
|
|
621
621
|
}
|
|
622
622
|
|
|
623
623
|
const basePathname = baseUrlObject.pathname;
|
|
@@ -2006,10 +2006,10 @@ const ensureWindowsDriveLetter = (url, baseUrl) => {
|
|
|
2006
2006
|
return `file:///${driveLetter}:${afterProtocol}`;
|
|
2007
2007
|
};
|
|
2008
2008
|
|
|
2009
|
-
const extractDriveLetter =
|
|
2009
|
+
const extractDriveLetter = resource => {
|
|
2010
2010
|
// we still have the windows drive letter
|
|
2011
|
-
if (/[a-zA-Z]/.test(
|
|
2012
|
-
return
|
|
2011
|
+
if (/[a-zA-Z]/.test(resource[1]) && resource[2] === ":") {
|
|
2012
|
+
return resource[1];
|
|
2013
2013
|
}
|
|
2014
2014
|
|
|
2015
2015
|
return null;
|
|
@@ -2136,7 +2136,7 @@ const createWatcher = (sourcePath, options) => {
|
|
|
2136
2136
|
return watcher;
|
|
2137
2137
|
};
|
|
2138
2138
|
|
|
2139
|
-
const
|
|
2139
|
+
const trackResources = () => {
|
|
2140
2140
|
const callbackArray = [];
|
|
2141
2141
|
|
|
2142
2142
|
const registerCleanupCallback = callback => {
|
|
@@ -2244,7 +2244,7 @@ const registerDirectoryLifecycle = (source, {
|
|
|
2244
2244
|
return watch;
|
|
2245
2245
|
};
|
|
2246
2246
|
|
|
2247
|
-
const tracker =
|
|
2247
|
+
const tracker = trackResources();
|
|
2248
2248
|
const infoMap = new Map();
|
|
2249
2249
|
|
|
2250
2250
|
const readEntryInfo = url => {
|
|
@@ -2380,7 +2380,7 @@ const registerDirectoryLifecycle = (source, {
|
|
|
2380
2380
|
|
|
2381
2381
|
if (entryInfo.type !== previousInfo.type) {
|
|
2382
2382
|
// it existed and was replaced by something else
|
|
2383
|
-
// we don't handle this as an update. We rather say the
|
|
2383
|
+
// we don't handle this as an update. We rather say the resource
|
|
2384
2384
|
// is lost and something else is found (call removed() then added())
|
|
2385
2385
|
handleEntryLost(previousInfo);
|
|
2386
2386
|
handleEntryFound(entryInfo);
|
|
@@ -2591,7 +2591,7 @@ const registerFileLifecycle = (source, {
|
|
|
2591
2591
|
}
|
|
2592
2592
|
}
|
|
2593
2593
|
|
|
2594
|
-
const tracker =
|
|
2594
|
+
const tracker = trackResources();
|
|
2595
2595
|
|
|
2596
2596
|
const handleFileFound = ({
|
|
2597
2597
|
existent
|
|
@@ -4014,10 +4014,10 @@ const createUrlGraph = ({
|
|
|
4014
4014
|
}
|
|
4015
4015
|
|
|
4016
4016
|
references.forEach(reference => {
|
|
4017
|
-
if (reference.
|
|
4018
|
-
//
|
|
4017
|
+
if (reference.isResourceHint) {
|
|
4018
|
+
// resource hint are a special kind of reference.
|
|
4019
4019
|
// They are a sort of weak reference to an url.
|
|
4020
|
-
// We ignore them so that url referenced only by
|
|
4020
|
+
// We ignore them so that url referenced only by resource hints
|
|
4021
4021
|
// have url.dependents.size === 0 and can be considered as not used
|
|
4022
4022
|
// It means html won't consider url referenced solely
|
|
4023
4023
|
// by <link> as dependency and it's fine
|
|
@@ -4243,7 +4243,7 @@ const parseAndTransformHtmlUrls = async (urlInfo, context) => {
|
|
|
4243
4243
|
crossorigin,
|
|
4244
4244
|
integrity
|
|
4245
4245
|
} = readFetchMetas(node);
|
|
4246
|
-
const
|
|
4246
|
+
const isResourceHint = ["preconnect", "dns-prefetch", "prefetch", "preload", "modulepreload"].includes(subtype);
|
|
4247
4247
|
const [reference] = referenceUtils.found({
|
|
4248
4248
|
type,
|
|
4249
4249
|
expectedType,
|
|
@@ -4252,7 +4252,7 @@ const parseAndTransformHtmlUrls = async (urlInfo, context) => {
|
|
|
4252
4252
|
specifier,
|
|
4253
4253
|
specifierLine: line,
|
|
4254
4254
|
specifierColumn: column,
|
|
4255
|
-
|
|
4255
|
+
isResourceHint,
|
|
4256
4256
|
crossorigin,
|
|
4257
4257
|
integrity
|
|
4258
4258
|
});
|
|
@@ -4669,7 +4669,7 @@ const jsenvPluginUrlAnalysis = ({
|
|
|
4669
4669
|
return;
|
|
4670
4670
|
}
|
|
4671
4671
|
|
|
4672
|
-
if (reference.specifier[0] === "#" && // For Html, css and in general "#" refer to a
|
|
4672
|
+
if (reference.specifier[0] === "#" && // For Html, css and in general "#" refer to a resource in the page
|
|
4673
4673
|
// so that urls must be kept intact
|
|
4674
4674
|
// However for js import specifiers they have a different meaning and we want
|
|
4675
4675
|
// to resolve them (https://nodejs.org/api/packages.html#imports for instance)
|
|
@@ -5825,10 +5825,10 @@ const jsenvPluginAsJsClassicHtml = ({
|
|
|
5825
5825
|
const convertedUrls = [];
|
|
5826
5826
|
|
|
5827
5827
|
const getReferenceAsJsClassic = async (reference, {
|
|
5828
|
-
// we don't cook
|
|
5829
|
-
// because they might refer to
|
|
5828
|
+
// we don't cook resource hints
|
|
5829
|
+
// because they might refer to resource that will be modified during build
|
|
5830
5830
|
// It also means something else HAVE to reference that url in order to cook it
|
|
5831
|
-
// so that the preload is deleted by "
|
|
5831
|
+
// so that the preload is deleted by "resync_resource_hints.js" otherwise
|
|
5832
5832
|
cookIt = false
|
|
5833
5833
|
} = {}) => {
|
|
5834
5834
|
const newReferenceProps = {
|
|
@@ -10451,7 +10451,7 @@ const jsenvPluginAsJsClassicConversion = ({
|
|
|
10451
10451
|
// - import specifier (static/dynamic import + re-export)
|
|
10452
10452
|
// - url specifier when inside System.register/_context.import()
|
|
10453
10453
|
// (because it's the transpiled equivalent of static and dynamic imports)
|
|
10454
|
-
// And not other references otherwise we could try to transform inline
|
|
10454
|
+
// And not other references otherwise we could try to transform inline resources
|
|
10455
10455
|
// or specifiers inside new URL()...
|
|
10456
10456
|
js_import_export: propagateJsClassicSearchParam,
|
|
10457
10457
|
js_url_specifier: (reference, context) => {
|
|
@@ -10468,7 +10468,7 @@ const jsenvPluginAsJsClassicConversion = ({
|
|
|
10468
10468
|
context,
|
|
10469
10469
|
searchParam: "as_js_classic",
|
|
10470
10470
|
// override the expectedType to "js_module"
|
|
10471
|
-
// because when there is ?as_js_classic it means the underlying
|
|
10471
|
+
// because when there is ?as_js_classic it means the underlying resource
|
|
10472
10472
|
// is a js_module
|
|
10473
10473
|
expectedType: "js_module"
|
|
10474
10474
|
});
|
|
@@ -11531,26 +11531,26 @@ const getParentUrl = url => {
|
|
|
11531
11531
|
if (url.startsWith("file://")) {
|
|
11532
11532
|
// With node.js new URL('../', 'file:///C:/').href
|
|
11533
11533
|
// returns "file:///C:/" instead of "file:///"
|
|
11534
|
-
const
|
|
11535
|
-
const slashLastIndex =
|
|
11534
|
+
const resource = url.slice("file://".length);
|
|
11535
|
+
const slashLastIndex = resource.lastIndexOf("/");
|
|
11536
11536
|
|
|
11537
11537
|
if (slashLastIndex === -1) {
|
|
11538
11538
|
return url;
|
|
11539
11539
|
}
|
|
11540
11540
|
|
|
11541
|
-
const lastCharIndex =
|
|
11541
|
+
const lastCharIndex = resource.length - 1;
|
|
11542
11542
|
|
|
11543
11543
|
if (slashLastIndex === lastCharIndex) {
|
|
11544
|
-
const slashBeforeLastIndex =
|
|
11544
|
+
const slashBeforeLastIndex = resource.lastIndexOf("/", slashLastIndex - 1);
|
|
11545
11545
|
|
|
11546
11546
|
if (slashBeforeLastIndex === -1) {
|
|
11547
11547
|
return url;
|
|
11548
11548
|
}
|
|
11549
11549
|
|
|
11550
|
-
return `file://${
|
|
11550
|
+
return `file://${resource.slice(0, slashBeforeLastIndex + 1)}`;
|
|
11551
11551
|
}
|
|
11552
11552
|
|
|
11553
|
-
return `file://${
|
|
11553
|
+
return `file://${resource.slice(0, slashLastIndex + 1)}`;
|
|
11554
11554
|
}
|
|
11555
11555
|
|
|
11556
11556
|
return new URL(url.endsWith("/") ? "../" : "./", url).href;
|
|
@@ -13190,7 +13190,7 @@ const jsenvPluginHtmlSupervisor = ({
|
|
|
13190
13190
|
name: "jsenv:html_supervisor",
|
|
13191
13191
|
appliesDuring: "dev",
|
|
13192
13192
|
serve: async (request, context) => {
|
|
13193
|
-
if (request.
|
|
13193
|
+
if (request.pathname.startsWith("/__get_code_frame__/")) {
|
|
13194
13194
|
const {
|
|
13195
13195
|
pathname,
|
|
13196
13196
|
searchParams
|
|
@@ -13249,8 +13249,8 @@ const jsenvPluginHtmlSupervisor = ({
|
|
|
13249
13249
|
};
|
|
13250
13250
|
}
|
|
13251
13251
|
|
|
13252
|
-
if (request.
|
|
13253
|
-
const file = request.
|
|
13252
|
+
if (request.pathname.startsWith("/__get_error_cause__/")) {
|
|
13253
|
+
const file = request.pathname.slice("/__get_error_cause__/".length);
|
|
13254
13254
|
|
|
13255
13255
|
if (!file) {
|
|
13256
13256
|
return {
|
|
@@ -13309,8 +13309,8 @@ const jsenvPluginHtmlSupervisor = ({
|
|
|
13309
13309
|
};
|
|
13310
13310
|
}
|
|
13311
13311
|
|
|
13312
|
-
if (request.
|
|
13313
|
-
const file = request.
|
|
13312
|
+
if (request.pathname.startsWith("/__open_in_editor__/")) {
|
|
13313
|
+
const file = request.pathname.slice("/__open_in_editor__/".length);
|
|
13314
13314
|
|
|
13315
13315
|
if (!file) {
|
|
13316
13316
|
return {
|
|
@@ -16397,7 +16397,7 @@ const htmlNodeCanHotReload = node => {
|
|
|
16397
16397
|
if (node.nodeName === "link") {
|
|
16398
16398
|
const {
|
|
16399
16399
|
isStylesheet,
|
|
16400
|
-
|
|
16400
|
+
isResourceHint,
|
|
16401
16401
|
rel
|
|
16402
16402
|
} = analyzeLinkNode(node);
|
|
16403
16403
|
|
|
@@ -16406,9 +16406,9 @@ const htmlNodeCanHotReload = node => {
|
|
|
16406
16406
|
return true;
|
|
16407
16407
|
}
|
|
16408
16408
|
|
|
16409
|
-
if (
|
|
16410
|
-
// for
|
|
16411
|
-
// but we won't do anything (if the
|
|
16409
|
+
if (isResourceHint) {
|
|
16410
|
+
// for resource hints html will be notified the underlying resource has changed
|
|
16411
|
+
// but we won't do anything (if the resource is deleted we should?)
|
|
16412
16412
|
return true;
|
|
16413
16413
|
}
|
|
16414
16414
|
|
|
@@ -16643,10 +16643,7 @@ import.meta.hot = createImportMetaHot(import.meta.url)
|
|
|
16643
16643
|
const jsenvPluginHmr = () => {
|
|
16644
16644
|
return {
|
|
16645
16645
|
name: "jsenv:hmr",
|
|
16646
|
-
appliesDuring:
|
|
16647
|
-
dev: true,
|
|
16648
|
-
test: false
|
|
16649
|
-
},
|
|
16646
|
+
appliesDuring: "dev",
|
|
16650
16647
|
redirectUrl: reference => {
|
|
16651
16648
|
const urlObject = new URL(reference.url);
|
|
16652
16649
|
|
|
@@ -16689,10 +16686,7 @@ const jsenvPluginAutoreloadClient = () => {
|
|
|
16689
16686
|
const autoreloadClientFileUrl = new URL("./js/autoreload.js", import.meta.url).href;
|
|
16690
16687
|
return {
|
|
16691
16688
|
name: "jsenv:autoreload_client",
|
|
16692
|
-
appliesDuring:
|
|
16693
|
-
dev: true,
|
|
16694
|
-
test: false
|
|
16695
|
-
},
|
|
16689
|
+
appliesDuring: "dev",
|
|
16696
16690
|
transformUrlContent: {
|
|
16697
16691
|
html: (htmlUrlInfo, context) => {
|
|
16698
16692
|
const htmlAst = parseHtmlString(htmlUrlInfo.content);
|
|
@@ -16722,10 +16716,7 @@ const jsenvPluginAutoreloadServer = ({
|
|
|
16722
16716
|
}) => {
|
|
16723
16717
|
return {
|
|
16724
16718
|
name: "jsenv:autoreload_server",
|
|
16725
|
-
appliesDuring:
|
|
16726
|
-
dev: true,
|
|
16727
|
-
test: false
|
|
16728
|
-
},
|
|
16719
|
+
appliesDuring: "dev",
|
|
16729
16720
|
serverEvents: {
|
|
16730
16721
|
reload: ({
|
|
16731
16722
|
sendServerEvent,
|
|
@@ -16887,7 +16878,7 @@ const jsenvPluginAutoreloadServer = ({
|
|
|
16887
16878
|
firstUrlInfo
|
|
16888
16879
|
}) => {
|
|
16889
16880
|
const mainHotUpdate = propagateUpdate(firstUrlInfo);
|
|
16890
|
-
const cause = `following files are no longer referenced: ${prunedUrlInfos.map(prunedUrlInfo => formatUrlForClient(prunedUrlInfo.url))}`; // now check if we can hot update the main
|
|
16881
|
+
const cause = `following files are no longer referenced: ${prunedUrlInfos.map(prunedUrlInfo => formatUrlForClient(prunedUrlInfo.url))}`; // now check if we can hot update the main resource
|
|
16891
16882
|
// then if we can hot update all dependencies
|
|
16892
16883
|
|
|
16893
16884
|
if (mainHotUpdate.declined) {
|
|
@@ -16934,7 +16925,7 @@ const jsenvPluginAutoreloadServer = ({
|
|
|
16934
16925
|
rootDirectoryUrl,
|
|
16935
16926
|
urlGraph
|
|
16936
16927
|
}) => {
|
|
16937
|
-
if (request.
|
|
16928
|
+
if (request.pathname === "/__graph__") {
|
|
16938
16929
|
const graphJson = JSON.stringify(urlGraph.toJSON(rootDirectoryUrl));
|
|
16939
16930
|
return {
|
|
16940
16931
|
status: 200,
|
|
@@ -17004,7 +16995,7 @@ const jsenvPluginExplorer = ({
|
|
|
17004
16995
|
serve: async (request, {
|
|
17005
16996
|
rootDirectoryUrl
|
|
17006
16997
|
}) => {
|
|
17007
|
-
if (request.
|
|
16998
|
+
if (request.pathname !== "/") {
|
|
17008
16999
|
return null;
|
|
17009
17000
|
}
|
|
17010
17001
|
|
|
@@ -17978,7 +17969,7 @@ const nodeStreamToObservable = nodeStream => {
|
|
|
17978
17969
|
// safe measure, ensure the readable stream gets
|
|
17979
17970
|
// used in the next ${readableStreamLifetimeInSeconds} otherwise destroys it
|
|
17980
17971
|
const timeout = setTimeout(() => {
|
|
17981
|
-
process.emitWarning(`Readable stream not used after ${readableStreamLifetimeInSeconds} seconds. It will be destroyed to release
|
|
17972
|
+
process.emitWarning(`Readable stream not used after ${readableStreamLifetimeInSeconds} seconds. It will be destroyed to release resources`, {
|
|
17982
17973
|
CODE: "READABLE_STREAM_TIMEOUT",
|
|
17983
17974
|
// url is for http client request
|
|
17984
17975
|
detail: `path: ${nodeStream.path}, fd: ${nodeStream.fd}, url: ${nodeStream.url}`
|
|
@@ -18058,8 +18049,8 @@ const fromNodeRequest = (nodeRequest, {
|
|
|
18058
18049
|
signal,
|
|
18059
18050
|
http2: Boolean(nodeRequest.stream),
|
|
18060
18051
|
origin: requestOrigin,
|
|
18061
|
-
...
|
|
18062
|
-
|
|
18052
|
+
...getPropertiesFromResource({
|
|
18053
|
+
resource: nodeRequest.url,
|
|
18063
18054
|
baseUrl: requestOrigin
|
|
18064
18055
|
}),
|
|
18065
18056
|
method: nodeRequest.method,
|
|
@@ -18068,13 +18059,13 @@ const fromNodeRequest = (nodeRequest, {
|
|
|
18068
18059
|
});
|
|
18069
18060
|
};
|
|
18070
18061
|
const applyRedirectionToRequest = (request, {
|
|
18071
|
-
|
|
18062
|
+
resource,
|
|
18072
18063
|
pathname,
|
|
18073
18064
|
...rest
|
|
18074
18065
|
}) => {
|
|
18075
18066
|
return { ...request,
|
|
18076
|
-
...(
|
|
18077
|
-
|
|
18067
|
+
...(resource ? getPropertiesFromResource({
|
|
18068
|
+
resource,
|
|
18078
18069
|
baseUrl: request.url
|
|
18079
18070
|
}) : pathname ? getPropertiesFromPathname({
|
|
18080
18071
|
pathname,
|
|
@@ -18084,16 +18075,16 @@ const applyRedirectionToRequest = (request, {
|
|
|
18084
18075
|
};
|
|
18085
18076
|
};
|
|
18086
18077
|
|
|
18087
|
-
const
|
|
18088
|
-
|
|
18078
|
+
const getPropertiesFromResource = ({
|
|
18079
|
+
resource,
|
|
18089
18080
|
baseUrl
|
|
18090
18081
|
}) => {
|
|
18091
|
-
const urlObject = new URL(
|
|
18082
|
+
const urlObject = new URL(resource, baseUrl);
|
|
18092
18083
|
let pathname = urlObject.pathname;
|
|
18093
18084
|
return {
|
|
18094
18085
|
url: String(urlObject),
|
|
18095
18086
|
pathname,
|
|
18096
|
-
|
|
18087
|
+
resource
|
|
18097
18088
|
};
|
|
18098
18089
|
};
|
|
18099
18090
|
|
|
@@ -18101,8 +18092,8 @@ const getPropertiesFromPathname = ({
|
|
|
18101
18092
|
pathname,
|
|
18102
18093
|
baseUrl
|
|
18103
18094
|
}) => {
|
|
18104
|
-
return
|
|
18105
|
-
|
|
18095
|
+
return getPropertiesFromResource({
|
|
18096
|
+
resource: `${pathname}${new URL(baseUrl).search}`,
|
|
18106
18097
|
baseUrl
|
|
18107
18098
|
});
|
|
18108
18099
|
};
|
|
@@ -18130,11 +18121,11 @@ const createPushRequest = (request, {
|
|
|
18130
18121
|
const getHeadersInheritedByPushRequest = request => {
|
|
18131
18122
|
const headersInherited = { ...request.headers
|
|
18132
18123
|
}; // mtime sent by the client in request headers concerns the main request
|
|
18133
|
-
// Time remains valid for request to other
|
|
18124
|
+
// Time remains valid for request to other resources so we keep it
|
|
18134
18125
|
// in child requests
|
|
18135
18126
|
// delete childHeaders["if-modified-since"]
|
|
18136
18127
|
// eTag sent by the client in request headers concerns the main request
|
|
18137
|
-
// A request made to an other
|
|
18128
|
+
// A request made to an other resource must not inherit the eTag
|
|
18138
18129
|
|
|
18139
18130
|
delete headersInherited["if-none-match"];
|
|
18140
18131
|
return headersInherited;
|
|
@@ -18961,7 +18952,7 @@ const startServer = async ({
|
|
|
18961
18952
|
requestWaitingMs
|
|
18962
18953
|
}) => {
|
|
18963
18954
|
warn(createDetailedMessage$1(`still no response found for request after ${requestWaitingMs} ms`, {
|
|
18964
|
-
"request url":
|
|
18955
|
+
"request url": request.url,
|
|
18965
18956
|
"request headers": JSON.stringify(request.headers, null, " ")
|
|
18966
18957
|
}));
|
|
18967
18958
|
}
|
|
@@ -19385,7 +19376,7 @@ const startServer = async ({
|
|
|
19385
19376
|
const onPushStreamError = e => {
|
|
19386
19377
|
addRequestLog(requestNode, {
|
|
19387
19378
|
type: "error",
|
|
19388
|
-
value: createDetailedMessage$1(`An error occured while pushing a stream to the response for ${request.
|
|
19379
|
+
value: createDetailedMessage$1(`An error occured while pushing a stream to the response for ${request.resource}`, {
|
|
19389
19380
|
"error stack": e.stack
|
|
19390
19381
|
})
|
|
19391
19382
|
});
|
|
@@ -19489,7 +19480,7 @@ const startServer = async ({
|
|
|
19489
19480
|
|
|
19490
19481
|
addRequestLog(requestNode, {
|
|
19491
19482
|
type: "info",
|
|
19492
|
-
value: request.parent ? `Push ${request.
|
|
19483
|
+
value: request.parent ? `Push ${request.resource}` : `${request.method} ${request.url}`
|
|
19493
19484
|
});
|
|
19494
19485
|
|
|
19495
19486
|
const warn = value => {
|
|
@@ -20307,7 +20298,7 @@ const fetchFileSystem = async (filesystemUrl, {
|
|
|
20307
20298
|
|
|
20308
20299
|
rootDirectoryUrl = rootDirectoryUrlString;
|
|
20309
20300
|
} // here you might be tempted to add || cacheControl === 'no-cache'
|
|
20310
|
-
// but no-cache means
|
|
20301
|
+
// but no-cache means resource can be cache but must be revalidated (yeah naming is strange)
|
|
20311
20302
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Cacheability
|
|
20312
20303
|
|
|
20313
20304
|
|
|
@@ -20448,7 +20439,7 @@ const getClientCacheResponse = async ({
|
|
|
20448
20439
|
sourceUrl
|
|
20449
20440
|
}) => {
|
|
20450
20441
|
// here you might be tempted to add || headers["cache-control"] === "no-cache"
|
|
20451
|
-
// but no-cache means
|
|
20442
|
+
// but no-cache means resource can be cache but must be revalidated (yeah naming is strange)
|
|
20452
20443
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Cacheability
|
|
20453
20444
|
if (headers["cache-control"] === "no-store" || // let's disable it on no-cache too
|
|
20454
20445
|
headers["cache-control"] === "no-cache") {
|
|
@@ -21156,8 +21147,8 @@ const flattenAndFilterPlugins = (plugins, {
|
|
|
21156
21147
|
}
|
|
21157
21148
|
|
|
21158
21149
|
if (typeof appliesDuring === "string") {
|
|
21159
|
-
if (!["dev", "
|
|
21160
|
-
throw new Error(`"appliesDuring" must be "dev"
|
|
21150
|
+
if (!["dev", "build"].includes(appliesDuring)) {
|
|
21151
|
+
throw new Error(`"appliesDuring" must be "dev" or "build", got ${appliesDuring}`);
|
|
21161
21152
|
}
|
|
21162
21153
|
|
|
21163
21154
|
if (scenarios[appliesDuring]) {
|
|
@@ -21849,7 +21840,7 @@ const validateResponseIntegrity = ({
|
|
|
21849
21840
|
return true;
|
|
21850
21841
|
}
|
|
21851
21842
|
|
|
21852
|
-
const error = new Error(`Integrity validation failed for
|
|
21843
|
+
const error = new Error(`Integrity validation failed for resource "${url}". The integrity found for this resource is "${strongestAlgo}-${actualBase64Value}"`);
|
|
21853
21844
|
error.code = "EINTEGRITY";
|
|
21854
21845
|
error.algorithm = strongestAlgo;
|
|
21855
21846
|
error.found = actualBase64Value;
|
|
@@ -21961,7 +21952,7 @@ const createKitchen = ({
|
|
|
21961
21952
|
isEntryPoint = false,
|
|
21962
21953
|
isInline = false,
|
|
21963
21954
|
injected = false,
|
|
21964
|
-
|
|
21955
|
+
isResourceHint = false,
|
|
21965
21956
|
content,
|
|
21966
21957
|
contentType,
|
|
21967
21958
|
assert,
|
|
@@ -21999,8 +21990,8 @@ const createKitchen = ({
|
|
|
21999
21990
|
isEntryPoint,
|
|
22000
21991
|
isInline,
|
|
22001
21992
|
injected,
|
|
22002
|
-
|
|
22003
|
-
// for inline
|
|
21993
|
+
isResourceHint,
|
|
21994
|
+
// for inline resources the reference contains the content
|
|
22004
21995
|
content,
|
|
22005
21996
|
contentType,
|
|
22006
21997
|
timing: {},
|
|
@@ -22051,8 +22042,8 @@ const createKitchen = ({
|
|
|
22051
22042
|
// And this is because this hook inject query params used to:
|
|
22052
22043
|
// - bypass browser cache (?v)
|
|
22053
22044
|
// - convey information (?hmr)
|
|
22054
|
-
// But do not represent an other
|
|
22055
|
-
// the same
|
|
22045
|
+
// But do not represent an other resource, it is considered as
|
|
22046
|
+
// the same resource under the hood
|
|
22056
22047
|
|
|
22057
22048
|
pluginController.callHooks("transformUrlSearchParams", reference, kitchenContext, returnValue => {
|
|
22058
22049
|
Object.keys(returnValue).forEach(key => {
|
|
@@ -22861,11 +22852,11 @@ const loadUrlGraph = async ({
|
|
|
22861
22852
|
references
|
|
22862
22853
|
} = urlInfo;
|
|
22863
22854
|
references.forEach(reference => {
|
|
22864
|
-
// we don't cook
|
|
22865
|
-
// because they might refer to
|
|
22855
|
+
// we don't cook resource hints
|
|
22856
|
+
// because they might refer to resource that will be modified during build
|
|
22866
22857
|
// It also means something else have to reference that url in order to cook it
|
|
22867
|
-
// so that the preload is deleted by "
|
|
22868
|
-
if (reference.
|
|
22858
|
+
// so that the preload is deleted by "resync_resource_hints.js" otherwise
|
|
22859
|
+
if (reference.isResourceHint) {
|
|
22869
22860
|
return;
|
|
22870
22861
|
} // we use reference.generatedUrl to mimic what a browser would do:
|
|
22871
22862
|
// do a fetch to the specifier as found in the file
|
|
@@ -23530,23 +23521,23 @@ self.serviceWorkerUrls = ${JSON.stringify(serviceWorkerUrls, null, " ")};
|
|
|
23530
23521
|
/*
|
|
23531
23522
|
* Update <link rel="preload"> and friends after build (once we know everything)
|
|
23532
23523
|
*
|
|
23533
|
-
* - Used to remove
|
|
23524
|
+
* - Used to remove resource hint targeting an url that is no longer used:
|
|
23534
23525
|
* - Happens because of import assertions transpilation (file is inlined into JS)
|
|
23535
23526
|
*/
|
|
23536
|
-
const
|
|
23527
|
+
const resyncResourceHints = async ({
|
|
23537
23528
|
logger,
|
|
23538
23529
|
finalGraphKitchen,
|
|
23539
23530
|
finalGraph,
|
|
23540
23531
|
rawUrls,
|
|
23541
23532
|
postBuildRedirections
|
|
23542
23533
|
}) => {
|
|
23543
|
-
const
|
|
23534
|
+
const resourceHintActions = [];
|
|
23544
23535
|
GRAPH.forEach(finalGraph, urlInfo => {
|
|
23545
23536
|
if (urlInfo.type !== "html") {
|
|
23546
23537
|
return;
|
|
23547
23538
|
}
|
|
23548
23539
|
|
|
23549
|
-
|
|
23540
|
+
resourceHintActions.push(async () => {
|
|
23550
23541
|
const htmlAst = parseHtmlString(urlInfo.content, {
|
|
23551
23542
|
storeOriginalPositions: false
|
|
23552
23543
|
});
|
|
@@ -23558,9 +23549,9 @@ const resyncRessourceHints = async ({
|
|
|
23558
23549
|
}
|
|
23559
23550
|
|
|
23560
23551
|
const rel = getHtmlNodeAttribute(linkNode, "rel");
|
|
23561
|
-
const
|
|
23552
|
+
const isresourceHint = ["preconnect", "dns-prefetch", "prefetch", "preload", "modulepreload"].includes(rel);
|
|
23562
23553
|
|
|
23563
|
-
if (!
|
|
23554
|
+
if (!isresourceHint) {
|
|
23564
23555
|
return;
|
|
23565
23556
|
}
|
|
23566
23557
|
|
|
@@ -23574,7 +23565,7 @@ const resyncRessourceHints = async ({
|
|
|
23574
23565
|
}
|
|
23575
23566
|
|
|
23576
23567
|
if (!buildUrl) {
|
|
23577
|
-
logger.warn(`remove
|
|
23568
|
+
logger.warn(`remove resource hint because cannot find "${href}"`);
|
|
23578
23569
|
actions.push(() => {
|
|
23579
23570
|
removeHtmlNode(linkNode);
|
|
23580
23571
|
});
|
|
@@ -23585,7 +23576,7 @@ const resyncRessourceHints = async ({
|
|
|
23585
23576
|
const urlInfo = finalGraph.getUrlInfo(buildUrl);
|
|
23586
23577
|
|
|
23587
23578
|
if (!urlInfo) {
|
|
23588
|
-
logger.warn(`remove
|
|
23579
|
+
logger.warn(`remove resource hint because cannot find "${buildUrl}" in the graph`);
|
|
23589
23580
|
actions.push(() => {
|
|
23590
23581
|
removeHtmlNode(linkNode);
|
|
23591
23582
|
});
|
|
@@ -23593,7 +23584,7 @@ const resyncRessourceHints = async ({
|
|
|
23593
23584
|
}
|
|
23594
23585
|
|
|
23595
23586
|
if (urlInfo.dependents.size === 0) {
|
|
23596
|
-
logger.info(`remove
|
|
23587
|
+
logger.info(`remove resource hint because "${href}" not used anymore`);
|
|
23597
23588
|
actions.push(() => {
|
|
23598
23589
|
removeHtmlNode(linkNode);
|
|
23599
23590
|
});
|
|
@@ -23625,7 +23616,7 @@ const resyncRessourceHints = async ({
|
|
|
23625
23616
|
}
|
|
23626
23617
|
});
|
|
23627
23618
|
});
|
|
23628
|
-
await Promise.all(
|
|
23619
|
+
await Promise.all(resourceHintActions.map(resourceHintAction => resourceHintAction()));
|
|
23629
23620
|
};
|
|
23630
23621
|
|
|
23631
23622
|
/*
|
|
@@ -23906,10 +23897,10 @@ build ${entryPointKeys.length} entry points`);
|
|
|
23906
23897
|
addToBundlerIfAny(dependencyUrlInfo);
|
|
23907
23898
|
});
|
|
23908
23899
|
rawUrlInfo.references.forEach(reference => {
|
|
23909
|
-
if (reference.
|
|
23900
|
+
if (reference.isResourceHint && reference.expectedType === "js_module") {
|
|
23910
23901
|
const referencedUrlInfo = rawGraph.getUrlInfo(reference.url);
|
|
23911
23902
|
|
|
23912
|
-
if (referencedUrlInfo && // something else than the
|
|
23903
|
+
if (referencedUrlInfo && // something else than the resource hint is using this url
|
|
23913
23904
|
referencedUrlInfo.dependents.size > 0) {
|
|
23914
23905
|
addToBundlerIfAny(referencedUrlInfo);
|
|
23915
23906
|
}
|
|
@@ -24202,7 +24193,7 @@ build ${entryPointKeys.length} entry points`);
|
|
|
24202
24193
|
throw new Error(`urls should be inside build directory at this stage, found "${reference.url}"`);
|
|
24203
24194
|
}
|
|
24204
24195
|
|
|
24205
|
-
if (reference.
|
|
24196
|
+
if (reference.isResourceHint) {
|
|
24206
24197
|
// return the raw url, we will resync at the end
|
|
24207
24198
|
return rawUrls[reference.url];
|
|
24208
24199
|
} // remove eventual search params and hash
|
|
@@ -24306,7 +24297,7 @@ build ${entryPointKeys.length} entry points`);
|
|
|
24306
24297
|
kitchen: finalGraphKitchen,
|
|
24307
24298
|
outDirectoryUrl: new URL(".jsenv/postbuild/", rootDirectoryUrl),
|
|
24308
24299
|
writeGeneratedFiles,
|
|
24309
|
-
|
|
24300
|
+
skipResourceHint: true,
|
|
24310
24301
|
startLoading: cookEntryFile => {
|
|
24311
24302
|
entryUrls.forEach(entryUrl => {
|
|
24312
24303
|
const [, postBuildEntryUrlInfo] = cookEntryFile({
|
|
@@ -24378,7 +24369,7 @@ ${Array.from(finalGraph.urlInfoMap.keys()).join("\n")}`);
|
|
|
24378
24369
|
urlInfo.data.buildUrlIsVersioned = useVersionedUrl;
|
|
24379
24370
|
urlInfo.data.buildUrlSpecifier = buildUrlSpecifier;
|
|
24380
24371
|
});
|
|
24381
|
-
await
|
|
24372
|
+
await resyncResourceHints({
|
|
24382
24373
|
logger,
|
|
24383
24374
|
finalGraphKitchen,
|
|
24384
24375
|
finalGraph,
|
|
@@ -24722,7 +24713,7 @@ const applyUrlVersioning = async ({
|
|
|
24722
24713
|
return null;
|
|
24723
24714
|
}
|
|
24724
24715
|
|
|
24725
|
-
if (reference.
|
|
24716
|
+
if (reference.isResourceHint) {
|
|
24726
24717
|
return null;
|
|
24727
24718
|
} // specifier comes from "normalize" hook done a bit earlier in this file
|
|
24728
24719
|
// we want to get back their build url to access their infos
|
|
@@ -24783,7 +24774,7 @@ const applyUrlVersioning = async ({
|
|
|
24783
24774
|
operation: buildOperation,
|
|
24784
24775
|
urlGraph: finalGraph,
|
|
24785
24776
|
kitchen: versioningKitchen,
|
|
24786
|
-
|
|
24777
|
+
skipResourceHint: true,
|
|
24787
24778
|
writeGeneratedFiles,
|
|
24788
24779
|
startLoading: cookEntryFile => {
|
|
24789
24780
|
postBuildEntryUrls.forEach(postBuildEntryUrl => {
|
|
@@ -25265,7 +25256,7 @@ const createFileService = ({
|
|
|
25265
25256
|
|
|
25266
25257
|
return async request => {
|
|
25267
25258
|
// serve file inside ".jsenv" directory
|
|
25268
|
-
const requestFileUrl = new URL(request.
|
|
25259
|
+
const requestFileUrl = new URL(request.resource.slice(1), rootDirectoryUrl).href;
|
|
25269
25260
|
|
|
25270
25261
|
if (urlIsInsideOf(requestFileUrl, jsenvDirectoryUrl)) {
|
|
25271
25262
|
return fetchFileSystem(requestFileUrl, {
|
|
@@ -25289,7 +25280,7 @@ const createFileService = ({
|
|
|
25289
25280
|
const parentUrl = inferParentFromRequest(request, rootDirectoryUrl);
|
|
25290
25281
|
|
|
25291
25282
|
if (parentUrl) {
|
|
25292
|
-
reference = urlGraph.inferReference(request.
|
|
25283
|
+
reference = urlGraph.inferReference(request.resource, parentUrl);
|
|
25293
25284
|
}
|
|
25294
25285
|
|
|
25295
25286
|
if (!reference) {
|
|
@@ -25299,7 +25290,7 @@ const createFileService = ({
|
|
|
25299
25290
|
},
|
|
25300
25291
|
parentUrl: parentUrl || rootDirectoryUrl,
|
|
25301
25292
|
type: "http_request",
|
|
25302
|
-
specifier: request.
|
|
25293
|
+
specifier: request.resource
|
|
25303
25294
|
});
|
|
25304
25295
|
reference = entryPoint[0];
|
|
25305
25296
|
}
|
|
@@ -29486,15 +29477,15 @@ const createBuildFilesService = ({
|
|
|
29486
29477
|
buildIndexPath
|
|
29487
29478
|
}) => {
|
|
29488
29479
|
return request => {
|
|
29489
|
-
const urlIsVersioned = new URL(request.
|
|
29480
|
+
const urlIsVersioned = new URL(request.url).searchParams.has("v");
|
|
29490
29481
|
|
|
29491
|
-
if (buildIndexPath && request.
|
|
29482
|
+
if (buildIndexPath && request.resource === "/") {
|
|
29492
29483
|
request = { ...request,
|
|
29493
|
-
|
|
29484
|
+
resource: `/${buildIndexPath}`
|
|
29494
29485
|
};
|
|
29495
29486
|
}
|
|
29496
29487
|
|
|
29497
|
-
return fetchFileSystem(new URL(request.
|
|
29488
|
+
return fetchFileSystem(new URL(request.resource.slice(1), buildDirectoryUrl), {
|
|
29498
29489
|
headers: request.headers,
|
|
29499
29490
|
cacheControl: urlIsVersioned ? `private,max-age=${SECONDS_IN_30_DAYS},immutable` : "private,max-age=0,must-revalidate",
|
|
29500
29491
|
etagEnabled: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/core",
|
|
3
|
-
"version": "28.0.
|
|
3
|
+
"version": "28.0.1",
|
|
4
4
|
"description": "Tool to develop, test and build js projects",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"@jsenv/integrity": "0.0.1",
|
|
74
74
|
"@jsenv/log": "3.1.0",
|
|
75
75
|
"@jsenv/node-esm-resolution": "0.1.0",
|
|
76
|
-
"@jsenv/server": "14.
|
|
76
|
+
"@jsenv/server": "14.1.0",
|
|
77
77
|
"@jsenv/sourcemap": "1.0.4",
|
|
78
78
|
"@jsenv/uneval": "1.6.0",
|
|
79
79
|
"@jsenv/url-meta": "7.0.0",
|
package/readme.md
CHANGED
|
@@ -16,7 +16,7 @@ npm create jsenv@latest
|
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
This command prompts to choose a demo from a list.
|
|
19
|
-
Each demo contains preconfigured scripts
|
|
19
|
+
Each demo contains preconfigured scripts:
|
|
20
20
|
|
|
21
21
|
- `npm run dev`: starts a dev server with autoreload.
|
|
22
22
|
- `npm run test`: execute test files on browsers(s) and/or Node.js.
|
package/src/build/build.js
CHANGED
|
@@ -45,7 +45,7 @@ import { createBuilUrlsGenerator } from "./build_urls_generator.js"
|
|
|
45
45
|
import { injectGlobalVersionMapping } from "./inject_global_version_mappings.js"
|
|
46
46
|
import { createVersionGenerator } from "./version_generator.js"
|
|
47
47
|
import { injectServiceWorkerUrls } from "./inject_service_worker_urls.js"
|
|
48
|
-
import {
|
|
48
|
+
import { resyncResourceHints } from "./resync_resource_hints.js"
|
|
49
49
|
|
|
50
50
|
// default runtimeCompat corresponds to
|
|
51
51
|
// "we can keep <script type="module"> intact":
|
|
@@ -314,13 +314,13 @@ build ${entryPointKeys.length} entry points`)
|
|
|
314
314
|
})
|
|
315
315
|
rawUrlInfo.references.forEach((reference) => {
|
|
316
316
|
if (
|
|
317
|
-
reference.
|
|
317
|
+
reference.isResourceHint &&
|
|
318
318
|
reference.expectedType === "js_module"
|
|
319
319
|
) {
|
|
320
320
|
const referencedUrlInfo = rawGraph.getUrlInfo(reference.url)
|
|
321
321
|
if (
|
|
322
322
|
referencedUrlInfo &&
|
|
323
|
-
// something else than the
|
|
323
|
+
// something else than the resource hint is using this url
|
|
324
324
|
referencedUrlInfo.dependents.size > 0
|
|
325
325
|
) {
|
|
326
326
|
addToBundlerIfAny(referencedUrlInfo)
|
|
@@ -611,7 +611,7 @@ build ${entryPointKeys.length} entry points`)
|
|
|
611
611
|
`urls should be inside build directory at this stage, found "${reference.url}"`,
|
|
612
612
|
)
|
|
613
613
|
}
|
|
614
|
-
if (reference.
|
|
614
|
+
if (reference.isResourceHint) {
|
|
615
615
|
// return the raw url, we will resync at the end
|
|
616
616
|
return rawUrls[reference.url]
|
|
617
617
|
}
|
|
@@ -717,7 +717,7 @@ build ${entryPointKeys.length} entry points`)
|
|
|
717
717
|
kitchen: finalGraphKitchen,
|
|
718
718
|
outDirectoryUrl: new URL(".jsenv/postbuild/", rootDirectoryUrl),
|
|
719
719
|
writeGeneratedFiles,
|
|
720
|
-
|
|
720
|
+
skipResourceHint: true,
|
|
721
721
|
startLoading: (cookEntryFile) => {
|
|
722
722
|
entryUrls.forEach((entryUrl) => {
|
|
723
723
|
const [, postBuildEntryUrlInfo] = cookEntryFile({
|
|
@@ -786,7 +786,7 @@ ${Array.from(finalGraph.urlInfoMap.keys()).join("\n")}`,
|
|
|
786
786
|
urlInfo.data.buildUrlIsVersioned = useVersionedUrl
|
|
787
787
|
urlInfo.data.buildUrlSpecifier = buildUrlSpecifier
|
|
788
788
|
})
|
|
789
|
-
await
|
|
789
|
+
await resyncResourceHints({
|
|
790
790
|
logger,
|
|
791
791
|
finalGraphKitchen,
|
|
792
792
|
finalGraph,
|
|
@@ -1116,7 +1116,7 @@ const applyUrlVersioning = async ({
|
|
|
1116
1116
|
if (reference.isInline || reference.url.startsWith("data:")) {
|
|
1117
1117
|
return null
|
|
1118
1118
|
}
|
|
1119
|
-
if (reference.
|
|
1119
|
+
if (reference.isResourceHint) {
|
|
1120
1120
|
return null
|
|
1121
1121
|
}
|
|
1122
1122
|
// specifier comes from "normalize" hook done a bit earlier in this file
|
|
@@ -1185,7 +1185,7 @@ const applyUrlVersioning = async ({
|
|
|
1185
1185
|
operation: buildOperation,
|
|
1186
1186
|
urlGraph: finalGraph,
|
|
1187
1187
|
kitchen: versioningKitchen,
|
|
1188
|
-
|
|
1188
|
+
skipResourceHint: true,
|
|
1189
1189
|
writeGeneratedFiles,
|
|
1190
1190
|
startLoading: (cookEntryFile) => {
|
|
1191
1191
|
postBuildEntryUrls.forEach((postBuildEntryUrl) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Update <link rel="preload"> and friends after build (once we know everything)
|
|
3
3
|
*
|
|
4
|
-
* - Used to remove
|
|
4
|
+
* - Used to remove resource hint targeting an url that is no longer used:
|
|
5
5
|
* - Happens because of import assertions transpilation (file is inlined into JS)
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -16,19 +16,19 @@ import {
|
|
|
16
16
|
|
|
17
17
|
import { GRAPH } from "./graph_utils.js"
|
|
18
18
|
|
|
19
|
-
export const
|
|
19
|
+
export const resyncResourceHints = async ({
|
|
20
20
|
logger,
|
|
21
21
|
finalGraphKitchen,
|
|
22
22
|
finalGraph,
|
|
23
23
|
rawUrls,
|
|
24
24
|
postBuildRedirections,
|
|
25
25
|
}) => {
|
|
26
|
-
const
|
|
26
|
+
const resourceHintActions = []
|
|
27
27
|
GRAPH.forEach(finalGraph, (urlInfo) => {
|
|
28
28
|
if (urlInfo.type !== "html") {
|
|
29
29
|
return
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
resourceHintActions.push(async () => {
|
|
32
32
|
const htmlAst = parseHtmlString(urlInfo.content, {
|
|
33
33
|
storeOriginalPositions: false,
|
|
34
34
|
})
|
|
@@ -38,14 +38,14 @@ export const resyncRessourceHints = async ({
|
|
|
38
38
|
return
|
|
39
39
|
}
|
|
40
40
|
const rel = getHtmlNodeAttribute(linkNode, "rel")
|
|
41
|
-
const
|
|
41
|
+
const isresourceHint = [
|
|
42
42
|
"preconnect",
|
|
43
43
|
"dns-prefetch",
|
|
44
44
|
"prefetch",
|
|
45
45
|
"preload",
|
|
46
46
|
"modulepreload",
|
|
47
47
|
].includes(rel)
|
|
48
|
-
if (!
|
|
48
|
+
if (!isresourceHint) {
|
|
49
49
|
return
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -57,7 +57,7 @@ export const resyncRessourceHints = async ({
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
if (!buildUrl) {
|
|
60
|
-
logger.warn(`remove
|
|
60
|
+
logger.warn(`remove resource hint because cannot find "${href}"`)
|
|
61
61
|
actions.push(() => {
|
|
62
62
|
removeHtmlNode(linkNode)
|
|
63
63
|
})
|
|
@@ -67,7 +67,7 @@ export const resyncRessourceHints = async ({
|
|
|
67
67
|
const urlInfo = finalGraph.getUrlInfo(buildUrl)
|
|
68
68
|
if (!urlInfo) {
|
|
69
69
|
logger.warn(
|
|
70
|
-
`remove
|
|
70
|
+
`remove resource hint because cannot find "${buildUrl}" in the graph`,
|
|
71
71
|
)
|
|
72
72
|
actions.push(() => {
|
|
73
73
|
removeHtmlNode(linkNode)
|
|
@@ -75,9 +75,7 @@ export const resyncRessourceHints = async ({
|
|
|
75
75
|
return
|
|
76
76
|
}
|
|
77
77
|
if (urlInfo.dependents.size === 0) {
|
|
78
|
-
logger.info(
|
|
79
|
-
`remove ressource hint because "${href}" not used anymore`,
|
|
80
|
-
)
|
|
78
|
+
logger.info(`remove resource hint because "${href}" not used anymore`)
|
|
81
79
|
actions.push(() => {
|
|
82
80
|
removeHtmlNode(linkNode)
|
|
83
81
|
})
|
|
@@ -109,6 +107,6 @@ export const resyncRessourceHints = async ({
|
|
|
109
107
|
})
|
|
110
108
|
})
|
|
111
109
|
await Promise.all(
|
|
112
|
-
|
|
110
|
+
resourceHintActions.map((resourceHintAction) => resourceHintAction()),
|
|
113
111
|
)
|
|
114
112
|
}
|
|
@@ -208,18 +208,15 @@ export const startBuildServer = async ({
|
|
|
208
208
|
|
|
209
209
|
const createBuildFilesService = ({ buildDirectoryUrl, buildIndexPath }) => {
|
|
210
210
|
return (request) => {
|
|
211
|
-
const urlIsVersioned = new URL(
|
|
212
|
-
|
|
213
|
-
request.origin,
|
|
214
|
-
).searchParams.has("v")
|
|
215
|
-
if (buildIndexPath && request.ressource === "/") {
|
|
211
|
+
const urlIsVersioned = new URL(request.url).searchParams.has("v")
|
|
212
|
+
if (buildIndexPath && request.resource === "/") {
|
|
216
213
|
request = {
|
|
217
214
|
...request,
|
|
218
|
-
|
|
215
|
+
resource: `/${buildIndexPath}`,
|
|
219
216
|
}
|
|
220
217
|
}
|
|
221
218
|
return fetchFileSystem(
|
|
222
|
-
new URL(request.
|
|
219
|
+
new URL(request.resource.slice(1), buildDirectoryUrl),
|
|
223
220
|
{
|
|
224
221
|
headers: request.headers,
|
|
225
222
|
cacheControl: urlIsVersioned
|
package/src/omega/kitchen.js
CHANGED
|
@@ -88,7 +88,7 @@ export const createKitchen = ({
|
|
|
88
88
|
isEntryPoint = false,
|
|
89
89
|
isInline = false,
|
|
90
90
|
injected = false,
|
|
91
|
-
|
|
91
|
+
isResourceHint = false,
|
|
92
92
|
content,
|
|
93
93
|
contentType,
|
|
94
94
|
assert,
|
|
@@ -125,8 +125,8 @@ export const createKitchen = ({
|
|
|
125
125
|
isEntryPoint,
|
|
126
126
|
isInline,
|
|
127
127
|
injected,
|
|
128
|
-
|
|
129
|
-
// for inline
|
|
128
|
+
isResourceHint,
|
|
129
|
+
// for inline resources the reference contains the content
|
|
130
130
|
content,
|
|
131
131
|
contentType,
|
|
132
132
|
timing: {},
|
|
@@ -182,8 +182,8 @@ export const createKitchen = ({
|
|
|
182
182
|
// And this is because this hook inject query params used to:
|
|
183
183
|
// - bypass browser cache (?v)
|
|
184
184
|
// - convey information (?hmr)
|
|
185
|
-
// But do not represent an other
|
|
186
|
-
// the same
|
|
185
|
+
// But do not represent an other resource, it is considered as
|
|
186
|
+
// the same resource under the hood
|
|
187
187
|
pluginController.callHooks(
|
|
188
188
|
"transformUrlSearchParams",
|
|
189
189
|
reference,
|
|
@@ -185,7 +185,7 @@ export const createFileService = ({
|
|
|
185
185
|
|
|
186
186
|
return async (request) => {
|
|
187
187
|
// serve file inside ".jsenv" directory
|
|
188
|
-
const requestFileUrl = new URL(request.
|
|
188
|
+
const requestFileUrl = new URL(request.resource.slice(1), rootDirectoryUrl)
|
|
189
189
|
.href
|
|
190
190
|
if (urlIsInsideOf(requestFileUrl, jsenvDirectoryUrl)) {
|
|
191
191
|
return fetchFileSystem(requestFileUrl, {
|
|
@@ -206,14 +206,14 @@ export const createFileService = ({
|
|
|
206
206
|
let reference
|
|
207
207
|
const parentUrl = inferParentFromRequest(request, rootDirectoryUrl)
|
|
208
208
|
if (parentUrl) {
|
|
209
|
-
reference = urlGraph.inferReference(request.
|
|
209
|
+
reference = urlGraph.inferReference(request.resource, parentUrl)
|
|
210
210
|
}
|
|
211
211
|
if (!reference) {
|
|
212
212
|
const entryPoint = kitchen.injectReference({
|
|
213
213
|
trace: { message: parentUrl || rootDirectoryUrl },
|
|
214
214
|
parentUrl: parentUrl || rootDirectoryUrl,
|
|
215
215
|
type: "http_request",
|
|
216
|
-
specifier: request.
|
|
216
|
+
specifier: request.resource,
|
|
217
217
|
})
|
|
218
218
|
reference = entryPoint[0]
|
|
219
219
|
}
|
|
@@ -31,11 +31,11 @@ export const loadUrlGraph = async ({
|
|
|
31
31
|
})
|
|
32
32
|
const { references } = urlInfo
|
|
33
33
|
references.forEach((reference) => {
|
|
34
|
-
// we don't cook
|
|
35
|
-
// because they might refer to
|
|
34
|
+
// we don't cook resource hints
|
|
35
|
+
// because they might refer to resource that will be modified during build
|
|
36
36
|
// It also means something else have to reference that url in order to cook it
|
|
37
|
-
// so that the preload is deleted by "
|
|
38
|
-
if (reference.
|
|
37
|
+
// so that the preload is deleted by "resync_resource_hints.js" otherwise
|
|
38
|
+
if (reference.isResourceHint) {
|
|
39
39
|
return
|
|
40
40
|
}
|
|
41
41
|
// we use reference.generatedUrl to mimic what a browser would do:
|
package/src/omega/url_graph.js
CHANGED
|
@@ -70,10 +70,10 @@ export const createUrlGraph = ({
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
references.forEach((reference) => {
|
|
73
|
-
if (reference.
|
|
74
|
-
//
|
|
73
|
+
if (reference.isResourceHint) {
|
|
74
|
+
// resource hint are a special kind of reference.
|
|
75
75
|
// They are a sort of weak reference to an url.
|
|
76
|
-
// We ignore them so that url referenced only by
|
|
76
|
+
// We ignore them so that url referenced only by resource hints
|
|
77
77
|
// have url.dependents.size === 0 and can be considered as not used
|
|
78
78
|
// It means html won't consider url referenced solely
|
|
79
79
|
// by <link> as dependency and it's fine
|
|
@@ -62,8 +62,8 @@ export const getDOMNodesUsingUrl = (urlToReload) => {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
})
|
|
65
|
-
// There is no real need to update a.href because the
|
|
66
|
-
// But in a scenario where the
|
|
65
|
+
// There is no real need to update a.href because the resource will be fetched when clicked.
|
|
66
|
+
// But in a scenario where the resource was already visited and is in browser cache, adding
|
|
67
67
|
// the dynamic query param ensure the cache is invalidated
|
|
68
68
|
Array.from(document.querySelectorAll("a")).forEach((a) => {
|
|
69
69
|
visitNodeAttributeAsUrl(a, "href")
|
|
@@ -13,7 +13,7 @@ export const jsenvPluginAutoreloadClient = () => {
|
|
|
13
13
|
|
|
14
14
|
return {
|
|
15
15
|
name: "jsenv:autoreload_client",
|
|
16
|
-
appliesDuring:
|
|
16
|
+
appliesDuring: "dev",
|
|
17
17
|
transformUrlContent: {
|
|
18
18
|
html: (htmlUrlInfo, context) => {
|
|
19
19
|
const htmlAst = parseHtmlString(htmlUrlInfo.content)
|
|
@@ -6,7 +6,7 @@ export const jsenvPluginAutoreloadServer = ({
|
|
|
6
6
|
}) => {
|
|
7
7
|
return {
|
|
8
8
|
name: "jsenv:autoreload_server",
|
|
9
|
-
appliesDuring:
|
|
9
|
+
appliesDuring: "dev",
|
|
10
10
|
serverEvents: {
|
|
11
11
|
reload: ({ sendServerEvent, rootDirectoryUrl, urlGraph }) => {
|
|
12
12
|
const formatUrlForClient = (url) => {
|
|
@@ -140,7 +140,7 @@ export const jsenvPluginAutoreloadServer = ({
|
|
|
140
140
|
const cause = `following files are no longer referenced: ${prunedUrlInfos.map(
|
|
141
141
|
(prunedUrlInfo) => formatUrlForClient(prunedUrlInfo.url),
|
|
142
142
|
)}`
|
|
143
|
-
// now check if we can hot update the main
|
|
143
|
+
// now check if we can hot update the main resource
|
|
144
144
|
// then if we can hot update all dependencies
|
|
145
145
|
if (mainHotUpdate.declined) {
|
|
146
146
|
notifyDeclined({
|
|
@@ -179,7 +179,7 @@ export const jsenvPluginAutoreloadServer = ({
|
|
|
179
179
|
},
|
|
180
180
|
},
|
|
181
181
|
serve: (request, { rootDirectoryUrl, urlGraph }) => {
|
|
182
|
-
if (request.
|
|
182
|
+
if (request.pathname === "/__graph__") {
|
|
183
183
|
const graphJson = JSON.stringify(urlGraph.toJSON(rootDirectoryUrl))
|
|
184
184
|
return {
|
|
185
185
|
status: 200,
|
|
@@ -11,7 +11,7 @@ export const jsenvPluginExplorer = ({ groups }) => {
|
|
|
11
11
|
name: "jsenv:explorer",
|
|
12
12
|
appliesDuring: "dev",
|
|
13
13
|
serve: async (request, { rootDirectoryUrl }) => {
|
|
14
|
-
if (request.
|
|
14
|
+
if (request.pathname !== "/") {
|
|
15
15
|
return null
|
|
16
16
|
}
|
|
17
17
|
const associationsForExplorable = {}
|
|
@@ -154,13 +154,13 @@ export const formatError = (
|
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
if (urlSite.line !== undefined) {
|
|
157
|
-
let
|
|
157
|
+
let resourceToFetch = `/__get_code_frame__/${formatUrlWithLineAndColumn(
|
|
158
158
|
urlSite,
|
|
159
159
|
)}`
|
|
160
160
|
if (!Error.captureStackTrace) {
|
|
161
|
-
|
|
161
|
+
resourceToFetch += `?remap`
|
|
162
162
|
}
|
|
163
|
-
const response = await window.fetch(
|
|
163
|
+
const response = await window.fetch(resourceToFetch)
|
|
164
164
|
const codeFrame = await response.text()
|
|
165
165
|
return {
|
|
166
166
|
codeFrame: formatErrorText({ message: codeFrame }),
|
|
@@ -45,7 +45,7 @@ export const jsenvPluginHtmlSupervisor = ({
|
|
|
45
45
|
name: "jsenv:html_supervisor",
|
|
46
46
|
appliesDuring: "dev",
|
|
47
47
|
serve: async (request, context) => {
|
|
48
|
-
if (request.
|
|
48
|
+
if (request.pathname.startsWith("/__get_code_frame__/")) {
|
|
49
49
|
const { pathname, searchParams } = new URL(request.url)
|
|
50
50
|
const urlWithLineAndColumn = pathname.slice(
|
|
51
51
|
"/__get_code_frame__/".length,
|
|
@@ -95,8 +95,8 @@ export const jsenvPluginHtmlSupervisor = ({
|
|
|
95
95
|
body: codeFrame,
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
|
-
if (request.
|
|
99
|
-
const file = request.
|
|
98
|
+
if (request.pathname.startsWith("/__get_error_cause__/")) {
|
|
99
|
+
const file = request.pathname.slice("/__get_error_cause__/".length)
|
|
100
100
|
if (!file) {
|
|
101
101
|
return {
|
|
102
102
|
status: 400,
|
|
@@ -148,8 +148,8 @@ export const jsenvPluginHtmlSupervisor = ({
|
|
|
148
148
|
body,
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
|
-
if (request.
|
|
152
|
-
const file = request.
|
|
151
|
+
if (request.pathname.startsWith("/__open_in_editor__/")) {
|
|
152
|
+
const file = request.pathname.slice("/__open_in_editor__/".length)
|
|
153
153
|
if (!file) {
|
|
154
154
|
return {
|
|
155
155
|
status: 400,
|
|
@@ -119,14 +119,14 @@ const getNodeContext = (node) => {
|
|
|
119
119
|
|
|
120
120
|
const htmlNodeCanHotReload = (node) => {
|
|
121
121
|
if (node.nodeName === "link") {
|
|
122
|
-
const { isStylesheet,
|
|
122
|
+
const { isStylesheet, isResourceHint, rel } = analyzeLinkNode(node)
|
|
123
123
|
if (isStylesheet) {
|
|
124
124
|
// stylesheets can be hot replaced by default
|
|
125
125
|
return true
|
|
126
126
|
}
|
|
127
|
-
if (
|
|
128
|
-
// for
|
|
129
|
-
// but we won't do anything (if the
|
|
127
|
+
if (isResourceHint) {
|
|
128
|
+
// for resource hints html will be notified the underlying resource has changed
|
|
129
|
+
// but we won't do anything (if the resource is deleted we should?)
|
|
130
130
|
return true
|
|
131
131
|
}
|
|
132
132
|
if (rel === "icon") {
|
|
@@ -208,9 +208,9 @@ const flattenAndFilterPlugins = (plugins, { scenarios }) => {
|
|
|
208
208
|
return
|
|
209
209
|
}
|
|
210
210
|
if (typeof appliesDuring === "string") {
|
|
211
|
-
if (!["dev", "
|
|
211
|
+
if (!["dev", "build"].includes(appliesDuring)) {
|
|
212
212
|
throw new Error(
|
|
213
|
-
`"appliesDuring" must be "dev"
|
|
213
|
+
`"appliesDuring" must be "dev" or "build", got ${appliesDuring}`,
|
|
214
214
|
)
|
|
215
215
|
}
|
|
216
216
|
if (scenarios[appliesDuring]) {
|
|
@@ -17,7 +17,7 @@ export const jsenvPluginToolbar = ({ logs = false } = {}) => {
|
|
|
17
17
|
|
|
18
18
|
return {
|
|
19
19
|
name: "jsenv:toolbar",
|
|
20
|
-
appliesDuring:
|
|
20
|
+
appliesDuring: "dev",
|
|
21
21
|
transformUrlContent: {
|
|
22
22
|
html: ({ url, content }, { referenceUtils }) => {
|
|
23
23
|
if (url === toolbarHtmlClientFileUrl) {
|
|
@@ -75,7 +75,7 @@ const jsenvPluginAsJsClassicConversion = ({
|
|
|
75
75
|
// - import specifier (static/dynamic import + re-export)
|
|
76
76
|
// - url specifier when inside System.register/_context.import()
|
|
77
77
|
// (because it's the transpiled equivalent of static and dynamic imports)
|
|
78
|
-
// And not other references otherwise we could try to transform inline
|
|
78
|
+
// And not other references otherwise we could try to transform inline resources
|
|
79
79
|
// or specifiers inside new URL()...
|
|
80
80
|
js_import_export: propagateJsClassicSearchParam,
|
|
81
81
|
js_url_specifier: (reference, context) => {
|
|
@@ -94,7 +94,7 @@ const jsenvPluginAsJsClassicConversion = ({
|
|
|
94
94
|
context,
|
|
95
95
|
searchParam: "as_js_classic",
|
|
96
96
|
// override the expectedType to "js_module"
|
|
97
|
-
// because when there is ?as_js_classic it means the underlying
|
|
97
|
+
// because when there is ?as_js_classic it means the underlying resource
|
|
98
98
|
// is a js_module
|
|
99
99
|
expectedType: "js_module",
|
|
100
100
|
})
|
|
@@ -75,10 +75,10 @@ export const jsenvPluginAsJsClassicHtml = ({
|
|
|
75
75
|
const getReferenceAsJsClassic = async (
|
|
76
76
|
reference,
|
|
77
77
|
{
|
|
78
|
-
// we don't cook
|
|
79
|
-
// because they might refer to
|
|
78
|
+
// we don't cook resource hints
|
|
79
|
+
// because they might refer to resource that will be modified during build
|
|
80
80
|
// It also means something else HAVE to reference that url in order to cook it
|
|
81
|
-
// so that the preload is deleted by "
|
|
81
|
+
// so that the preload is deleted by "resync_resource_hints.js" otherwise
|
|
82
82
|
cookIt = false,
|
|
83
83
|
} = {},
|
|
84
84
|
) => {
|
|
@@ -35,7 +35,7 @@ export const parseAndTransformHtmlUrls = async (urlInfo, context) => {
|
|
|
35
35
|
}) => {
|
|
36
36
|
const { crossorigin, integrity } = readFetchMetas(node)
|
|
37
37
|
|
|
38
|
-
const
|
|
38
|
+
const isResourceHint = [
|
|
39
39
|
"preconnect",
|
|
40
40
|
"dns-prefetch",
|
|
41
41
|
"prefetch",
|
|
@@ -50,7 +50,7 @@ export const parseAndTransformHtmlUrls = async (urlInfo, context) => {
|
|
|
50
50
|
specifier,
|
|
51
51
|
specifierLine: line,
|
|
52
52
|
specifierColumn: column,
|
|
53
|
-
|
|
53
|
+
isResourceHint,
|
|
54
54
|
crossorigin,
|
|
55
55
|
integrity,
|
|
56
56
|
})
|
|
@@ -32,7 +32,7 @@ export const jsenvPluginUrlAnalysis = ({
|
|
|
32
32
|
}
|
|
33
33
|
if (
|
|
34
34
|
reference.specifier[0] === "#" &&
|
|
35
|
-
// For Html, css and in general "#" refer to a
|
|
35
|
+
// For Html, css and in general "#" refer to a resource in the page
|
|
36
36
|
// so that urls must be kept intact
|
|
37
37
|
// However for js import specifiers they have a different meaning and we want
|
|
38
38
|
// to resolve them (https://nodejs.org/api/packages.html#imports for instance)
|