@jsenv/core 28.3.0 → 28.3.4
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/main.js +125 -106
- package/package.json +1 -1
- package/src/omega/kitchen.js +19 -27
- package/src/omega/server/file_service.js +30 -13
- package/src/omega/url_graph/url_info_transformations.js +50 -47
- package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +31 -20
- package/src/plugins/importmap/jsenv_plugin_importmap.js +3 -2
package/dist/main.js
CHANGED
|
@@ -11940,7 +11940,7 @@ const jsenvPluginImportmap = () => {
|
|
|
11940
11940
|
// We must precook the importmap to know its content and inline it into the HTML
|
|
11941
11941
|
// In this situation the ref to the importmap was already discovered
|
|
11942
11942
|
// when parsing the HTML
|
|
11943
|
-
const importmapReference = context.referenceUtils.
|
|
11943
|
+
const importmapReference = context.referenceUtils.find(ref => ref.generatedSpecifier === src);
|
|
11944
11944
|
const importmapUrlInfo = context.urlGraph.getUrlInfo(importmapReference.url);
|
|
11945
11945
|
await context.cook(importmapUrlInfo, {
|
|
11946
11946
|
reference: importmapReference
|
|
@@ -16998,28 +16998,36 @@ const jsenvPluginAutoreloadServer = ({
|
|
|
16998
16998
|
url,
|
|
16999
16999
|
event
|
|
17000
17000
|
}) => {
|
|
17001
|
-
const
|
|
17001
|
+
const onUrlInfo = urlInfo => {
|
|
17002
|
+
const relativeUrl = formatUrlForClient(url);
|
|
17003
|
+
const hotUpdate = propagateUpdate(urlInfo);
|
|
17002
17004
|
|
|
17003
|
-
|
|
17004
|
-
|
|
17005
|
-
|
|
17005
|
+
if (hotUpdate.declined) {
|
|
17006
|
+
notifyDeclined({
|
|
17007
|
+
cause: `${relativeUrl} ${event}`,
|
|
17008
|
+
reason: hotUpdate.reason,
|
|
17009
|
+
declinedBy: hotUpdate.declinedBy
|
|
17010
|
+
});
|
|
17011
|
+
} else {
|
|
17012
|
+
notifyAccepted({
|
|
17013
|
+
cause: `${relativeUrl} ${event}`,
|
|
17014
|
+
reason: hotUpdate.reason,
|
|
17015
|
+
instructions: hotUpdate.instructions
|
|
17016
|
+
});
|
|
17017
|
+
}
|
|
17018
|
+
};
|
|
17006
17019
|
|
|
17007
|
-
|
|
17008
|
-
|
|
17020
|
+
urlGraph.urlInfoMap.forEach(urlInfo => {
|
|
17021
|
+
if (urlInfo.url === url) {
|
|
17022
|
+
onUrlInfo(urlInfo);
|
|
17023
|
+
} else {
|
|
17024
|
+
const urlWithoutSearch = asUrlWithoutSearch(urlInfo.url);
|
|
17009
17025
|
|
|
17010
|
-
|
|
17011
|
-
|
|
17012
|
-
|
|
17013
|
-
|
|
17014
|
-
|
|
17015
|
-
});
|
|
17016
|
-
} else {
|
|
17017
|
-
notifyAccepted({
|
|
17018
|
-
cause: `${relativeUrl} ${event}`,
|
|
17019
|
-
reason: hotUpdate.reason,
|
|
17020
|
-
instructions: hotUpdate.instructions
|
|
17021
|
-
});
|
|
17022
|
-
}
|
|
17026
|
+
if (urlWithoutSearch === url) {
|
|
17027
|
+
onUrlInfo(urlInfo);
|
|
17028
|
+
}
|
|
17029
|
+
}
|
|
17030
|
+
});
|
|
17023
17031
|
});
|
|
17024
17032
|
clientFilesPruneCallbackList.push((prunedUrlInfos, firstUrlInfo) => {
|
|
17025
17033
|
const mainHotUpdate = propagateUpdate(firstUrlInfo);
|
|
@@ -17800,7 +17808,7 @@ const createUrlInfoTransformer = ({
|
|
|
17800
17808
|
// jsenv won't emit a warning and use the following strategy:
|
|
17801
17809
|
// "no sourcemap is better than wrong sourcemap"
|
|
17802
17810
|
|
|
17803
|
-
urlInfo.sourcemapIsWrong = sourcemapIsWrong;
|
|
17811
|
+
urlInfo.sourcemapIsWrong = urlInfo.sourcemapIsWrong || sourcemapIsWrong;
|
|
17804
17812
|
}
|
|
17805
17813
|
};
|
|
17806
17814
|
|
|
@@ -17809,52 +17817,54 @@ const createUrlInfoTransformer = ({
|
|
|
17809
17817
|
applyIntermediateTransformations(urlInfo, transformations);
|
|
17810
17818
|
}
|
|
17811
17819
|
|
|
17812
|
-
if (
|
|
17813
|
-
|
|
17814
|
-
|
|
17815
|
-
|
|
17816
|
-
|
|
17817
|
-
|
|
17818
|
-
|
|
17819
|
-
|
|
17820
|
-
|
|
17821
|
-
|
|
17822
|
-
|
|
17823
|
-
|
|
17824
|
-
|
|
17825
|
-
|
|
17826
|
-
|
|
17827
|
-
|
|
17828
|
-
|
|
17820
|
+
if (urlInfo.sourcemapReference) {
|
|
17821
|
+
if (sourcemapsEnabled && urlInfo.sourcemap && !urlInfo.generatedUrl.startsWith("data:")) {
|
|
17822
|
+
// during build this function can be called after the file is cooked
|
|
17823
|
+
// - to update content and sourcemap after "optimize" hook
|
|
17824
|
+
// - to inject versioning into the entry point content
|
|
17825
|
+
// in this scenarion we don't want to call injectSourcemap
|
|
17826
|
+
// just update the content and the
|
|
17827
|
+
const sourcemapReference = urlInfo.sourcemapReference;
|
|
17828
|
+
const sourcemapUrlInfo = urlGraph.getUrlInfo(sourcemapReference.url);
|
|
17829
|
+
sourcemapUrlInfo.contentType = "application/json";
|
|
17830
|
+
const sourcemap = urlInfo.sourcemap;
|
|
17831
|
+
|
|
17832
|
+
if (sourcemapsRelativeSources) {
|
|
17833
|
+
sourcemap.sources = sourcemap.sources.map(source => {
|
|
17834
|
+
const sourceRelative = urlToRelativeUrl(source, urlInfo.url);
|
|
17835
|
+
return sourceRelative || ".";
|
|
17836
|
+
});
|
|
17837
|
+
}
|
|
17829
17838
|
|
|
17830
|
-
|
|
17831
|
-
|
|
17832
|
-
|
|
17833
|
-
|
|
17834
|
-
|
|
17839
|
+
if (sourcemapsSourcesProtocol !== "file:///") {
|
|
17840
|
+
sourcemap.sources = sourcemap.sources.map(source => {
|
|
17841
|
+
if (source.startsWith("file:///")) {
|
|
17842
|
+
return `${sourcemapsSourcesProtocol}${source.slice("file:///".length)}`;
|
|
17843
|
+
}
|
|
17835
17844
|
|
|
17836
|
-
|
|
17837
|
-
|
|
17838
|
-
|
|
17845
|
+
return source;
|
|
17846
|
+
});
|
|
17847
|
+
}
|
|
17839
17848
|
|
|
17840
|
-
|
|
17849
|
+
sourcemapUrlInfo.content = JSON.stringify(sourcemap, null, " ");
|
|
17841
17850
|
|
|
17842
|
-
|
|
17843
|
-
|
|
17844
|
-
|
|
17845
|
-
|
|
17851
|
+
if (!urlInfo.sourcemapIsWrong) {
|
|
17852
|
+
if (sourcemaps === "inline") {
|
|
17853
|
+
sourcemapReference.generatedSpecifier = generateSourcemapDataUrl(sourcemap);
|
|
17854
|
+
}
|
|
17846
17855
|
|
|
17847
|
-
|
|
17848
|
-
|
|
17849
|
-
|
|
17850
|
-
|
|
17851
|
-
|
|
17852
|
-
|
|
17856
|
+
if (sourcemaps === "file" || sourcemaps === "inline") {
|
|
17857
|
+
urlInfo.content = SOURCEMAP.writeComment({
|
|
17858
|
+
contentType: urlInfo.contentType,
|
|
17859
|
+
content: urlInfo.content,
|
|
17860
|
+
specifier: sourcemaps === "file" && sourcemapsRelativeSources ? urlToRelativeUrl(sourcemapReference.url, urlInfo.url) : sourcemapReference.generatedSpecifier
|
|
17861
|
+
});
|
|
17862
|
+
}
|
|
17853
17863
|
}
|
|
17864
|
+
} else {
|
|
17865
|
+
// in the end we don't use the sourcemap placeholder
|
|
17866
|
+
urlGraph.deleteUrlInfo(urlInfo.sourcemapReference.url);
|
|
17854
17867
|
}
|
|
17855
|
-
} else if (urlInfo.sourcemapReference) {
|
|
17856
|
-
// in the end we don't use the sourcemap placeholder
|
|
17857
|
-
urlGraph.deleteUrlInfo(urlInfo.sourcemapReference.url);
|
|
17858
17868
|
}
|
|
17859
17869
|
|
|
17860
17870
|
urlInfo.contentEtag = urlInfo.content === urlInfo.originalContent ? urlInfo.originalContentEtag : bufferToEtag$1(Buffer.from(urlInfo.content));
|
|
@@ -18632,6 +18642,8 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
|
|
|
18632
18642
|
const references = [];
|
|
18633
18643
|
context.referenceUtils = {
|
|
18634
18644
|
_references: references,
|
|
18645
|
+
find: predicate => references.find(predicate),
|
|
18646
|
+
readGeneratedSpecifier,
|
|
18635
18647
|
add: props => {
|
|
18636
18648
|
const [reference, referencedUrlInfo] = resolveReference(createReference({
|
|
18637
18649
|
parentUrl: urlInfo.url,
|
|
@@ -18640,8 +18652,6 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
|
|
|
18640
18652
|
references.push(reference);
|
|
18641
18653
|
return [reference, referencedUrlInfo];
|
|
18642
18654
|
},
|
|
18643
|
-
find: predicate => references.find(predicate),
|
|
18644
|
-
readGeneratedSpecifier,
|
|
18645
18655
|
found: ({
|
|
18646
18656
|
trace,
|
|
18647
18657
|
...rest
|
|
@@ -18703,6 +18713,29 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
|
|
|
18703
18713
|
|
|
18704
18714
|
return [newReference, newUrlInfo];
|
|
18705
18715
|
},
|
|
18716
|
+
inject: ({
|
|
18717
|
+
trace,
|
|
18718
|
+
...rest
|
|
18719
|
+
}) => {
|
|
18720
|
+
if (trace === undefined) {
|
|
18721
|
+
const {
|
|
18722
|
+
url,
|
|
18723
|
+
line,
|
|
18724
|
+
column
|
|
18725
|
+
} = getCallerPosition();
|
|
18726
|
+
trace = traceFromUrlSite({
|
|
18727
|
+
url,
|
|
18728
|
+
line,
|
|
18729
|
+
column
|
|
18730
|
+
});
|
|
18731
|
+
}
|
|
18732
|
+
|
|
18733
|
+
return context.referenceUtils.add({
|
|
18734
|
+
trace,
|
|
18735
|
+
injected: true,
|
|
18736
|
+
...rest
|
|
18737
|
+
});
|
|
18738
|
+
},
|
|
18706
18739
|
becomesInline: (reference, {
|
|
18707
18740
|
isOriginalPosition,
|
|
18708
18741
|
specifier,
|
|
@@ -18729,37 +18762,8 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
|
|
|
18729
18762
|
content
|
|
18730
18763
|
});
|
|
18731
18764
|
},
|
|
18732
|
-
|
|
18733
|
-
|
|
18734
|
-
...rest
|
|
18735
|
-
}) => {
|
|
18736
|
-
if (trace === undefined) {
|
|
18737
|
-
const {
|
|
18738
|
-
url,
|
|
18739
|
-
line,
|
|
18740
|
-
column
|
|
18741
|
-
} = getCallerPosition();
|
|
18742
|
-
trace = traceFromUrlSite({
|
|
18743
|
-
url,
|
|
18744
|
-
line,
|
|
18745
|
-
column
|
|
18746
|
-
});
|
|
18747
|
-
}
|
|
18748
|
-
|
|
18749
|
-
return context.referenceUtils.add({
|
|
18750
|
-
trace,
|
|
18751
|
-
injected: true,
|
|
18752
|
-
...rest
|
|
18753
|
-
});
|
|
18754
|
-
},
|
|
18755
|
-
findByGeneratedSpecifier: generatedSpecifier => {
|
|
18756
|
-
const reference = references.find(ref => ref.generatedSpecifier === generatedSpecifier);
|
|
18757
|
-
|
|
18758
|
-
if (!reference) {
|
|
18759
|
-
throw new Error(`No reference found using the following generatedSpecifier: "${generatedSpecifier}"`);
|
|
18760
|
-
}
|
|
18761
|
-
|
|
18762
|
-
return reference;
|
|
18765
|
+
becomesExternal: () => {
|
|
18766
|
+
throw new Error("not implemented yet");
|
|
18763
18767
|
}
|
|
18764
18768
|
}; // "fetchUrlContent" hook
|
|
18765
18769
|
|
|
@@ -25305,11 +25309,17 @@ const createFileService = ({
|
|
|
25305
25309
|
clientFileChangeCallbackList.push(({
|
|
25306
25310
|
url
|
|
25307
25311
|
}) => {
|
|
25308
|
-
|
|
25312
|
+
urlGraph.urlInfoMap.forEach(urlInfo => {
|
|
25313
|
+
if (urlInfo.url === url) {
|
|
25314
|
+
urlGraph.considerModified(urlInfo);
|
|
25315
|
+
} else {
|
|
25316
|
+
const urlWithoutSearch = asUrlWithoutSearch(urlInfo.url);
|
|
25309
25317
|
|
|
25310
|
-
|
|
25311
|
-
|
|
25312
|
-
|
|
25318
|
+
if (urlWithoutSearch === url) {
|
|
25319
|
+
urlGraph.considerModified(urlInfo);
|
|
25320
|
+
}
|
|
25321
|
+
}
|
|
25322
|
+
});
|
|
25313
25323
|
});
|
|
25314
25324
|
const kitchen = createKitchen({
|
|
25315
25325
|
signal,
|
|
@@ -25486,22 +25496,30 @@ const createFileService = ({
|
|
|
25486
25496
|
const urlInfoTargetedByCache = urlGraph.getParentIfInline(urlInfo);
|
|
25487
25497
|
|
|
25488
25498
|
if (ifNoneMatch) {
|
|
25489
|
-
|
|
25499
|
+
const [clientOriginalContentEtag, clientContentEtag] = ifNoneMatch.split("_");
|
|
25500
|
+
|
|
25501
|
+
if (urlInfoTargetedByCache.originalContentEtag === clientOriginalContentEtag && urlInfoTargetedByCache.contentEtag === clientContentEtag && urlInfoTargetedByCache.isValid()) {
|
|
25502
|
+
const headers = {
|
|
25503
|
+
"cache-control": `private,max-age=0,must-revalidate`
|
|
25504
|
+
};
|
|
25505
|
+
Object.keys(urlInfo.headers).forEach(key => {
|
|
25506
|
+
if (key !== "content-length") {
|
|
25507
|
+
headers[key] = urlInfo.headers[key];
|
|
25508
|
+
}
|
|
25509
|
+
});
|
|
25490
25510
|
return {
|
|
25491
25511
|
status: 304,
|
|
25492
|
-
headers
|
|
25493
|
-
"cache-control": `private,max-age=0,must-revalidate`,
|
|
25494
|
-
...urlInfo.headers
|
|
25495
|
-
}
|
|
25512
|
+
headers
|
|
25496
25513
|
};
|
|
25497
25514
|
}
|
|
25498
25515
|
}
|
|
25499
25516
|
|
|
25500
25517
|
try {
|
|
25501
25518
|
// urlInfo objects are reused, they must be "reset" before cooking them again
|
|
25502
|
-
if (urlInfo.contentEtag && !urlInfo.isInline && urlInfo.type !== "sourcemap") {
|
|
25519
|
+
if ((urlInfo.error || urlInfo.contentEtag) && !urlInfo.isInline && urlInfo.type !== "sourcemap") {
|
|
25503
25520
|
urlInfo.error = null;
|
|
25504
25521
|
urlInfo.sourcemap = null;
|
|
25522
|
+
urlInfo.sourcemapIsWrong = null;
|
|
25505
25523
|
urlInfo.sourcemapReference = null;
|
|
25506
25524
|
urlInfo.content = null;
|
|
25507
25525
|
urlInfo.originalContent = null;
|
|
@@ -25526,11 +25544,12 @@ const createFileService = ({
|
|
|
25526
25544
|
url: reference.url,
|
|
25527
25545
|
status: 200,
|
|
25528
25546
|
headers: {
|
|
25529
|
-
"content-length": Buffer.byteLength(urlInfo.content),
|
|
25530
25547
|
"cache-control": `private,max-age=0,must-revalidate`,
|
|
25531
|
-
"
|
|
25548
|
+
// it's safe to use "_" separator because etag is encoded with base64 (see https://stackoverflow.com/a/13195197)
|
|
25549
|
+
"eTag": `${urlInfoTargetedByCache.originalContentEtag}_${urlInfoTargetedByCache.contentEtag}`,
|
|
25532
25550
|
...urlInfo.headers,
|
|
25533
|
-
"content-type": urlInfo.contentType
|
|
25551
|
+
"content-type": urlInfo.contentType,
|
|
25552
|
+
"content-length": Buffer.byteLength(urlInfo.content)
|
|
25534
25553
|
},
|
|
25535
25554
|
body: urlInfo.content,
|
|
25536
25555
|
timing: urlInfo.timing
|
package/package.json
CHANGED
package/src/omega/kitchen.js
CHANGED
|
@@ -429,6 +429,8 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
|
|
|
429
429
|
const references = []
|
|
430
430
|
context.referenceUtils = {
|
|
431
431
|
_references: references,
|
|
432
|
+
find: (predicate) => references.find(predicate),
|
|
433
|
+
readGeneratedSpecifier,
|
|
432
434
|
add: (props) => {
|
|
433
435
|
const [reference, referencedUrlInfo] = resolveReference(
|
|
434
436
|
createReference({
|
|
@@ -440,8 +442,6 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
|
|
|
440
442
|
references.push(reference)
|
|
441
443
|
return [reference, referencedUrlInfo]
|
|
442
444
|
},
|
|
443
|
-
find: (predicate) => references.find(predicate),
|
|
444
|
-
readGeneratedSpecifier,
|
|
445
445
|
found: ({ trace, ...rest }) => {
|
|
446
446
|
if (trace === undefined) {
|
|
447
447
|
trace = traceFromUrlSite(
|
|
@@ -511,6 +511,21 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
|
|
|
511
511
|
}
|
|
512
512
|
return [newReference, newUrlInfo]
|
|
513
513
|
},
|
|
514
|
+
inject: ({ trace, ...rest }) => {
|
|
515
|
+
if (trace === undefined) {
|
|
516
|
+
const { url, line, column } = getCallerPosition()
|
|
517
|
+
trace = traceFromUrlSite({
|
|
518
|
+
url,
|
|
519
|
+
line,
|
|
520
|
+
column,
|
|
521
|
+
})
|
|
522
|
+
}
|
|
523
|
+
return context.referenceUtils.add({
|
|
524
|
+
trace,
|
|
525
|
+
injected: true,
|
|
526
|
+
...rest,
|
|
527
|
+
})
|
|
528
|
+
},
|
|
514
529
|
becomesInline: (
|
|
515
530
|
reference,
|
|
516
531
|
{
|
|
@@ -544,31 +559,8 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
|
|
|
544
559
|
content,
|
|
545
560
|
})
|
|
546
561
|
},
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
const { url, line, column } = getCallerPosition()
|
|
550
|
-
trace = traceFromUrlSite({
|
|
551
|
-
url,
|
|
552
|
-
line,
|
|
553
|
-
column,
|
|
554
|
-
})
|
|
555
|
-
}
|
|
556
|
-
return context.referenceUtils.add({
|
|
557
|
-
trace,
|
|
558
|
-
injected: true,
|
|
559
|
-
...rest,
|
|
560
|
-
})
|
|
561
|
-
},
|
|
562
|
-
findByGeneratedSpecifier: (generatedSpecifier) => {
|
|
563
|
-
const reference = references.find(
|
|
564
|
-
(ref) => ref.generatedSpecifier === generatedSpecifier,
|
|
565
|
-
)
|
|
566
|
-
if (!reference) {
|
|
567
|
-
throw new Error(
|
|
568
|
-
`No reference found using the following generatedSpecifier: "${generatedSpecifier}"`,
|
|
569
|
-
)
|
|
570
|
-
}
|
|
571
|
-
return reference
|
|
562
|
+
becomesExternal: () => {
|
|
563
|
+
throw new Error("not implemented yet")
|
|
572
564
|
},
|
|
573
565
|
}
|
|
574
566
|
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
composeTwoResponses,
|
|
6
6
|
} from "@jsenv/server"
|
|
7
7
|
import { registerDirectoryLifecycle, bufferToEtag } from "@jsenv/filesystem"
|
|
8
|
-
import { urlIsInsideOf, moveUrl } from "@jsenv/urls"
|
|
8
|
+
import { urlIsInsideOf, moveUrl, asUrlWithoutSearch } from "@jsenv/urls"
|
|
9
9
|
import { URL_META } from "@jsenv/url-meta"
|
|
10
10
|
|
|
11
11
|
import { getCorePlugins } from "@jsenv/core/src/plugins/plugins.js"
|
|
@@ -95,10 +95,16 @@ export const createFileService = ({
|
|
|
95
95
|
)
|
|
96
96
|
const urlGraph = createUrlGraph()
|
|
97
97
|
clientFileChangeCallbackList.push(({ url }) => {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
urlGraph.urlInfoMap.forEach((urlInfo) => {
|
|
99
|
+
if (urlInfo.url === url) {
|
|
100
|
+
urlGraph.considerModified(urlInfo)
|
|
101
|
+
} else {
|
|
102
|
+
const urlWithoutSearch = asUrlWithoutSearch(urlInfo.url)
|
|
103
|
+
if (urlWithoutSearch === url) {
|
|
104
|
+
urlGraph.considerModified(urlInfo)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
})
|
|
102
108
|
})
|
|
103
109
|
const kitchen = createKitchen({
|
|
104
110
|
signal,
|
|
@@ -264,28 +270,38 @@ export const createFileService = ({
|
|
|
264
270
|
const urlInfoTargetedByCache = urlGraph.getParentIfInline(urlInfo)
|
|
265
271
|
|
|
266
272
|
if (ifNoneMatch) {
|
|
273
|
+
const [clientOriginalContentEtag, clientContentEtag] =
|
|
274
|
+
ifNoneMatch.split("_")
|
|
267
275
|
if (
|
|
268
|
-
urlInfoTargetedByCache.
|
|
276
|
+
urlInfoTargetedByCache.originalContentEtag ===
|
|
277
|
+
clientOriginalContentEtag &&
|
|
278
|
+
urlInfoTargetedByCache.contentEtag === clientContentEtag &&
|
|
269
279
|
urlInfoTargetedByCache.isValid()
|
|
270
280
|
) {
|
|
281
|
+
const headers = {
|
|
282
|
+
"cache-control": `private,max-age=0,must-revalidate`,
|
|
283
|
+
}
|
|
284
|
+
Object.keys(urlInfo.headers).forEach((key) => {
|
|
285
|
+
if (key !== "content-length") {
|
|
286
|
+
headers[key] = urlInfo.headers[key]
|
|
287
|
+
}
|
|
288
|
+
})
|
|
271
289
|
return {
|
|
272
290
|
status: 304,
|
|
273
|
-
headers
|
|
274
|
-
"cache-control": `private,max-age=0,must-revalidate`,
|
|
275
|
-
...urlInfo.headers,
|
|
276
|
-
},
|
|
291
|
+
headers,
|
|
277
292
|
}
|
|
278
293
|
}
|
|
279
294
|
}
|
|
280
295
|
try {
|
|
281
296
|
// urlInfo objects are reused, they must be "reset" before cooking them again
|
|
282
297
|
if (
|
|
283
|
-
urlInfo.contentEtag &&
|
|
298
|
+
(urlInfo.error || urlInfo.contentEtag) &&
|
|
284
299
|
!urlInfo.isInline &&
|
|
285
300
|
urlInfo.type !== "sourcemap"
|
|
286
301
|
) {
|
|
287
302
|
urlInfo.error = null
|
|
288
303
|
urlInfo.sourcemap = null
|
|
304
|
+
urlInfo.sourcemapIsWrong = null
|
|
289
305
|
urlInfo.sourcemapReference = null
|
|
290
306
|
urlInfo.content = null
|
|
291
307
|
urlInfo.originalContent = null
|
|
@@ -305,11 +321,12 @@ export const createFileService = ({
|
|
|
305
321
|
url: reference.url,
|
|
306
322
|
status: 200,
|
|
307
323
|
headers: {
|
|
308
|
-
"content-length": Buffer.byteLength(urlInfo.content),
|
|
309
324
|
"cache-control": `private,max-age=0,must-revalidate`,
|
|
310
|
-
"
|
|
325
|
+
// it's safe to use "_" separator because etag is encoded with base64 (see https://stackoverflow.com/a/13195197)
|
|
326
|
+
"eTag": `${urlInfoTargetedByCache.originalContentEtag}_${urlInfoTargetedByCache.contentEtag}`,
|
|
311
327
|
...urlInfo.headers,
|
|
312
328
|
"content-type": urlInfo.contentType,
|
|
329
|
+
"content-length": Buffer.byteLength(urlInfo.content),
|
|
313
330
|
},
|
|
314
331
|
body: urlInfo.content,
|
|
315
332
|
timing: urlInfo.timing,
|
|
@@ -164,7 +164,7 @@ export const createUrlInfoTransformer = ({
|
|
|
164
164
|
// is a nightmare no-one could solve in years so
|
|
165
165
|
// jsenv won't emit a warning and use the following strategy:
|
|
166
166
|
// "no sourcemap is better than wrong sourcemap"
|
|
167
|
-
urlInfo.sourcemapIsWrong = sourcemapIsWrong
|
|
167
|
+
urlInfo.sourcemapIsWrong = urlInfo.sourcemapIsWrong || sourcemapIsWrong
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
|
|
@@ -172,57 +172,60 @@ export const createUrlInfoTransformer = ({
|
|
|
172
172
|
if (transformations) {
|
|
173
173
|
applyIntermediateTransformations(urlInfo, transformations)
|
|
174
174
|
}
|
|
175
|
-
if (
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (sourcemapsSourcesProtocol !== "file:///") {
|
|
196
|
-
sourcemap.sources = sourcemap.sources.map((source) => {
|
|
197
|
-
if (source.startsWith("file:///")) {
|
|
198
|
-
return `${sourcemapsSourcesProtocol}${source.slice(
|
|
199
|
-
"file:///".length,
|
|
200
|
-
)}`
|
|
201
|
-
}
|
|
202
|
-
return source
|
|
203
|
-
})
|
|
204
|
-
}
|
|
205
|
-
sourcemapUrlInfo.content = JSON.stringify(sourcemap, null, " ")
|
|
206
|
-
if (!urlInfo.sourcemapIsWrong) {
|
|
207
|
-
if (sourcemaps === "inline") {
|
|
208
|
-
sourcemapReference.generatedSpecifier =
|
|
209
|
-
generateSourcemapDataUrl(sourcemap)
|
|
175
|
+
if (urlInfo.sourcemapReference) {
|
|
176
|
+
if (
|
|
177
|
+
sourcemapsEnabled &&
|
|
178
|
+
urlInfo.sourcemap &&
|
|
179
|
+
!urlInfo.generatedUrl.startsWith("data:")
|
|
180
|
+
) {
|
|
181
|
+
// during build this function can be called after the file is cooked
|
|
182
|
+
// - to update content and sourcemap after "optimize" hook
|
|
183
|
+
// - to inject versioning into the entry point content
|
|
184
|
+
// in this scenarion we don't want to call injectSourcemap
|
|
185
|
+
// just update the content and the
|
|
186
|
+
const sourcemapReference = urlInfo.sourcemapReference
|
|
187
|
+
const sourcemapUrlInfo = urlGraph.getUrlInfo(sourcemapReference.url)
|
|
188
|
+
sourcemapUrlInfo.contentType = "application/json"
|
|
189
|
+
const sourcemap = urlInfo.sourcemap
|
|
190
|
+
if (sourcemapsRelativeSources) {
|
|
191
|
+
sourcemap.sources = sourcemap.sources.map((source) => {
|
|
192
|
+
const sourceRelative = urlToRelativeUrl(source, urlInfo.url)
|
|
193
|
+
return sourceRelative || "."
|
|
194
|
+
})
|
|
210
195
|
}
|
|
211
|
-
if (
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
196
|
+
if (sourcemapsSourcesProtocol !== "file:///") {
|
|
197
|
+
sourcemap.sources = sourcemap.sources.map((source) => {
|
|
198
|
+
if (source.startsWith("file:///")) {
|
|
199
|
+
return `${sourcemapsSourcesProtocol}${source.slice(
|
|
200
|
+
"file:///".length,
|
|
201
|
+
)}`
|
|
202
|
+
}
|
|
203
|
+
return source
|
|
219
204
|
})
|
|
220
205
|
}
|
|
206
|
+
sourcemapUrlInfo.content = JSON.stringify(sourcemap, null, " ")
|
|
207
|
+
if (!urlInfo.sourcemapIsWrong) {
|
|
208
|
+
if (sourcemaps === "inline") {
|
|
209
|
+
sourcemapReference.generatedSpecifier =
|
|
210
|
+
generateSourcemapDataUrl(sourcemap)
|
|
211
|
+
}
|
|
212
|
+
if (sourcemaps === "file" || sourcemaps === "inline") {
|
|
213
|
+
urlInfo.content = SOURCEMAP.writeComment({
|
|
214
|
+
contentType: urlInfo.contentType,
|
|
215
|
+
content: urlInfo.content,
|
|
216
|
+
specifier:
|
|
217
|
+
sourcemaps === "file" && sourcemapsRelativeSources
|
|
218
|
+
? urlToRelativeUrl(sourcemapReference.url, urlInfo.url)
|
|
219
|
+
: sourcemapReference.generatedSpecifier,
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
// in the end we don't use the sourcemap placeholder
|
|
225
|
+
urlGraph.deleteUrlInfo(urlInfo.sourcemapReference.url)
|
|
221
226
|
}
|
|
222
|
-
} else if (urlInfo.sourcemapReference) {
|
|
223
|
-
// in the end we don't use the sourcemap placeholder
|
|
224
|
-
urlGraph.deleteUrlInfo(urlInfo.sourcemapReference.url)
|
|
225
227
|
}
|
|
228
|
+
|
|
226
229
|
urlInfo.contentEtag =
|
|
227
230
|
urlInfo.content === urlInfo.originalContent
|
|
228
231
|
? urlInfo.originalContentEtag
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
urlIsInsideOf,
|
|
3
|
+
urlToRelativeUrl,
|
|
4
|
+
asUrlWithoutSearch,
|
|
5
|
+
} from "@jsenv/urls"
|
|
2
6
|
|
|
3
7
|
export const jsenvPluginAutoreloadServer = ({
|
|
4
8
|
clientFileChangeCallbackList,
|
|
@@ -113,26 +117,33 @@ export const jsenvPluginAutoreloadServer = ({
|
|
|
113
117
|
return iterate(firstUrlInfo, seen)
|
|
114
118
|
}
|
|
115
119
|
clientFileChangeCallbackList.push(({ url, event }) => {
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
reason: hotUpdate.reason,
|
|
133
|
-
instructions: hotUpdate.instructions,
|
|
134
|
-
})
|
|
120
|
+
const onUrlInfo = (urlInfo) => {
|
|
121
|
+
const relativeUrl = formatUrlForClient(url)
|
|
122
|
+
const hotUpdate = propagateUpdate(urlInfo)
|
|
123
|
+
if (hotUpdate.declined) {
|
|
124
|
+
notifyDeclined({
|
|
125
|
+
cause: `${relativeUrl} ${event}`,
|
|
126
|
+
reason: hotUpdate.reason,
|
|
127
|
+
declinedBy: hotUpdate.declinedBy,
|
|
128
|
+
})
|
|
129
|
+
} else {
|
|
130
|
+
notifyAccepted({
|
|
131
|
+
cause: `${relativeUrl} ${event}`,
|
|
132
|
+
reason: hotUpdate.reason,
|
|
133
|
+
instructions: hotUpdate.instructions,
|
|
134
|
+
})
|
|
135
|
+
}
|
|
135
136
|
}
|
|
137
|
+
urlGraph.urlInfoMap.forEach((urlInfo) => {
|
|
138
|
+
if (urlInfo.url === url) {
|
|
139
|
+
onUrlInfo(urlInfo)
|
|
140
|
+
} else {
|
|
141
|
+
const urlWithoutSearch = asUrlWithoutSearch(urlInfo.url)
|
|
142
|
+
if (urlWithoutSearch === url) {
|
|
143
|
+
onUrlInfo(urlInfo)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
})
|
|
136
147
|
})
|
|
137
148
|
clientFilesPruneCallbackList.push((prunedUrlInfos, firstUrlInfo) => {
|
|
138
149
|
const mainHotUpdate = propagateUpdate(firstUrlInfo)
|
|
@@ -148,8 +148,9 @@ export const jsenvPluginImportmap = () => {
|
|
|
148
148
|
// We must precook the importmap to know its content and inline it into the HTML
|
|
149
149
|
// In this situation the ref to the importmap was already discovered
|
|
150
150
|
// when parsing the HTML
|
|
151
|
-
const importmapReference =
|
|
152
|
-
|
|
151
|
+
const importmapReference = context.referenceUtils.find(
|
|
152
|
+
(ref) => ref.generatedSpecifier === src,
|
|
153
|
+
)
|
|
153
154
|
const importmapUrlInfo = context.urlGraph.getUrlInfo(
|
|
154
155
|
importmapReference.url,
|
|
155
156
|
)
|