@jsenv/core 41.5.13 → 41.5.15
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/build/build.js +23 -1
- package/dist/build/jsenv_core_packages.js +22 -4
- package/dist/start_dev_server/jsenv_core_packages.js +22 -4
- package/dist/start_dev_server/start_dev_server.js +39 -1
- package/package.json +2 -2
- package/src/dev/dev_server_plugins/dev_server_plugin_serve_source_files.js +16 -0
- package/src/kitchen/url_graph/url_graph.js +4 -0
- package/src/plugins/autoreload/jsenv_plugin_hot_search_param.js +13 -0
- package/src/plugins/protocol_file/jsenv_plugin_directory_listing.js +6 -1
package/dist/build/build.js
CHANGED
|
@@ -2131,6 +2131,10 @@ const createUrlInfo = (url, context) => {
|
|
|
2131
2131
|
modifiedTimestamp: 0,
|
|
2132
2132
|
descendantModifiedTimestamp: 0,
|
|
2133
2133
|
dereferencedTimestamp: 0,
|
|
2134
|
+
// when a client last fetched this url outside a hot request; in other
|
|
2135
|
+
// words the last time this url entered a fresh page (see
|
|
2136
|
+
// jsenv_plugin_hot_search_param)
|
|
2137
|
+
servedWithoutHotTimestamp: 0,
|
|
2134
2138
|
originalContentEtag: null,
|
|
2135
2139
|
contentEtag: null,
|
|
2136
2140
|
isValid: () => false,
|
|
@@ -7631,7 +7635,12 @@ const getDirectoryContentItems = ({
|
|
|
7631
7635
|
fileUrls.push(fileUrlObject);
|
|
7632
7636
|
}
|
|
7633
7637
|
}
|
|
7634
|
-
|
|
7638
|
+
// Not the default numeric order: what is drawn here is a directory as a human
|
|
7639
|
+
// reads it, so a leading number is part of the name ("10_a" right after
|
|
7640
|
+
// "1_a") the way the filesystem and the editor next to this browser show it.
|
|
7641
|
+
fileUrls.sort((leftUrl, rightUrl) =>
|
|
7642
|
+
compareFileUrls(leftUrl, rightUrl, { numeric: false }),
|
|
7643
|
+
);
|
|
7635
7644
|
|
|
7636
7645
|
const items = [];
|
|
7637
7646
|
for (const fileUrl of fileUrls) {
|
|
@@ -9440,6 +9449,7 @@ const jsenvPluginHotSearchParam = () => {
|
|
|
9440
9449
|
modifiedTimestamp,
|
|
9441
9450
|
descendantModifiedTimestamp,
|
|
9442
9451
|
dereferencedTimestamp,
|
|
9452
|
+
servedWithoutHotTimestamp,
|
|
9443
9453
|
} = referencedUrlInfo;
|
|
9444
9454
|
if (
|
|
9445
9455
|
!modifiedTimestamp &&
|
|
@@ -9463,6 +9473,18 @@ const jsenvPluginHotSearchParam = () => {
|
|
|
9463
9473
|
descendantModifiedTimestamp,
|
|
9464
9474
|
dereferencedTimestamp,
|
|
9465
9475
|
);
|
|
9476
|
+
// These timestamps say "this url changed at some point", not "the client
|
|
9477
|
+
// is running an outdated version of it": they are never cleared, so a
|
|
9478
|
+
// file modified once keeps them for the rest of the dev server's life.
|
|
9479
|
+
// A client that fetched this url after that modification (a page load:
|
|
9480
|
+
// see rememberServedWithoutHot in the dev server) already runs the
|
|
9481
|
+
// latest content. Sending it "?hot" then makes the browser evaluate a
|
|
9482
|
+
// second, identical copy of a module it already has — a second module
|
|
9483
|
+
// scope for a file that never changed, which breaks everything a module
|
|
9484
|
+
// holds once (registries, contexts, singletons).
|
|
9485
|
+
if (latestTimestamp <= servedWithoutHotTimestamp) {
|
|
9486
|
+
return null;
|
|
9487
|
+
}
|
|
9466
9488
|
return {
|
|
9467
9489
|
hot: latestTimestamp,
|
|
9468
9490
|
};
|
|
@@ -2620,7 +2620,25 @@ const assertAndNormalizeFileUrl = (
|
|
|
2620
2620
|
return value;
|
|
2621
2621
|
};
|
|
2622
2622
|
|
|
2623
|
-
|
|
2623
|
+
/*
|
|
2624
|
+
* Which of two pathnames comes first: directories before what is under them,
|
|
2625
|
+
* deeper before shallower, then the names themselves.
|
|
2626
|
+
*
|
|
2627
|
+
* How a leading number reads is the caller's call, because the two readings are
|
|
2628
|
+
* both right somewhere:
|
|
2629
|
+
* - numeric (the default), where the number is a quantity: "9_x" then "10_x" —
|
|
2630
|
+
* what a generated list wants, so a story numbered by hand stays in the order
|
|
2631
|
+
* it happens;
|
|
2632
|
+
* - { numeric: false }, where the number is part of the name: "10_x" right
|
|
2633
|
+
* after "1_x" — the order the filesystem itself gives, and the one every file
|
|
2634
|
+
* explorer above it shows, so a human reading a directory finds it in the
|
|
2635
|
+
* order their editor already shows.
|
|
2636
|
+
*/
|
|
2637
|
+
const comparePathnames = (
|
|
2638
|
+
leftPathame,
|
|
2639
|
+
rightPathname,
|
|
2640
|
+
{ numeric = true } = {},
|
|
2641
|
+
) => {
|
|
2624
2642
|
const leftPartArray = leftPathame.split("/");
|
|
2625
2643
|
const rightPartArray = rightPathname.split("/");
|
|
2626
2644
|
|
|
@@ -2656,7 +2674,7 @@ const comparePathnames = (leftPathame, rightPathname) => {
|
|
|
2656
2674
|
i++;
|
|
2657
2675
|
// local comparison comes first
|
|
2658
2676
|
const comparison = leftPart.localeCompare(rightPart, undefined, {
|
|
2659
|
-
numeric
|
|
2677
|
+
numeric,
|
|
2660
2678
|
sensitivity: "base",
|
|
2661
2679
|
});
|
|
2662
2680
|
if (comparison !== 0) {
|
|
@@ -2673,8 +2691,8 @@ const comparePathnames = (leftPathame, rightPathname) => {
|
|
|
2673
2691
|
return 0;
|
|
2674
2692
|
};
|
|
2675
2693
|
|
|
2676
|
-
const compareFileUrls = (a, b) => {
|
|
2677
|
-
return comparePathnames(new URL(a).pathname, new URL(b).pathname);
|
|
2694
|
+
const compareFileUrls = (a, b, options) => {
|
|
2695
|
+
return comparePathnames(new URL(a).pathname, new URL(b).pathname, options);
|
|
2678
2696
|
};
|
|
2679
2697
|
|
|
2680
2698
|
const isWindows$3 = process.platform === "win32";
|
|
@@ -1921,7 +1921,25 @@ const assertAndNormalizeFileUrl = (
|
|
|
1921
1921
|
return value;
|
|
1922
1922
|
};
|
|
1923
1923
|
|
|
1924
|
-
|
|
1924
|
+
/*
|
|
1925
|
+
* Which of two pathnames comes first: directories before what is under them,
|
|
1926
|
+
* deeper before shallower, then the names themselves.
|
|
1927
|
+
*
|
|
1928
|
+
* How a leading number reads is the caller's call, because the two readings are
|
|
1929
|
+
* both right somewhere:
|
|
1930
|
+
* - numeric (the default), where the number is a quantity: "9_x" then "10_x" —
|
|
1931
|
+
* what a generated list wants, so a story numbered by hand stays in the order
|
|
1932
|
+
* it happens;
|
|
1933
|
+
* - { numeric: false }, where the number is part of the name: "10_x" right
|
|
1934
|
+
* after "1_x" — the order the filesystem itself gives, and the one every file
|
|
1935
|
+
* explorer above it shows, so a human reading a directory finds it in the
|
|
1936
|
+
* order their editor already shows.
|
|
1937
|
+
*/
|
|
1938
|
+
const comparePathnames = (
|
|
1939
|
+
leftPathame,
|
|
1940
|
+
rightPathname,
|
|
1941
|
+
{ numeric = true } = {},
|
|
1942
|
+
) => {
|
|
1925
1943
|
const leftPartArray = leftPathame.split("/");
|
|
1926
1944
|
const rightPartArray = rightPathname.split("/");
|
|
1927
1945
|
|
|
@@ -1957,7 +1975,7 @@ const comparePathnames = (leftPathame, rightPathname) => {
|
|
|
1957
1975
|
i++;
|
|
1958
1976
|
// local comparison comes first
|
|
1959
1977
|
const comparison = leftPart.localeCompare(rightPart, undefined, {
|
|
1960
|
-
numeric
|
|
1978
|
+
numeric,
|
|
1961
1979
|
sensitivity: "base",
|
|
1962
1980
|
});
|
|
1963
1981
|
if (comparison !== 0) {
|
|
@@ -1974,8 +1992,8 @@ const comparePathnames = (leftPathame, rightPathname) => {
|
|
|
1974
1992
|
return 0;
|
|
1975
1993
|
};
|
|
1976
1994
|
|
|
1977
|
-
const compareFileUrls = (a, b) => {
|
|
1978
|
-
return comparePathnames(new URL(a).pathname, new URL(b).pathname);
|
|
1995
|
+
const compareFileUrls = (a, b, options) => {
|
|
1996
|
+
return comparePathnames(new URL(a).pathname, new URL(b).pathname, options);
|
|
1979
1997
|
};
|
|
1980
1998
|
|
|
1981
1999
|
const isWindows$3 = process.platform === "win32";
|
|
@@ -4363,7 +4363,12 @@ const getDirectoryContentItems = ({
|
|
|
4363
4363
|
fileUrls.push(fileUrlObject);
|
|
4364
4364
|
}
|
|
4365
4365
|
}
|
|
4366
|
-
|
|
4366
|
+
// Not the default numeric order: what is drawn here is a directory as a human
|
|
4367
|
+
// reads it, so a leading number is part of the name ("10_a" right after
|
|
4368
|
+
// "1_a") the way the filesystem and the editor next to this browser show it.
|
|
4369
|
+
fileUrls.sort((leftUrl, rightUrl) =>
|
|
4370
|
+
compareFileUrls(leftUrl, rightUrl, { numeric: false }),
|
|
4371
|
+
);
|
|
4367
4372
|
|
|
4368
4373
|
const items = [];
|
|
4369
4374
|
for (const fileUrl of fileUrls) {
|
|
@@ -7165,6 +7170,7 @@ const jsenvPluginHotSearchParam = () => {
|
|
|
7165
7170
|
modifiedTimestamp,
|
|
7166
7171
|
descendantModifiedTimestamp,
|
|
7167
7172
|
dereferencedTimestamp,
|
|
7173
|
+
servedWithoutHotTimestamp,
|
|
7168
7174
|
} = referencedUrlInfo;
|
|
7169
7175
|
if (
|
|
7170
7176
|
!modifiedTimestamp &&
|
|
@@ -7188,6 +7194,18 @@ const jsenvPluginHotSearchParam = () => {
|
|
|
7188
7194
|
descendantModifiedTimestamp,
|
|
7189
7195
|
dereferencedTimestamp,
|
|
7190
7196
|
);
|
|
7197
|
+
// These timestamps say "this url changed at some point", not "the client
|
|
7198
|
+
// is running an outdated version of it": they are never cleared, so a
|
|
7199
|
+
// file modified once keeps them for the rest of the dev server's life.
|
|
7200
|
+
// A client that fetched this url after that modification (a page load:
|
|
7201
|
+
// see rememberServedWithoutHot in the dev server) already runs the
|
|
7202
|
+
// latest content. Sending it "?hot" then makes the browser evaluate a
|
|
7203
|
+
// second, identical copy of a module it already has — a second module
|
|
7204
|
+
// scope for a file that never changed, which breaks everything a module
|
|
7205
|
+
// holds once (registries, contexts, singletons).
|
|
7206
|
+
if (latestTimestamp <= servedWithoutHotTimestamp) {
|
|
7207
|
+
return null;
|
|
7208
|
+
}
|
|
7191
7209
|
return {
|
|
7192
7210
|
hot: latestTimestamp,
|
|
7193
7211
|
};
|
|
@@ -9670,6 +9688,10 @@ const createUrlInfo = (url, context) => {
|
|
|
9670
9688
|
modifiedTimestamp: 0,
|
|
9671
9689
|
descendantModifiedTimestamp: 0,
|
|
9672
9690
|
dereferencedTimestamp: 0,
|
|
9691
|
+
// when a client last fetched this url outside a hot request; in other
|
|
9692
|
+
// words the last time this url entered a fresh page (see
|
|
9693
|
+
// jsenv_plugin_hot_search_param)
|
|
9694
|
+
servedWithoutHotTimestamp: 0,
|
|
9673
9695
|
originalContentEtag: null,
|
|
9674
9696
|
contentEtag: null,
|
|
9675
9697
|
isValid: () => false,
|
|
@@ -11917,7 +11939,20 @@ const devServerPluginServeSourceFiles = ({
|
|
|
11917
11939
|
);
|
|
11918
11940
|
return response;
|
|
11919
11941
|
};
|
|
11942
|
+
// What the client holds for this url is what we last sent it: a
|
|
11943
|
+
// request without "?hot" is a page loading this url into an empty
|
|
11944
|
+
// module registry, so from here on the client runs this exact
|
|
11945
|
+
// content. Remembering when that happened is what allows
|
|
11946
|
+
// jsenv_plugin_hot_search_param to tell a modification the client
|
|
11947
|
+
// has already received from one it must re-execute to see.
|
|
11948
|
+
const rememberServedWithoutHot = () => {
|
|
11949
|
+
if (request.searchParams.has("hot")) {
|
|
11950
|
+
return;
|
|
11951
|
+
}
|
|
11952
|
+
urlInfo.servedWithoutHotTimestamp = Date.now();
|
|
11953
|
+
};
|
|
11920
11954
|
const respondWithNotModified = () => {
|
|
11955
|
+
rememberServedWithoutHot();
|
|
11921
11956
|
const headers = {
|
|
11922
11957
|
"cache-control": `private,max-age=0,must-revalidate`,
|
|
11923
11958
|
};
|
|
@@ -12002,6 +12037,9 @@ const devServerPluginServeSourceFiles = ({
|
|
|
12002
12037
|
) {
|
|
12003
12038
|
return respondWithNotModified();
|
|
12004
12039
|
}
|
|
12040
|
+
if (urlInfo.status === 200) {
|
|
12041
|
+
rememberServedWithoutHot();
|
|
12042
|
+
}
|
|
12005
12043
|
response = {
|
|
12006
12044
|
url: reference.url,
|
|
12007
12045
|
// a plugin can cook a complete response body for an url that is
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/core",
|
|
3
|
-
"version": "41.5.
|
|
3
|
+
"version": "41.5.15",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Tool to develop, test and build js projects",
|
|
6
6
|
"repository": {
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"dependencies": {
|
|
76
76
|
"@jsenv/ast": "6.10.3",
|
|
77
77
|
"@jsenv/js-module-fallback": "1.6.7",
|
|
78
|
-
"@jsenv/plugin-bundling": "2.10.
|
|
78
|
+
"@jsenv/plugin-bundling": "2.10.27",
|
|
79
79
|
"@jsenv/plugin-minification": "1.7.20",
|
|
80
80
|
"@jsenv/plugin-supervisor": "1.8.22",
|
|
81
81
|
"@jsenv/plugin-transpilation": "1.7.7",
|
|
@@ -387,7 +387,20 @@ export const devServerPluginServeSourceFiles = ({
|
|
|
387
387
|
);
|
|
388
388
|
return response;
|
|
389
389
|
};
|
|
390
|
+
// What the client holds for this url is what we last sent it: a
|
|
391
|
+
// request without "?hot" is a page loading this url into an empty
|
|
392
|
+
// module registry, so from here on the client runs this exact
|
|
393
|
+
// content. Remembering when that happened is what allows
|
|
394
|
+
// jsenv_plugin_hot_search_param to tell a modification the client
|
|
395
|
+
// has already received from one it must re-execute to see.
|
|
396
|
+
const rememberServedWithoutHot = () => {
|
|
397
|
+
if (request.searchParams.has("hot")) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
urlInfo.servedWithoutHotTimestamp = Date.now();
|
|
401
|
+
};
|
|
390
402
|
const respondWithNotModified = () => {
|
|
403
|
+
rememberServedWithoutHot();
|
|
391
404
|
const headers = {
|
|
392
405
|
"cache-control": `private,max-age=0,must-revalidate`,
|
|
393
406
|
};
|
|
@@ -472,6 +485,9 @@ export const devServerPluginServeSourceFiles = ({
|
|
|
472
485
|
) {
|
|
473
486
|
return respondWithNotModified();
|
|
474
487
|
}
|
|
488
|
+
if (urlInfo.status === 200) {
|
|
489
|
+
rememberServedWithoutHot();
|
|
490
|
+
}
|
|
475
491
|
response = {
|
|
476
492
|
url: reference.url,
|
|
477
493
|
// a plugin can cook a complete response body for an url that is
|
|
@@ -183,6 +183,10 @@ const createUrlInfo = (url, context) => {
|
|
|
183
183
|
modifiedTimestamp: 0,
|
|
184
184
|
descendantModifiedTimestamp: 0,
|
|
185
185
|
dereferencedTimestamp: 0,
|
|
186
|
+
// when a client last fetched this url outside a hot request; in other
|
|
187
|
+
// words the last time this url entered a fresh page (see
|
|
188
|
+
// jsenv_plugin_hot_search_param)
|
|
189
|
+
servedWithoutHotTimestamp: 0,
|
|
186
190
|
originalContentEtag: null,
|
|
187
191
|
contentEtag: null,
|
|
188
192
|
isValid: () => false,
|
|
@@ -48,6 +48,7 @@ export const jsenvPluginHotSearchParam = () => {
|
|
|
48
48
|
modifiedTimestamp,
|
|
49
49
|
descendantModifiedTimestamp,
|
|
50
50
|
dereferencedTimestamp,
|
|
51
|
+
servedWithoutHotTimestamp,
|
|
51
52
|
} = referencedUrlInfo;
|
|
52
53
|
if (
|
|
53
54
|
!modifiedTimestamp &&
|
|
@@ -71,6 +72,18 @@ export const jsenvPluginHotSearchParam = () => {
|
|
|
71
72
|
descendantModifiedTimestamp,
|
|
72
73
|
dereferencedTimestamp,
|
|
73
74
|
);
|
|
75
|
+
// These timestamps say "this url changed at some point", not "the client
|
|
76
|
+
// is running an outdated version of it": they are never cleared, so a
|
|
77
|
+
// file modified once keeps them for the rest of the dev server's life.
|
|
78
|
+
// A client that fetched this url after that modification (a page load:
|
|
79
|
+
// see rememberServedWithoutHot in the dev server) already runs the
|
|
80
|
+
// latest content. Sending it "?hot" then makes the browser evaluate a
|
|
81
|
+
// second, identical copy of a module it already has — a second module
|
|
82
|
+
// scope for a file that never changed, which breaks everything a module
|
|
83
|
+
// holds once (registries, contexts, singletons).
|
|
84
|
+
if (latestTimestamp <= servedWithoutHotTimestamp) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
74
87
|
return {
|
|
75
88
|
hot: latestTimestamp,
|
|
76
89
|
};
|
|
@@ -456,7 +456,12 @@ const getDirectoryContentItems = ({
|
|
|
456
456
|
fileUrls.push(fileUrlObject);
|
|
457
457
|
}
|
|
458
458
|
}
|
|
459
|
-
|
|
459
|
+
// Not the default numeric order: what is drawn here is a directory as a human
|
|
460
|
+
// reads it, so a leading number is part of the name ("10_a" right after
|
|
461
|
+
// "1_a") the way the filesystem and the editor next to this browser show it.
|
|
462
|
+
fileUrls.sort((leftUrl, rightUrl) =>
|
|
463
|
+
compareFileUrls(leftUrl, rightUrl, { numeric: false }),
|
|
464
|
+
);
|
|
460
465
|
|
|
461
466
|
const items = [];
|
|
462
467
|
for (const fileUrl of fileUrls) {
|