@jsenv/core 41.5.14 → 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.
@@ -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,
@@ -9445,6 +9449,7 @@ const jsenvPluginHotSearchParam = () => {
9445
9449
  modifiedTimestamp,
9446
9450
  descendantModifiedTimestamp,
9447
9451
  dereferencedTimestamp,
9452
+ servedWithoutHotTimestamp,
9448
9453
  } = referencedUrlInfo;
9449
9454
  if (
9450
9455
  !modifiedTimestamp &&
@@ -9468,6 +9473,18 @@ const jsenvPluginHotSearchParam = () => {
9468
9473
  descendantModifiedTimestamp,
9469
9474
  dereferencedTimestamp,
9470
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
+ }
9471
9488
  return {
9472
9489
  hot: latestTimestamp,
9473
9490
  };
@@ -7170,6 +7170,7 @@ const jsenvPluginHotSearchParam = () => {
7170
7170
  modifiedTimestamp,
7171
7171
  descendantModifiedTimestamp,
7172
7172
  dereferencedTimestamp,
7173
+ servedWithoutHotTimestamp,
7173
7174
  } = referencedUrlInfo;
7174
7175
  if (
7175
7176
  !modifiedTimestamp &&
@@ -7193,6 +7194,18 @@ const jsenvPluginHotSearchParam = () => {
7193
7194
  descendantModifiedTimestamp,
7194
7195
  dereferencedTimestamp,
7195
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
+ }
7196
7209
  return {
7197
7210
  hot: latestTimestamp,
7198
7211
  };
@@ -9675,6 +9688,10 @@ const createUrlInfo = (url, context) => {
9675
9688
  modifiedTimestamp: 0,
9676
9689
  descendantModifiedTimestamp: 0,
9677
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,
9678
9695
  originalContentEtag: null,
9679
9696
  contentEtag: null,
9680
9697
  isValid: () => false,
@@ -11922,7 +11939,20 @@ const devServerPluginServeSourceFiles = ({
11922
11939
  );
11923
11940
  return response;
11924
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
+ };
11925
11954
  const respondWithNotModified = () => {
11955
+ rememberServedWithoutHot();
11926
11956
  const headers = {
11927
11957
  "cache-control": `private,max-age=0,must-revalidate`,
11928
11958
  };
@@ -12007,6 +12037,9 @@ const devServerPluginServeSourceFiles = ({
12007
12037
  ) {
12008
12038
  return respondWithNotModified();
12009
12039
  }
12040
+ if (urlInfo.status === 200) {
12041
+ rememberServedWithoutHot();
12042
+ }
12010
12043
  response = {
12011
12044
  url: reference.url,
12012
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.14",
3
+ "version": "41.5.15",
4
4
  "type": "module",
5
5
  "description": "Tool to develop, test and build js projects",
6
6
  "repository": {
@@ -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
  };