@jsenv/core 39.14.2 → 40.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/js/directory_listing.js +16 -9
  2. package/dist/js/server_events_client.js +2 -2
  3. package/dist/jsenv_core.js +6974 -10642
  4. package/package.json +22 -19
  5. package/src/build/build.js +122 -93
  6. package/src/build/build_specifier_manager.js +103 -94
  7. package/src/build/build_urls_generator.js +1 -1
  8. package/src/build/{version_mappings_injection.js → mappings_injection.js} +62 -21
  9. package/src/build/start_build_server.js +46 -36
  10. package/src/dev/start_dev_server.js +246 -248
  11. package/src/helpers/watch_source_files.js +50 -36
  12. package/src/kitchen/fetched_content_compliance.js +4 -2
  13. package/src/kitchen/kitchen.js +31 -24
  14. package/src/kitchen/url_graph/references.js +10 -2
  15. package/src/kitchen/url_graph/url_graph.js +3 -0
  16. package/src/kitchen/url_graph/url_graph_visitor.js +3 -0
  17. package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +29 -16
  18. package/src/plugins/html_syntax_error_fallback/jsenv_plugin_html_syntax_error_fallback.js +1 -1
  19. package/src/plugins/plugin_controller.js +194 -200
  20. package/src/plugins/plugins.js +5 -0
  21. package/src/plugins/protocol_file/client/directory_listing.jsx +5 -0
  22. package/src/plugins/protocol_file/jsenv_plugin_directory_listing.js +92 -67
  23. package/src/plugins/protocol_file/jsenv_plugin_fs_redirection.js +17 -7
  24. package/src/plugins/protocol_file/jsenv_plugin_protocol_file.js +6 -0
  25. package/src/plugins/protocol_http/jsenv_plugin_protocol_http.js +33 -3
  26. package/src/plugins/reference_analysis/html/jsenv_plugin_html_reference_analysis.js +15 -22
  27. package/src/plugins/reference_analysis/js/jsenv_plugin_js_reference_analysis.js +53 -2
  28. package/src/plugins/resolution_node_esm/jsenv_plugin_node_esm_resolution.js +37 -30
  29. package/src/plugins/resolution_node_esm/node_esm_resolver.js +4 -8
  30. package/src/plugins/resolution_web/jsenv_plugin_web_resolution.js +8 -6
  31. package/src/plugins/server_events/client/server_events_client.js +2 -2
  32. package/src/plugins/server_events/jsenv_plugin_server_events.js +18 -16
  33. package/dist/js/ws.js +0 -6863
  34. package/src/helpers/lookup_package_directory.js +0 -9
@@ -28,9 +28,9 @@ import { GRAPH_VISITOR } from "../kitchen/url_graph/url_graph_visitor.js";
28
28
  import { isWebWorkerUrlInfo } from "../kitchen/web_workers.js";
29
29
  import { createBuildUrlsGenerator } from "./build_urls_generator.js";
30
30
  import {
31
- injectVersionMappingsAsGlobal,
32
- injectVersionMappingsAsImportmap,
33
- } from "./version_mappings_injection.js";
31
+ injectGlobalMappings,
32
+ injectImportmapMappings,
33
+ } from "./mappings_injection.js";
34
34
 
35
35
  export const createBuildSpecifierManager = ({
36
36
  rawKitchen,
@@ -131,18 +131,18 @@ export const createBuildSpecifierManager = ({
131
131
  },
132
132
  urlInfosToBundle,
133
133
  );
134
- Object.keys(urlInfosBundled).forEach((url) => {
134
+ for (const url of Object.keys(urlInfosBundled)) {
135
135
  const urlInfoBundled = urlInfosBundled[url];
136
136
  if (urlInfoBundled.sourceUrls) {
137
- urlInfoBundled.sourceUrls.forEach((sourceUrl) => {
137
+ for (const sourceUrl of urlInfoBundled.sourceUrls) {
138
138
  const sourceRawUrlInfo = rawKitchen.graph.getUrlInfo(sourceUrl);
139
139
  if (sourceRawUrlInfo) {
140
140
  sourceRawUrlInfo.data.bundled = true;
141
141
  }
142
- });
142
+ }
143
143
  }
144
144
  bundleInfoMap.set(url, urlInfoBundled);
145
- });
145
+ }
146
146
  };
147
147
 
148
148
  const jsenvPluginMoveToBuildDirectory = {
@@ -338,7 +338,7 @@ export const createBuildSpecifierManager = ({
338
338
 
339
339
  const versionMap = new Map();
340
340
 
341
- const workerReferenceSet = new Set();
341
+ const referenceInSeparateContextSet = new Set();
342
342
  const referenceVersioningInfoMap = new Map();
343
343
  const _getReferenceVersioningInfo = (reference) => {
344
344
  if (!shouldApplyVersioningOnReference(reference)) {
@@ -395,7 +395,7 @@ export const createBuildSpecifierManager = ({
395
395
  },
396
396
  };
397
397
  }
398
- if (canUseImportmap && !isInsideWorker(reference)) {
398
+ if (canUseImportmap && !isInsideSeparateContext(reference)) {
399
399
  return {
400
400
  type: "importmap",
401
401
  render: (buildSpecifier) => {
@@ -422,8 +422,8 @@ export const createBuildSpecifierManager = ({
422
422
  referenceVersioningInfoMap.set(reference, info);
423
423
  return info;
424
424
  };
425
- const isInsideWorker = (reference) => {
426
- if (workerReferenceSet.has(reference)) {
425
+ const isInsideSeparateContext = (reference) => {
426
+ if (referenceInSeparateContextSet.has(reference)) {
427
427
  return true;
428
428
  }
429
429
  const referenceOwnerUrllInfo = reference.ownerUrlInfo;
@@ -443,7 +443,7 @@ export const createBuildSpecifierManager = ({
443
443
  );
444
444
  }
445
445
  if (is) {
446
- workerReferenceSet.add(reference);
446
+ referenceInSeparateContextSet.add(reference);
447
447
  return true;
448
448
  }
449
449
  return false;
@@ -646,6 +646,9 @@ export const createBuildSpecifierManager = ({
646
646
  }
647
647
  };
648
648
 
649
+ const importMappings = {};
650
+ const globalMappings = {};
651
+
649
652
  const applyVersioningOnBuildSpecifier = (buildSpecifier, reference) => {
650
653
  if (!versioning) {
651
654
  return buildSpecifier;
@@ -671,10 +674,6 @@ export const createBuildSpecifierManager = ({
671
674
  };
672
675
  const finishVersioning = async () => {
673
676
  inject_global_registry_and_importmap: {
674
- const actions = [];
675
- const visitors = [];
676
- const globalMappings = {};
677
- const importmapMappings = {};
678
677
  for (const [reference, versioningInfo] of referenceVersioningInfoMap) {
679
678
  if (versioningInfo.type === "global") {
680
679
  const urlInfo = reference.urlInfo;
@@ -690,37 +689,7 @@ export const createBuildSpecifierManager = ({
690
689
  const buildSpecifier = buildUrlToBuildSpecifierMap.get(buildUrl);
691
690
  const buildSpecifierVersioned =
692
691
  buildSpecifierToBuildSpecifierVersionedMap.get(buildSpecifier);
693
- importmapMappings[buildSpecifier] = buildSpecifierVersioned;
694
- }
695
- }
696
- if (Object.keys(globalMappings).length > 0) {
697
- visitors.push((urlInfo) => {
698
- if (urlInfo.isEntryPoint) {
699
- actions.push(async () => {
700
- await injectVersionMappingsAsGlobal(urlInfo, globalMappings);
701
- });
702
- }
703
- });
704
- }
705
- if (Object.keys(importmapMappings).length > 0) {
706
- visitors.push((urlInfo) => {
707
- if (urlInfo.type === "html" && urlInfo.isEntryPoint) {
708
- actions.push(async () => {
709
- await injectVersionMappingsAsImportmap(
710
- urlInfo,
711
- importmapMappings,
712
- );
713
- });
714
- }
715
- });
716
- }
717
- if (visitors.length) {
718
- GRAPH_VISITOR.forEach(finalKitchen.graph, (urlInfo) => {
719
- if (urlInfo.isRoot) return;
720
- visitors.forEach((visitor) => visitor(urlInfo));
721
- });
722
- if (actions.length) {
723
- await Promise.all(actions.map((action) => action()));
692
+ importMappings[buildSpecifier] = buildSpecifierVersioned;
724
693
  }
725
694
  }
726
695
  }
@@ -751,7 +720,6 @@ export const createBuildSpecifierManager = ({
751
720
  if (versioning) {
752
721
  prepareVersioning();
753
722
  }
754
-
755
723
  const urlInfoSet = new Set();
756
724
  GRAPH_VISITOR.forEachUrlInfoStronglyReferenced(
757
725
  finalKitchen.graph.rootUrlInfo,
@@ -788,11 +756,73 @@ export const createBuildSpecifierManager = ({
788
756
  }
789
757
  },
790
758
  );
791
-
792
- workerReferenceSet.clear();
759
+ referenceInSeparateContextSet.clear();
793
760
  if (versioning) {
794
761
  await finishVersioning();
795
762
  }
763
+ const actions = [];
764
+ const visitors = [];
765
+ if (Object.keys(globalMappings).length > 0) {
766
+ visitors.push((urlInfo) => {
767
+ if (urlInfo.isRoot) {
768
+ return;
769
+ }
770
+ if (!urlInfo.isEntryPoint) {
771
+ return;
772
+ }
773
+ actions.push(async () => {
774
+ await injectGlobalMappings(urlInfo, globalMappings);
775
+ });
776
+ });
777
+ }
778
+ sync_importmap: {
779
+ visitors.push((urlInfo) => {
780
+ if (urlInfo.isRoot) {
781
+ return;
782
+ }
783
+ if (!urlInfo.isEntryPoint) {
784
+ return;
785
+ }
786
+ if (urlInfo.type !== "html") {
787
+ return;
788
+ }
789
+
790
+ actions.push(async () => {
791
+ await injectImportmapMappings(urlInfo, (topLevelMappings) => {
792
+ if (!topLevelMappings) {
793
+ return importMappings;
794
+ }
795
+ const topLevelMappingsToKeep = {};
796
+ for (const topLevelMappingKey of Object.keys(topLevelMappings)) {
797
+ const topLevelMappingValue =
798
+ topLevelMappings[topLevelMappingKey];
799
+ const urlInfo = finalKitchen.graph.getUrlInfo(
800
+ `ignore:${topLevelMappingKey}`,
801
+ );
802
+ if (urlInfo) {
803
+ topLevelMappingsToKeep[topLevelMappingKey] =
804
+ topLevelMappingValue;
805
+ }
806
+ }
807
+ return {
808
+ ...topLevelMappingsToKeep,
809
+ ...importMappings,
810
+ };
811
+ });
812
+ });
813
+ });
814
+ }
815
+
816
+ if (visitors.length) {
817
+ GRAPH_VISITOR.forEach(finalKitchen.graph, (urlInfo) => {
818
+ for (const visitor of visitors) {
819
+ visitor(urlInfo);
820
+ }
821
+ });
822
+ if (actions.length) {
823
+ await Promise.all(actions.map((action) => action()));
824
+ }
825
+ }
796
826
 
797
827
  for (const urlInfo of urlInfoSet) {
798
828
  urlInfo.kitchen.urlInfoTransformer.applySourcemapOnContent(
@@ -809,19 +839,9 @@ export const createBuildSpecifierManager = ({
809
839
  urlInfoSet.clear();
810
840
  },
811
841
 
812
- prepareResyncResourceHints: () => {
813
- const actions = [];
814
- GRAPH_VISITOR.forEach(finalKitchen.graph, (urlInfo) => {
815
- if (urlInfo.type !== "html") {
816
- return;
817
- }
818
- const htmlAst = parseHtml({
819
- html: urlInfo.content,
820
- url: urlInfo.url,
821
- storeOriginalPositions: false,
822
- });
823
- const mutations = [];
824
- const hintToInjectMap = new Map();
842
+ prepareResyncResourceHints: ({ registerHtmlRefine }) => {
843
+ const hintToInjectMap = new Map();
844
+ registerHtmlRefine((htmlAst, { registerHtmlMutation }) => {
825
845
  visitHtmlNodes(htmlAst, {
826
846
  link: (node) => {
827
847
  const href = getHtmlNodeAttribute(node, "href");
@@ -846,7 +866,7 @@ export const createBuildSpecifierManager = ({
846
866
  logger.warn(
847
867
  `${UNICODE.WARNING} remove resource hint because cannot find "${href}" in the graph`,
848
868
  );
849
- mutations.push(() => {
869
+ registerHtmlMutation(() => {
850
870
  removeHtmlNode(node);
851
871
  });
852
872
  return;
@@ -857,7 +877,7 @@ export const createBuildSpecifierManager = ({
857
877
  logger.warn(
858
878
  `${UNICODE.WARNING} remove resource hint on "${href}" because it was bundled`,
859
879
  );
860
- mutations.push(() => {
880
+ registerHtmlMutation(() => {
861
881
  removeHtmlNode(node);
862
882
  });
863
883
  return;
@@ -865,13 +885,13 @@ export const createBuildSpecifierManager = ({
865
885
  logger.warn(
866
886
  `${UNICODE.WARNING} remove resource hint on "${href}" because it is not used anymore`,
867
887
  );
868
- mutations.push(() => {
888
+ registerHtmlMutation(() => {
869
889
  removeHtmlNode(node);
870
890
  });
871
891
  return;
872
892
  }
873
893
  const buildGeneratedSpecifier = getBuildGeneratedSpecifier(urlInfo);
874
- mutations.push(() => {
894
+ registerHtmlMutation(() => {
875
895
  setHtmlNodeAttributes(node, {
876
896
  href: buildGeneratedSpecifier,
877
897
  ...(urlInfo.type === "js_classic"
@@ -890,43 +910,31 @@ export const createBuildSpecifierManager = ({
890
910
  }
891
911
  },
892
912
  });
893
- hintToInjectMap.forEach(({ node }, urlInfo) => {
894
- const buildGeneratedSpecifier = getBuildGeneratedSpecifier(urlInfo);
913
+ for (const [referencedUrlInfo, { node }] of hintToInjectMap) {
914
+ const buildGeneratedSpecifier =
915
+ getBuildGeneratedSpecifier(referencedUrlInfo);
895
916
  const found = findHtmlNode(htmlAst, (htmlNode) => {
896
917
  return (
897
918
  htmlNode.nodeName === "link" &&
898
919
  getHtmlNodeAttribute(htmlNode, "href") === buildGeneratedSpecifier
899
920
  );
900
921
  });
901
- if (!found) {
902
- mutations.push(() => {
903
- const nodeToInsert = createHtmlNode({
904
- tagName: "link",
905
- rel: getHtmlNodeAttribute(node, "rel"),
906
- href: buildGeneratedSpecifier,
907
- as: getHtmlNodeAttribute(node, "as"),
908
- type: getHtmlNodeAttribute(node, "type"),
909
- crossorigin: getHtmlNodeAttribute(node, "crossorigin"),
910
- });
911
- insertHtmlNodeAfter(nodeToInsert, node);
912
- });
922
+ if (found) {
923
+ continue;
913
924
  }
914
- });
915
- if (mutations.length > 0) {
916
- actions.push(() => {
917
- mutations.forEach((mutation) => mutation());
918
- urlInfo.mutateContent({
919
- content: stringifyHtmlAst(htmlAst),
925
+ registerHtmlMutation(() => {
926
+ const nodeToInsert = createHtmlNode({
927
+ tagName: "link",
928
+ rel: getHtmlNodeAttribute(node, "rel"),
929
+ href: buildGeneratedSpecifier,
930
+ as: getHtmlNodeAttribute(node, "as"),
931
+ type: getHtmlNodeAttribute(node, "type"),
932
+ crossorigin: getHtmlNodeAttribute(node, "crossorigin"),
920
933
  });
934
+ insertHtmlNodeAfter(nodeToInsert, node);
921
935
  });
922
936
  }
923
937
  });
924
- if (actions.length === 0) {
925
- return null;
926
- }
927
- return () => {
928
- actions.map((resourceHintAction) => resourceHintAction());
929
- };
930
938
  },
931
939
 
932
940
  prepareServiceWorkerUrlInjection: () => {
@@ -1071,7 +1079,8 @@ const findRawUrlInfoWhenInline = (reference, rawKitchen) => {
1071
1079
  if (
1072
1080
  inlineUrlSite.url === reference.ownerUrlInfo.url &&
1073
1081
  inlineUrlSite.line === reference.specifierLine &&
1074
- inlineUrlSite.column === reference.specifierColumn
1082
+ inlineUrlSite.column === reference.specifierColumn &&
1083
+ rawUrlInfoCandidate.contentType === reference.contentType
1075
1084
  ) {
1076
1085
  return true;
1077
1086
  }
@@ -127,7 +127,7 @@ const determineDirectoryPath = ({
127
127
  });
128
128
  return parentDirectoryPath;
129
129
  }
130
- if (urlInfo.isEntryPoint) {
130
+ if (urlInfo.isEntryPoint && !urlInfo.isDynamicEntryPoint) {
131
131
  return "";
132
132
  }
133
133
  if (urlInfo.type === "importmap") {
@@ -2,23 +2,25 @@
2
2
 
3
3
  import {
4
4
  createHtmlNode,
5
+ findHtmlNode,
6
+ getHtmlNodeAttribute,
7
+ getHtmlNodeText,
5
8
  injectHtmlNodeAsEarlyAsPossible,
6
9
  parseHtml,
10
+ removeHtmlNode,
11
+ setHtmlNodeText,
7
12
  stringifyHtmlAst,
8
13
  } from "@jsenv/ast";
9
14
 
10
15
  import { isWebWorkerUrlInfo } from "@jsenv/core/src/kitchen/web_workers.js";
11
16
  import { prependContent } from "../kitchen/prepend_content.js";
12
17
 
13
- export const injectVersionMappingsAsGlobal = async (
14
- urlInfo,
15
- versionMappings,
16
- ) => {
18
+ export const injectGlobalMappings = async (urlInfo, mappings) => {
17
19
  if (urlInfo.type === "html") {
18
20
  const minification = Boolean(
19
21
  urlInfo.context.getPluginMeta("willMinifyJsClassic"),
20
22
  );
21
- const content = generateClientCodeForVersionMappings(versionMappings, {
23
+ const content = generateClientCodeForMappings(mappings, {
22
24
  globalName: "window",
23
25
  minification,
24
26
  });
@@ -29,7 +31,7 @@ export const injectVersionMappingsAsGlobal = async (
29
31
  const minification = Boolean(
30
32
  urlInfo.context.getPluginMeta("willMinifyJsClassic"),
31
33
  );
32
- const content = generateClientCodeForVersionMappings(versionMappings, {
34
+ const content = generateClientCodeForMappings(mappings, {
33
35
  globalName: isWebWorkerUrlInfo(urlInfo) ? "self" : "window",
34
36
  minification,
35
37
  });
@@ -38,7 +40,7 @@ export const injectVersionMappingsAsGlobal = async (
38
40
  }
39
41
  };
40
42
 
41
- const generateClientCodeForVersionMappings = (
43
+ const generateClientCodeForMappings = (
42
44
  versionMappings,
43
45
  { globalName, minification },
44
46
  ) => {
@@ -57,7 +59,7 @@ const generateClientCodeForVersionMappings = (
57
59
  })();`;
58
60
  };
59
61
 
60
- export const injectVersionMappingsAsImportmap = (urlInfo, versionMappings) => {
62
+ export const injectImportmapMappings = (urlInfo, getMappings) => {
61
63
  const htmlAst = parseHtml({
62
64
  html: urlInfo.content,
63
65
  url: urlInfo.url,
@@ -69,20 +71,59 @@ export const injectVersionMappingsAsImportmap = (urlInfo, versionMappings) => {
69
71
  const importmapMinification = Boolean(
70
72
  urlInfo.context.getPluginMeta("willMinifyJson"),
71
73
  );
72
- injectHtmlNodeAsEarlyAsPossible(
73
- htmlAst,
74
- createHtmlNode({
75
- tagName: "script",
76
- type: "importmap",
77
- children: importmapMinification
78
- ? JSON.stringify({ imports: versionMappings })
79
- : JSON.stringify({ imports: versionMappings }, null, " "),
80
- }),
81
- "jsenv:versioning",
82
- );
83
- urlInfo.mutateContent({
84
- content: stringifyHtmlAst(htmlAst),
74
+ const importmapNode = findHtmlNode(htmlAst, (node) => {
75
+ return (
76
+ node.tagName === "script" &&
77
+ getHtmlNodeAttribute(node, "type") === "importmap"
78
+ );
79
+ });
80
+ const generateMappingText = (mappings) => {
81
+ if (importmapMinification) {
82
+ return JSON.stringify({ imports: mappings });
83
+ }
84
+ return JSON.stringify({ imports: mappings }, null, " ");
85
+ };
86
+
87
+ const mutate = (mutation) => {
88
+ mutation();
89
+ urlInfo.mutateContent({
90
+ content: stringifyHtmlAst(htmlAst),
91
+ });
92
+ };
93
+
94
+ if (importmapNode) {
95
+ // we want to remove some mappings, override others, add eventually add new
96
+ const currentMappings = JSON.parse(getHtmlNodeText(importmapNode));
97
+ const mappings = getMappings(currentMappings.imports);
98
+ if (!mappings || Object.keys(mappings).length === 0) {
99
+ mutate(() => {
100
+ removeHtmlNode(importmapNode);
101
+ });
102
+ return;
103
+ }
104
+ mutate(() => {
105
+ setHtmlNodeText(importmapNode, generateMappingText(mappings), {
106
+ indentation: "auto",
107
+ });
108
+ });
109
+ return;
110
+ }
111
+ const mappings = getMappings(null);
112
+ if (!mappings || Object.keys(mappings).length === 0) {
113
+ return;
114
+ }
115
+ mutate(() => {
116
+ injectHtmlNodeAsEarlyAsPossible(
117
+ htmlAst,
118
+ createHtmlNode({
119
+ tagName: "script",
120
+ type: "importmap",
121
+ children: generateMappingText(getMappings(null)),
122
+ }),
123
+ "jsenv:versioning",
124
+ );
85
125
  });
126
+ return;
86
127
  };
87
128
 
88
129
  const stringifyParams = (params, prefix = "") => {
@@ -17,7 +17,7 @@ import { Abort, raceProcessTeardownEvents } from "@jsenv/abort";
17
17
  import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem";
18
18
  import { createLogger, createTaskLog } from "@jsenv/humanize";
19
19
  import {
20
- fetchFileSystem,
20
+ createFileSystemFetch,
21
21
  jsenvAccessControlAllowedHeaders,
22
22
  jsenvServiceCORS,
23
23
  jsenvServiceErrorHandler,
@@ -36,6 +36,7 @@ export const startBuildServer = async ({
36
36
  buildDirectoryUrl,
37
37
  buildMainFilePath = "index.html",
38
38
  port = 9779,
39
+ routes,
39
40
  services = [],
40
41
  acceptAnyIp,
41
42
  hostname,
@@ -120,6 +121,7 @@ export const startBuildServer = async ({
120
121
  port,
121
122
  serverTiming: true,
122
123
  requestWaitingMs: 60_000,
124
+ routes,
123
125
  services: [
124
126
  jsenvServiceCORS({
125
127
  accessControlAllowRequestOrigin: true,
@@ -130,13 +132,10 @@ export const startBuildServer = async ({
130
132
  timingAllowOrigin: true,
131
133
  }),
132
134
  ...services,
133
- {
134
- name: "jsenv:build_files_service",
135
- handleRequest: createBuildFilesService({
136
- buildDirectoryUrl,
137
- buildMainFilePath,
138
- }),
139
- },
135
+ jsenvBuildFileService({
136
+ buildDirectoryUrl,
137
+ buildMainFilePath,
138
+ }),
140
139
  jsenvServiceErrorHandler({
141
140
  sendErrorDetails: true,
142
141
  }),
@@ -160,35 +159,46 @@ export const startBuildServer = async ({
160
159
  };
161
160
  };
162
161
 
163
- const createBuildFilesService = ({ buildDirectoryUrl, buildMainFilePath }) => {
164
- return (request) => {
165
- const urlIsVersioned = new URL(request.url).searchParams.has("v");
166
- if (buildMainFilePath && request.resource === "/") {
167
- request = {
168
- ...request,
169
- resource: `/${buildMainFilePath}`,
170
- };
171
- }
172
- const urlObject = new URL(request.resource.slice(1), buildDirectoryUrl);
173
- return fetchFileSystem(urlObject, {
174
- headers: request.headers,
175
- cacheControl: urlIsVersioned
176
- ? `private,max-age=${SECONDS_IN_30_DAYS},immutable`
177
- : "private,max-age=0,must-revalidate",
178
- etagEnabled: true,
179
- compressionEnabled: true,
180
- rootDirectoryUrl: buildDirectoryUrl,
181
- canReadDirectory: true,
182
- ENOENTFallback: () => {
183
- if (
184
- !urlToExtension(urlObject) &&
185
- !urlToPathname(urlObject).endsWith("/")
186
- ) {
187
- return new URL(buildMainFilePath, buildDirectoryUrl);
188
- }
189
- return null;
162
+ const jsenvBuildFileService = ({ buildDirectoryUrl, buildMainFilePath }) => {
163
+ return {
164
+ name: "jsenv:build_files",
165
+ routes: [
166
+ {
167
+ endpoint: "GET *",
168
+ description: "Serve static files.",
169
+ fetch: (request, helpers) => {
170
+ const urlIsVersioned = new URL(request.url).searchParams.has("v");
171
+ if (buildMainFilePath && request.resource === "/") {
172
+ request = {
173
+ ...request,
174
+ resource: `/${buildMainFilePath}`,
175
+ };
176
+ }
177
+ const urlObject = new URL(
178
+ request.resource.slice(1),
179
+ buildDirectoryUrl,
180
+ );
181
+ return createFileSystemFetch(buildDirectoryUrl, {
182
+ cacheControl: urlIsVersioned
183
+ ? `private,max-age=${SECONDS_IN_30_DAYS},immutable`
184
+ : "private,max-age=0,must-revalidate",
185
+ etagEnabled: true,
186
+ compressionEnabled: true,
187
+ rootDirectoryUrl: buildDirectoryUrl,
188
+ canReadDirectory: true,
189
+ ENOENTFallback: () => {
190
+ if (
191
+ !urlToExtension(urlObject) &&
192
+ !urlToPathname(urlObject).endsWith("/")
193
+ ) {
194
+ return new URL(buildMainFilePath, buildDirectoryUrl);
195
+ }
196
+ return null;
197
+ },
198
+ })(request, helpers);
199
+ },
190
200
  },
191
- });
201
+ ],
192
202
  };
193
203
  };
194
204