vite-plugin-html-pages 2.0.7 → 2.0.8

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/index.js CHANGED
@@ -9,6 +9,74 @@ import pLimit from "p-limit";
9
9
  import fs from "fs/promises";
10
10
  import path2 from "path";
11
11
 
12
+ // src/page-helper-generator.ts
13
+ function paramsTypeFromDefinitions(paramDefinitions) {
14
+ if (paramDefinitions.length === 0) {
15
+ return "{}";
16
+ }
17
+ const fields = paramDefinitions.map((param) => {
18
+ if (param.type === "single") {
19
+ return `${JSON.stringify(param.name)}: string`;
20
+ }
21
+ if (param.type === "catch-all") {
22
+ return `${JSON.stringify(param.name)}: string[]`;
23
+ }
24
+ return `${JSON.stringify(param.name)}?: string[]`;
25
+ });
26
+ return `{ ${fields.join("; ")} }`;
27
+ }
28
+ function generateTypedPageHelper(page) {
29
+ const paramsType = page ? paramsTypeFromDefinitions(page.paramDefinitions ?? []) : "{}";
30
+ return `
31
+ export type PageParams = ${paramsType};
32
+
33
+ export type StaticParams = PageParams[];
34
+
35
+ export type DataContext = {
36
+ params: PageParams;
37
+ dev: boolean;
38
+ };
39
+
40
+ export type RenderContext<TData = unknown> = {
41
+ params: PageParams;
42
+ data: TData;
43
+ dev: boolean;
44
+ };
45
+
46
+ export type PageContext<TData = unknown> = {
47
+ params: PageParams;
48
+ data?: TData;
49
+ dev: boolean;
50
+ };
51
+
52
+ export type PageModule<TData = unknown> = {
53
+ generateStaticParams?: () => StaticParams | Promise<StaticParams>;
54
+ data?: (ctx: DataContext) => TData | Promise<TData>;
55
+ render: (ctx: RenderContext<TData>) => any;
56
+ };
57
+
58
+ export function definePage<T extends (ctx: PageContext) => any>(fn: T): T {
59
+ return fn;
60
+ }
61
+
62
+ export function defineData<T extends (ctx: DataContext) => any>(fn: T): T {
63
+ return fn;
64
+ }
65
+
66
+ export function defineStaticParams<
67
+ T extends () => StaticParams | Promise<StaticParams>
68
+ >(fn: T): T {
69
+ return fn;
70
+ }
71
+
72
+ export function definePageModule<TData>(
73
+ mod: PageModule<TData>,
74
+ ): PageModule<TData> {
75
+ return mod;
76
+ }
77
+ `;
78
+ }
79
+
12
80
  // src/path-utils.ts
13
81
  import path from "path";
14
82
  function toPosix(value) {
@@ -31,68 +99,53 @@ function stripPageSuffix(filePath, extensions) {
31
99
  }
32
100
 
33
101
  // src/typegen.ts
34
- function paramsTypeFromDefinitions(paramDefinitions) {
35
- if (paramDefinitions.length === 0) {
36
- return "{}";
37
- }
38
- const fields = paramDefinitions.map((param) => {
39
- if (param.type === "single") {
40
- return `${param.name}: string`;
41
- }
42
- if (param.type === "catch-all") {
43
- return `${param.name}: string[]`;
44
- }
45
- return `${param.name}?: string[]`;
46
- });
47
- return `{ ${fields.join("; ")} }`;
48
- }
49
102
  function pageHelperModuleSource(page) {
50
103
  const paramsType = paramsTypeFromDefinitions(page.paramDefinitions ?? []);
51
104
  return `export type PageParams = ${paramsType};
52
-
53
- export type StaticParams = PageParams[];
54
-
55
- export type DataContext = {
56
- params: PageParams;
57
- dev: boolean;
58
- };
59
-
60
- export type RenderContext<TData = unknown> = {
61
- params: PageParams;
62
- data: TData;
63
- dev: boolean;
64
- };
65
-
66
- export type PageContext<TData = unknown> = {
67
- params: PageParams;
68
- data?: TData;
69
- dev: boolean;
70
- };
71
-
72
- export type RenderResult = unknown;
73
-
74
- export type PageModule<TData = unknown> = {
75
- generateStaticParams?: () => StaticParams | Promise<StaticParams>;
76
- data?: (ctx: DataContext) => TData | Promise<TData>;
77
- render: (ctx: RenderContext<TData>) => RenderResult | Promise<RenderResult>;
78
- };
79
-
80
- export declare function definePage<
81
- T extends (ctx: PageContext) => RenderResult | Promise<RenderResult>
82
- >(fn: T): T;
83
-
84
- export declare function defineData<
85
- T extends (ctx: DataContext) => unknown | Promise<unknown>
86
- >(fn: T): T;
87
-
88
- export declare function defineStaticParams<
89
- T extends () => StaticParams | Promise<StaticParams>
90
- >(fn: T): T;
91
-
92
- export declare function definePageModule<TData>(
93
- mod: PageModule<TData>
94
- ): PageModule<TData>;
95
- `;
105
+
106
+ export type StaticParams = PageParams[];
107
+
108
+ export type DataContext = {
109
+ params: PageParams;
110
+ dev: boolean;
111
+ };
112
+
113
+ export type RenderContext<TData = unknown> = {
114
+ params: PageParams;
115
+ data: TData;
116
+ dev: boolean;
117
+ };
118
+
119
+ export type PageContext<TData = unknown> = {
120
+ params: PageParams;
121
+ data?: TData;
122
+ dev: boolean;
123
+ };
124
+
125
+ export type RenderResult = unknown;
126
+
127
+ export type PageModule<TData = unknown> = {
128
+ generateStaticParams?: () => StaticParams | Promise<StaticParams>;
129
+ data?: (ctx: DataContext) => TData | Promise<TData>;
130
+ render: (ctx: RenderContext<TData>) => RenderResult | Promise<RenderResult>;
131
+ };
132
+
133
+ export declare function definePage<
134
+ T extends (ctx: PageContext) => RenderResult | Promise<RenderResult>
135
+ >(fn: T): T;
136
+
137
+ export declare function defineData<
138
+ T extends (ctx: DataContext) => unknown | Promise<unknown>
139
+ >(fn: T): T;
140
+
141
+ export declare function defineStaticParams<
142
+ T extends () => StaticParams | Promise<StaticParams>
143
+ >(fn: T): T;
144
+
145
+ export declare function definePageModule<TData>(
146
+ mod: PageModule<TData>
147
+ ): PageModule<TData>;
148
+ `;
96
149
  }
97
150
  function stripPageExtension(filePath) {
98
151
  return filePath.replace(/\.(ht|html)\.(js|ts|jsx|tsx)$/i, "");
@@ -354,8 +407,17 @@ var PLUGIN_NAME = "vite-plugin-html-pages";
354
407
  var VIRTUAL_BUILD_ENTRY_ID = `\0${PLUGIN_NAME}:build-entry`;
355
408
  var VIRTUAL_PAGE_HELPER_ID = `${PLUGIN_NAME}/page`;
356
409
  var RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX = `\0${PLUGIN_NAME}/page:`;
357
- var VIRTUAL_MANIFEST_ID = `\0virtual:${PLUGIN_NAME}-manifest`;
358
410
  var CACHE_DIR_NAME = `node_modules/.cache/${PLUGIN_NAME}`;
411
+ var DEFAULT_PAGE_EXTENSIONS = [
412
+ ".ht.js",
413
+ ".html.js",
414
+ ".ht.ts",
415
+ ".html.ts",
416
+ ".ht.jsx",
417
+ ".html.jsx",
418
+ ".ht.tsx",
419
+ ".html.tsx"
420
+ ];
359
421
  var VIRTUAL_JSX_RUNTIME_ID = `${PLUGIN_NAME}/jsx-runtime`;
360
422
  var VIRTUAL_JSX_DEV_RUNTIME_ID = `${PLUGIN_NAME}/jsx-dev-runtime`;
361
423
  var RESOLVED_VIRTUAL_JSX_RUNTIME_ID = `\0${VIRTUAL_JSX_RUNTIME_ID}`;
@@ -515,7 +577,7 @@ async function discoverEntryPages(root, options) {
515
577
  const fgModule = await import("fast-glob");
516
578
  const fg2 = fgModule.default ?? fgModule;
517
579
  const pagesDir = options.pagesDir ?? "src";
518
- const pageExtensions = options.pageExtensions?.length ? options.pageExtensions : [".ht.js", ".html.js", ".ht.ts", ".html.ts", ".ht.jsx", ".html.jsx", ".ht.tsx", ".html.tsx"];
580
+ const pageExtensions = options.pageExtensions?.length ? options.pageExtensions : DEFAULT_PAGE_EXTENSIONS;
519
581
  const include = Array.isArray(options.include) ? options.include : options.include ? [options.include] : buildDefaultIncludeGlobs(pagesDir, pageExtensions);
520
582
  const exclude = Array.isArray(options.exclude) ? options.exclude : options.exclude ? [options.exclude] : [];
521
583
  const pagesRoot = normalizeFsPath(path4.join(root, pagesDir));
@@ -700,6 +762,9 @@ async function renderPage(page, mod, dev = false) {
700
762
  ctx.data = await mod.data(ctx);
701
763
  }
702
764
  const result = await resolveRenderResult(page, mod, ctx);
765
+ if (result == null) {
766
+ throw invalidHtmlReturn(page, result);
767
+ }
703
768
  if (typeof result === "string") {
704
769
  return ensureDoctype(result);
705
770
  }
@@ -727,79 +792,9 @@ async function renderPage(page, mod, dev = false) {
727
792
  import path5 from "path";
728
793
  import {
729
794
  createServer,
730
- isRunnableDevEnvironment
795
+ isRunnableDevEnvironment,
796
+ loadConfigFromFile
731
797
  } from "vite";
732
-
733
- // src/page-helper-generator.ts
734
- function paramsTypeFromDefinitions2(paramDefinitions) {
735
- if (paramDefinitions.length === 0) {
736
- return "{}";
737
- }
738
- const fields = paramDefinitions.map((param) => {
739
- if (param.type === "single") {
740
- return `${JSON.stringify(param.name)}: string`;
741
- }
742
- if (param.type === "catch-all") {
743
- return `${JSON.stringify(param.name)}: string[]`;
744
- }
745
- return `${JSON.stringify(param.name)}?: string[]`;
746
- });
747
- return `{ ${fields.join("; ")} }`;
748
- }
749
- function generateTypedPageHelper(page) {
750
- const paramsType = page ? paramsTypeFromDefinitions2(page.paramDefinitions ?? []) : "{}";
751
- return `
752
- export type PageParams = ${paramsType};
753
-
754
- export type StaticParams = PageParams[];
755
-
756
- export type DataContext = {
757
- params: PageParams;
758
- dev: boolean;
759
- };
760
-
761
- export type RenderContext<TData = unknown> = {
762
- params: PageParams;
763
- data: TData;
764
- dev: boolean;
765
- };
766
-
767
- export type PageContext<TData = unknown> = {
768
- params: PageParams;
769
- data?: TData;
770
- dev: boolean;
771
- };
772
-
773
- export type PageModule<TData = unknown> = {
774
- generateStaticParams?: () => StaticParams | Promise<StaticParams>;
775
- data?: (ctx: DataContext) => TData | Promise<TData>;
776
- render: (ctx: RenderContext<TData>) => any;
777
- };
778
-
779
- export function definePage<T extends (ctx: PageContext) => any>(fn: T): T {
780
- return fn;
781
- }
782
-
783
- export function defineData<T extends (ctx: DataContext) => any>(fn: T): T {
784
- return fn;
785
- }
786
-
787
- export function defineStaticParams<
788
- T extends () => StaticParams | Promise<StaticParams>
789
- >(fn: T): T {
790
- return fn;
791
- }
792
-
793
- export function definePageModule<TData>(
794
- mod: PageModule<TData>,
795
- ): PageModule<TData> {
796
- return mod;
797
- }
798
- `;
799
- }
800
-
801
- // src/module-loader.ts
802
- var buildServer = null;
803
798
  async function importPageModule(server, url) {
804
799
  const environment = server.environments.ssr;
805
800
  if (!isRunnableDevEnvironment(environment)) {
@@ -810,15 +805,12 @@ async function importPageModule(server, url) {
810
805
  const mod = await environment.runner.import(url);
811
806
  return normalizeLoadedPageModule(mod);
812
807
  }
813
- function isStructuredPageModule2(value) {
814
- return !!value && typeof value === "object" && "render" in value && typeof value.render === "function";
815
- }
816
808
  function isLocalPageTypesImport(id) {
817
809
  return /^\.\/\$types(?:\.[A-Za-z0-9_.-]+)?$/.test(id);
818
810
  }
819
811
  function normalizeLoadedPageModule(mod) {
820
812
  const pageModule = mod ?? {};
821
- if (isStructuredPageModule2(pageModule.default)) {
813
+ if (isStructuredPageModule(pageModule.default)) {
822
814
  const structured = pageModule.default;
823
815
  return {
824
816
  default: structured.render,
@@ -830,83 +822,125 @@ function normalizeLoadedPageModule(mod) {
830
822
  }
831
823
  return pageModule;
832
824
  }
825
+ async function flattenPluginOptions(option) {
826
+ const resolved = await option;
827
+ if (!resolved) return [];
828
+ if (Array.isArray(resolved)) {
829
+ const nested = await Promise.all(
830
+ resolved.map((entry) => flattenPluginOptions(entry))
831
+ );
832
+ return nested.flat();
833
+ }
834
+ return [resolved];
835
+ }
836
+ async function loadUserConfig(args) {
837
+ if (!args.configFile) {
838
+ return { userConfig: {}, userPlugins: [] };
839
+ }
840
+ const loaded = await loadConfigFromFile(
841
+ { command: "serve", mode: args.mode },
842
+ args.configFile,
843
+ args.root
844
+ );
845
+ if (!loaded) {
846
+ return { userConfig: {}, userPlugins: [] };
847
+ }
848
+ const plugins = await flattenPluginOptions(loaded.config.plugins);
849
+ return {
850
+ userConfig: loaded.config,
851
+ // Filter ourselves out so evaluating page modules does not
852
+ // recursively spawn more build loaders.
853
+ userPlugins: plugins.filter((plugin) => plugin.name !== PLUGIN_NAME)
854
+ };
855
+ }
856
+ function createPageHelperPlugin(getPages) {
857
+ return {
858
+ name: `${PLUGIN_NAME}:page-helper`,
859
+ resolveId(id, importer) {
860
+ if (id === VIRTUAL_PAGE_HELPER_ID && importer) {
861
+ return `${RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX}${importer}`;
862
+ }
863
+ if (importer && isLocalPageTypesImport(id)) {
864
+ return `${VIRTUAL_LOCAL_TYPES_PREFIX}${importer}::${id}`;
865
+ }
866
+ return null;
867
+ },
868
+ async load(id) {
869
+ if (id.startsWith(VIRTUAL_LOCAL_TYPES_PREFIX)) {
870
+ return `
871
+ export {
872
+ definePage,
873
+ defineData,
874
+ defineStaticParams,
875
+ definePageModule
876
+ } from 'vite-plugin-html-pages/page';
877
+ `;
878
+ }
879
+ if (!id.startsWith(RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX)) {
880
+ return null;
881
+ }
882
+ const importer = id.slice(RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX.length);
883
+ const pages = await getPages();
884
+ const normalizedImporter = path5.resolve(importer);
885
+ const page = pages.find(
886
+ (candidate) => path5.resolve(candidate.absolutePath) === normalizedImporter
887
+ );
888
+ return generateTypedPageHelper(page);
889
+ }
890
+ };
891
+ }
833
892
  async function createPageModuleLoader(args) {
834
893
  const { mode, root, server, getPages } = args;
835
894
  if (mode === "dev") {
836
895
  if (!server) {
837
896
  throw new Error("[vite-plugin-html-pages] dev server not available");
838
897
  }
839
- return async (_entryPath, relativePath) => importPageModule(server, `/${relativePath}`);
898
+ return {
899
+ loadModule: async (_entryPath, relativePath) => importPageModule(server, `/${relativePath}`),
900
+ close: async () => {
901
+ }
902
+ };
840
903
  }
841
904
  if (!getPages) {
842
905
  throw new Error(
843
906
  "[vite-plugin-html-pages] getPages is required in build mode"
844
907
  );
845
908
  }
846
- if (!buildServer) {
847
- const config = {
848
- root,
849
- configFile: false,
850
- logLevel: "error",
851
- appType: "custom",
852
- esbuild: {
853
- jsx: "automatic",
854
- jsxImportSource: "vite-plugin-html-pages"
855
- },
856
- server: {
857
- middlewareMode: true
858
- },
859
- plugins: [
860
- {
861
- name: "vite-plugin-html-pages:page-helper",
862
- resolveId(id, importer) {
863
- if (id === VIRTUAL_PAGE_HELPER_ID && importer) {
864
- return `${RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX}${importer}`;
865
- }
866
- if (importer && isLocalPageTypesImport(id)) {
867
- return `${VIRTUAL_LOCAL_TYPES_PREFIX}${importer}::${id}`;
868
- }
869
- return null;
870
- },
871
- async load(id) {
872
- if (id.startsWith(VIRTUAL_LOCAL_TYPES_PREFIX)) {
873
- return `
874
- export {
875
- definePage,
876
- defineData,
877
- defineStaticParams,
878
- definePageModule
879
- } from 'vite-plugin-html-pages/page';
880
- `;
881
- }
882
- if (!id.startsWith(RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX)) {
883
- return null;
884
- }
885
- const importer = id.slice(
886
- RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX.length
887
- );
888
- const pages = await getPages();
889
- const normalizedImporter = path5.resolve(importer);
890
- const page = pages.find(
891
- (candidate) => path5.resolve(candidate.absolutePath) === normalizedImporter
892
- );
893
- return generateTypedPageHelper(page);
894
- }
895
- }
896
- ]
897
- };
898
- buildServer = await createServer(config);
899
- }
900
- return async (entryPath) => {
901
- const relativePath = "/" + path5.relative(root, entryPath).replace(/\\/g, "/");
902
- return importPageModule(buildServer, relativePath);
909
+ const configMode = args.configMode ?? "production";
910
+ const { userConfig, userPlugins } = await loadUserConfig({
911
+ root,
912
+ configFile: args.configFile,
913
+ mode: configMode
914
+ });
915
+ const config = {
916
+ ...userConfig,
917
+ root,
918
+ mode: configMode,
919
+ configFile: false,
920
+ logLevel: "error",
921
+ appType: "custom",
922
+ esbuild: userConfig.esbuild === false ? false : {
923
+ ...userConfig.esbuild,
924
+ jsx: "automatic",
925
+ jsxImportSource: PLUGIN_NAME
926
+ },
927
+ server: {
928
+ ...userConfig.server,
929
+ middlewareMode: true,
930
+ watch: null
931
+ },
932
+ plugins: [...userPlugins, createPageHelperPlugin(getPages)]
933
+ };
934
+ const buildServer = await createServer(config);
935
+ return {
936
+ loadModule: async (entryPath) => {
937
+ const relativePath = "/" + path5.relative(root, entryPath).replace(/\\/g, "/");
938
+ return importPageModule(buildServer, relativePath);
939
+ },
940
+ close: async () => {
941
+ await buildServer.close();
942
+ }
903
943
  };
904
- }
905
- async function closePageModuleLoader() {
906
- if (buildServer) {
907
- await buildServer.close();
908
- buildServer = null;
909
- }
910
944
  }
911
945
 
912
946
  // src/dev-server.ts
@@ -925,8 +959,12 @@ function normalizeRoutePath2(input) {
925
959
  function shouldSkipHtmlRouting(url, pagesDir) {
926
960
  return url.startsWith("/@vite") || url.startsWith("/@fs/") || url.startsWith("/node_modules/") || url.startsWith(`/${pagesDir}/`) || url === "/favicon.ico" || isStaticAssetRequest(url);
927
961
  }
962
+ function hasDotDotSegment(url) {
963
+ return url.split("/").includes("..");
964
+ }
928
965
  function tryRewriteRootAssetToSrc(root, pagesDir, url) {
929
966
  if (!url.startsWith("/")) return null;
967
+ if (hasDotDotSegment(url)) return null;
930
968
  if (!isStaticAssetRequest(url)) return null;
931
969
  if (url.startsWith(`/${pagesDir}/`)) return null;
932
970
  const candidate = path6.join(root, pagesDir, url.slice(1));
@@ -939,6 +977,7 @@ function rewriteRootAssetUrlsInDevHtml(html, root, pagesDir) {
939
977
  return html.replace(
940
978
  /\b(href|src)=["'](\/[^"']+)["']/g,
941
979
  (full, attr, url) => {
980
+ if (hasDotDotSegment(url)) return full;
942
981
  if (!isStaticAssetRequest(url)) return full;
943
982
  if (url.startsWith(`/${pagesDir}/`)) return full;
944
983
  const candidate = path6.join(root, pagesDir, url.slice(1));
@@ -949,7 +988,7 @@ function rewriteRootAssetUrlsInDevHtml(html, root, pagesDir) {
949
988
  }
950
989
  function installDevServer(args) {
951
990
  const { server, root, pagesDir, getPages } = args;
952
- const loadModulePromise = createPageModuleLoader({
991
+ const loaderPromise = createPageModuleLoader({
953
992
  mode: "dev",
954
993
  root,
955
994
  server
@@ -960,7 +999,9 @@ function installDevServer(args) {
960
999
  const url = normalizeRoutePath2(originalUrl);
961
1000
  const rewrittenAssetUrl = tryRewriteRootAssetToSrc(root, pagesDir, url);
962
1001
  if (rewrittenAssetUrl) {
963
- req.url = rewrittenAssetUrl + originalUrl.slice(url.length);
1002
+ const suffixIndex = originalUrl.search(/[?#]/);
1003
+ const suffix = suffixIndex === -1 ? "" : originalUrl.slice(suffixIndex);
1004
+ req.url = rewrittenAssetUrl + suffix;
964
1005
  return next();
965
1006
  }
966
1007
  if (shouldSkipHtmlRouting(url, pagesDir)) {
@@ -973,7 +1014,7 @@ function installDevServer(args) {
973
1014
  if (!page) {
974
1015
  return next();
975
1016
  }
976
- const loadModule = await loadModulePromise;
1017
+ const { loadModule } = await loaderPromise;
977
1018
  const mod = await loadModule(page.entryPath, page.relativePath);
978
1019
  if (!mod) {
979
1020
  return next();
@@ -1029,10 +1070,11 @@ function collectScriptSrcs(html) {
1029
1070
  }
1030
1071
  function collectStylesheetHrefs(html) {
1031
1072
  const out = [];
1032
- for (const match of html.matchAll(
1033
- /<link\b[^>]*\brel=["']stylesheet["'][^>]*\bhref=["']([^"']+)["'][^>]*>/gi
1034
- )) {
1035
- out.push(match[1]);
1073
+ for (const match of html.matchAll(/<link\b[^>]*>/gi)) {
1074
+ const tag = match[0];
1075
+ if (!/\brel=["']?stylesheet["']?/i.test(tag)) continue;
1076
+ const href = tag.match(/\bhref=["']([^"']+)["']/i);
1077
+ if (href) out.push(href[1]);
1036
1078
  }
1037
1079
  return out;
1038
1080
  }
@@ -1048,6 +1090,20 @@ function collectLiteralDynamicImports(html) {
1048
1090
  function unique(values) {
1049
1091
  return [...new Set(values)];
1050
1092
  }
1093
+ function collectHrefSrcUrls(html) {
1094
+ const out = [];
1095
+ for (const match of html.matchAll(/\b(?:href|src)=["']([^"']+)["']/gi)) {
1096
+ out.push(match[1]);
1097
+ }
1098
+ return out;
1099
+ }
1100
+ function collectLocalAssetUrls(html) {
1101
+ const candidates = [
1102
+ ...collectHrefSrcUrls(html),
1103
+ ...collectLiteralDynamicImports(html)
1104
+ ];
1105
+ return unique(candidates.filter(isLocalRootUrl).map(stripQueryAndHash));
1106
+ }
1051
1107
  function formatPageLabel(pageLabel) {
1052
1108
  return pageLabel ? ` (${pageLabel})` : "";
1053
1109
  }
@@ -1132,6 +1188,12 @@ async function buildPageIndex(args) {
1132
1188
  const mod = modulesByEntry.get(entry.entryPath) ?? {};
1133
1189
  if (entry.dynamic) {
1134
1190
  const rows = (mod.generateStaticParams ? await mod.generateStaticParams() : []) ?? [];
1191
+ const paramRows = Array.isArray(rows) ? rows : [];
1192
+ if (paramRows.length === 0) {
1193
+ console.warn(
1194
+ `[${PLUGIN_NAME}] \u26A0\uFE0F Dynamic page "${entry.relativePath}" generated no routes. Export generateStaticParams() returning at least one params object to emit pages for it.`
1195
+ );
1196
+ }
1135
1197
  pages.push(
1136
1198
  ...expandStaticPaths(
1137
1199
  {
@@ -1144,7 +1206,7 @@ async function buildPageIndex(args) {
1144
1206
  paramNames: entry.paramNames,
1145
1207
  paramDefinitions: entry.paramDefinitions
1146
1208
  },
1147
- Array.isArray(rows) ? rows : [],
1209
+ paramRows,
1148
1210
  cleanUrls
1149
1211
  )
1150
1212
  );
@@ -1328,8 +1390,8 @@ function warnIfNotESM(root) {
1328
1390
  } catch {
1329
1391
  }
1330
1392
  }
1331
- function isLocalPageTypesImport2(id) {
1332
- return /^\.\/\$types(?:\.[A-Za-z0-9_.-]+)?$/.test(id);
1393
+ function escapeXml(value) {
1394
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
1333
1395
  }
1334
1396
  function chunkArray(items, size) {
1335
1397
  const out = [];
@@ -1338,6 +1400,50 @@ function chunkArray(items, size) {
1338
1400
  }
1339
1401
  return out;
1340
1402
  }
1403
+ var DEFAULT_404_HTML = `<!doctype html>
1404
+ <html lang="en">
1405
+ <head>
1406
+ <meta charset="UTF-8" />
1407
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
1408
+ <title>404 - Page Not Found</title>
1409
+ <style>
1410
+ :root {
1411
+ color-scheme: light dark;
1412
+ }
1413
+ body {
1414
+ margin: 0;
1415
+ font-family: system-ui, sans-serif;
1416
+ min-height: 100vh;
1417
+ display: grid;
1418
+ place-items: center;
1419
+ padding: 2rem;
1420
+ }
1421
+ main {
1422
+ max-width: 40rem;
1423
+ text-align: center;
1424
+ }
1425
+ h1 {
1426
+ font-size: 3rem;
1427
+ margin: 0 0 1rem;
1428
+ }
1429
+ p {
1430
+ margin: 0.5rem 0;
1431
+ line-height: 1.5;
1432
+ }
1433
+ a {
1434
+ color: inherit;
1435
+ }
1436
+ </style>
1437
+ </head>
1438
+ <body>
1439
+ <main>
1440
+ <h1>404</h1>
1441
+ <p>Page not found.</p>
1442
+ <p><a href="/">Go back home</a></p>
1443
+ </main>
1444
+ </body>
1445
+ </html>
1446
+ `;
1341
1447
  function isHtJsxFile(id) {
1342
1448
  return id.endsWith(".ht.jsx") || id.endsWith(".html.jsx") || id.endsWith(".ht.tsx") || id.endsWith(".html.tsx");
1343
1449
  }
@@ -1354,18 +1460,12 @@ function htPages(options = {}) {
1354
1460
  let server = null;
1355
1461
  let devPages = [];
1356
1462
  let watcherAttached = false;
1463
+ let userConfigFile;
1464
+ let resolvedMode = "production";
1465
+ let buildPipelinePromise = null;
1357
1466
  const cleanUrls = options.cleanUrls ?? true;
1358
1467
  const pagesDir = options.pagesDir ?? "src";
1359
- const pageExtensions = options.pageExtensions?.length ? options.pageExtensions : [
1360
- ".ht.js",
1361
- ".html.js",
1362
- ".ht.ts",
1363
- ".html.ts",
1364
- ".ht.jsx",
1365
- ".html.jsx",
1366
- ".ht.tsx",
1367
- ".html.tsx"
1368
- ];
1468
+ const pageExtensions = options.pageExtensions?.length ? options.pageExtensions : DEFAULT_PAGE_EXTENSIONS;
1369
1469
  function logDebug(enabled, ...args) {
1370
1470
  if (!enabled) return;
1371
1471
  console.log(`[${PLUGIN_NAME}]`, ...args);
@@ -1384,13 +1484,13 @@ function htPages(options = {}) {
1384
1484
  entries.map((e) => e.relativePath)
1385
1485
  );
1386
1486
  if (!server) return [];
1387
- const loadModule = await createPageModuleLoader({
1487
+ const loader = await createPageModuleLoader({
1388
1488
  mode: "dev",
1389
1489
  root,
1390
1490
  server
1391
1491
  });
1392
1492
  for (const entry of entries) {
1393
- const mod = await loadModule(entry.entryPath, entry.relativePath);
1493
+ const mod = await loader.loadModule(entry.entryPath, entry.relativePath);
1394
1494
  modulesByEntry.set(entry.entryPath, mod);
1395
1495
  }
1396
1496
  devPages = await buildPageIndex({
@@ -1405,29 +1505,55 @@ function htPages(options = {}) {
1405
1505
  );
1406
1506
  return devPages;
1407
1507
  }
1408
- async function buildPagesPipeline() {
1508
+ async function createBuildPipeline() {
1409
1509
  const entries = await discoverEntryPages(root, options);
1410
1510
  await writePageTypeDeclarations({
1411
1511
  root,
1412
1512
  pagesDir,
1413
1513
  entries
1414
1514
  });
1415
- const modulesByEntry = /* @__PURE__ */ new Map();
1416
- const loadModule = await createPageModuleLoader({
1515
+ const loader = await createPageModuleLoader({
1417
1516
  mode: "build",
1418
1517
  root,
1419
- getPages: async () => entries
1518
+ getPages: async () => entries,
1519
+ configFile: userConfigFile,
1520
+ configMode: resolvedMode
1420
1521
  });
1421
- for (const entry of entries) {
1422
- const mod = await loadModule(entry.entryPath, entry.relativePath);
1423
- modulesByEntry.set(entry.entryPath, mod);
1522
+ try {
1523
+ const modulesByEntry = /* @__PURE__ */ new Map();
1524
+ for (const entry of entries) {
1525
+ const mod = await loader.loadModule(
1526
+ entry.entryPath,
1527
+ entry.relativePath
1528
+ );
1529
+ modulesByEntry.set(entry.entryPath, mod);
1530
+ }
1531
+ const pages = await buildPageIndex({
1532
+ entries,
1533
+ modulesByEntry,
1534
+ cleanUrls
1535
+ });
1536
+ return { entries, modulesByEntry, pages, closeLoader: loader.close };
1537
+ } catch (error) {
1538
+ await loader.close();
1539
+ throw error;
1540
+ }
1541
+ }
1542
+ function getBuildPipeline() {
1543
+ if (!buildPipelinePromise) {
1544
+ buildPipelinePromise = createBuildPipeline();
1545
+ }
1546
+ return buildPipelinePromise;
1547
+ }
1548
+ async function closeBuildPipeline() {
1549
+ const pending = buildPipelinePromise;
1550
+ buildPipelinePromise = null;
1551
+ if (!pending) return;
1552
+ try {
1553
+ const pipeline = await pending;
1554
+ await pipeline.closeLoader();
1555
+ } catch {
1424
1556
  }
1425
- const pages = await buildPageIndex({
1426
- entries,
1427
- modulesByEntry,
1428
- cleanUrls
1429
- });
1430
- return { entries, modulesByEntry, pages };
1431
1557
  }
1432
1558
  return {
1433
1559
  name: PLUGIN_NAME,
@@ -1464,7 +1590,7 @@ function htPages(options = {}) {
1464
1590
  return RESOLVED_VIRTUAL_JSX_DEV_RUNTIME_ID;
1465
1591
  }
1466
1592
  }
1467
- if (importer && isLocalPageTypesImport2(id)) {
1593
+ if (importer && isLocalPageTypesImport(id)) {
1468
1594
  return `${VIRTUAL_LOCAL_TYPES_PREFIX}${importer}::${id}`;
1469
1595
  }
1470
1596
  return null;
@@ -1489,9 +1615,9 @@ export { Fragment, jsx, jsxs, jsxDEV } from ${JSON.stringify(
1489
1615
  }
1490
1616
  if (id.startsWith(RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX)) {
1491
1617
  const importer = id.slice(RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX.length);
1492
- const { pages } = await buildPagesPipeline();
1618
+ const entries = await discoverEntryPages(root, options);
1493
1619
  const normalizedImporter = path9.resolve(importer);
1494
- const page = pages.find(
1620
+ const page = entries.find(
1495
1621
  (candidate) => path9.resolve(candidate.absolutePath) === normalizedImporter
1496
1622
  );
1497
1623
  return generateTypedPageHelper(page);
@@ -1529,12 +1655,15 @@ export {
1529
1655
  },
1530
1656
  configResolved(resolved) {
1531
1657
  root = options.root ? path9.resolve(resolved.root, options.root) : resolved.root;
1658
+ userConfigFile = resolved.configFile ?? void 0;
1659
+ resolvedMode = resolved.mode;
1532
1660
  if (!hasWarnedESM) {
1533
1661
  warnIfNotESM(root);
1534
1662
  hasWarnedESM = true;
1535
1663
  }
1536
1664
  },
1537
1665
  async buildStart() {
1666
+ await closeBuildPipeline();
1538
1667
  const entries = await discoverEntryPages(root, options);
1539
1668
  for (const entry of entries) {
1540
1669
  this.addWatchFile(entry.entryPath);
@@ -1590,8 +1719,10 @@ export {
1590
1719
  );
1591
1720
  }
1592
1721
  };
1722
+ const pagesRoot = path9.join(root, pagesDir);
1593
1723
  const reload = (file) => {
1594
- if (!file.includes(`${path9.sep}${pagesDir}${path9.sep}`) && !file.includes(`/${pagesDir}/`)) {
1724
+ const relative = path9.relative(pagesRoot, file);
1725
+ if (relative === "" || relative.startsWith("..") || path9.isAbsolute(relative) || relative.split(path9.sep).includes("node_modules")) {
1595
1726
  return;
1596
1727
  }
1597
1728
  void reloadAfterChange(file);
@@ -1613,32 +1744,92 @@ export {
1613
1744
  },
1614
1745
  async generateBundle(_, bundle) {
1615
1746
  try {
1616
- const { modulesByEntry, pages } = await buildPagesPipeline();
1617
- const staticAssets = await collectStaticAssets({
1618
- root,
1619
- pagesDir,
1620
- pageExtensions
1621
- });
1747
+ const { modulesByEntry, pages } = await getBuildPipeline();
1622
1748
  logDebug(
1623
1749
  options.debug,
1624
1750
  "emitting pages",
1625
1751
  pages.map((p) => p.fileName)
1626
1752
  );
1753
+ const limit = pLimit(options.renderConcurrency ?? 8);
1754
+ const batchSize = options.renderBatchSize ?? Math.max(options.renderConcurrency ?? 8, 32);
1755
+ const renderedPages = [];
1756
+ for (const batch of chunkArray(pages, batchSize)) {
1757
+ await Promise.all(
1758
+ batch.map(
1759
+ (page) => limit(async () => {
1760
+ const mod = modulesByEntry.get(page.entryPath);
1761
+ if (!mod) {
1762
+ throw new Error(
1763
+ `[${PLUGIN_NAME}] Missing module for page entry: ${page.entryPath}`
1764
+ );
1765
+ }
1766
+ const html = await renderPage(page, mod, false);
1767
+ validateHtmlAssetReferences({
1768
+ root,
1769
+ pagesDir,
1770
+ html,
1771
+ pluginName: PLUGIN_NAME,
1772
+ pageLabel: page.relativePath,
1773
+ missingAssets: options.missingAssets ?? "error"
1774
+ });
1775
+ renderedPages.push({ page, html });
1776
+ })
1777
+ )
1778
+ );
1779
+ }
1780
+ const rendered404 = renderedPages.find(
1781
+ (rendered) => rendered.page.routePath === "/404"
1782
+ );
1783
+ const notFoundHtml = rendered404?.html ?? DEFAULT_404_HTML;
1784
+ logDebug(
1785
+ options.debug,
1786
+ rendered404 ? "generated 404.html from user page" : "generated default 404.html"
1787
+ );
1788
+ const referencedUrls = /* @__PURE__ */ new Set();
1789
+ for (const { html } of renderedPages) {
1790
+ for (const url of collectLocalAssetUrls(html)) {
1791
+ referencedUrls.add(url);
1792
+ }
1793
+ }
1794
+ for (const url of collectLocalAssetUrls(notFoundHtml)) {
1795
+ referencedUrls.add(url);
1796
+ }
1797
+ const staticAssets = await collectStaticAssets({
1798
+ root,
1799
+ pagesDir,
1800
+ pageExtensions
1801
+ });
1802
+ const isReferenced = (asset) => referencedUrls.has(`/${asset.outputFileName}`) || referencedUrls.has(`/${asset.relativePathFromSrc}`);
1803
+ const processableAssets = [];
1804
+ const skippedAssets = [];
1805
+ for (const asset of staticAssets) {
1806
+ if (asset.kind !== "process") continue;
1807
+ if (isReferenced(asset)) {
1808
+ processableAssets.push(asset);
1809
+ } else {
1810
+ skippedAssets.push(asset);
1811
+ }
1812
+ }
1813
+ if (skippedAssets.length > 0) {
1814
+ logDebug(
1815
+ options.debug,
1816
+ "skipping unreferenced code assets",
1817
+ skippedAssets.map((asset) => asset.relativePathFromSrc)
1818
+ );
1819
+ }
1627
1820
  logDebug(
1628
1821
  options.debug,
1629
1822
  "emitting static assets",
1630
- staticAssets.map((asset) => ({
1823
+ staticAssets.filter((asset) => asset.kind === "copy" || isReferenced(asset)).map((asset) => ({
1631
1824
  kind: asset.kind,
1632
1825
  input: asset.relativePathFromSrc,
1633
1826
  output: asset.outputFileName
1634
1827
  }))
1635
1828
  );
1636
- const limit = pLimit(options.renderConcurrency ?? 8);
1637
- const batchSize = options.renderBatchSize ?? Math.max(options.renderConcurrency ?? 8, 32);
1638
1829
  const processedOutputs = await buildProcessedStaticAssets({
1639
1830
  root,
1640
1831
  pagesDir,
1641
- assets: staticAssets,
1832
+ assets: processableAssets,
1642
1833
  minify: true,
1643
1834
  sourcemap: false
1644
1835
  });
@@ -1658,117 +1849,28 @@ export {
1658
1849
  source
1659
1850
  });
1660
1851
  }
1661
- for (const batch of chunkArray(pages, batchSize)) {
1662
- await Promise.all(
1663
- batch.map(
1664
- (page) => limit(async () => {
1665
- const mod = modulesByEntry.get(page.entryPath);
1666
- if (!mod) {
1667
- throw new Error(
1668
- `[${PLUGIN_NAME}] Missing module for page entry: ${page.entryPath}`
1669
- );
1670
- }
1671
- const html = await renderPage(page, mod, false);
1672
- validateHtmlAssetReferences({
1673
- root,
1674
- pagesDir,
1675
- html,
1676
- pluginName: PLUGIN_NAME,
1677
- pageLabel: page.relativePath,
1678
- missingAssets: options.missingAssets ?? "error"
1679
- });
1680
- this.emitFile({
1681
- type: "asset",
1682
- fileName: options.mapOutputPath?.(page) ?? page.fileName,
1683
- source: html
1684
- });
1685
- })
1686
- )
1687
- );
1688
- }
1689
- const notFoundPage = pages.find((p) => p.routePath === "/404");
1690
- if (notFoundPage) {
1691
- const mod = modulesByEntry.get(notFoundPage.entryPath);
1692
- if (!mod) {
1693
- throw new Error(
1694
- `[${PLUGIN_NAME}] Missing module for 404 page entry: ${notFoundPage.entryPath}`
1695
- );
1696
- }
1697
- const html = await renderPage(notFoundPage, mod, false);
1698
- validateHtmlAssetReferences({
1699
- root,
1700
- pagesDir,
1701
- html,
1702
- pluginName: PLUGIN_NAME,
1703
- pageLabel: notFoundPage.relativePath,
1704
- missingAssets: options.missingAssets ?? "error"
1705
- });
1852
+ for (const { page, html } of renderedPages) {
1706
1853
  this.emitFile({
1707
1854
  type: "asset",
1708
- fileName: "404.html",
1855
+ fileName: options.mapOutputPath?.(page) ?? page.fileName,
1709
1856
  source: html
1710
1857
  });
1711
- logDebug(options.debug, "generated 404.html from user page");
1712
- } else {
1713
- const default404 = `<!doctype html>
1714
- <html lang="en">
1715
- <head>
1716
- <meta charset="UTF-8" />
1717
- <meta name="viewport" content="width=device-width, initial-scale=1" />
1718
- <title>404 - Page Not Found</title>
1719
- <style>
1720
- :root {
1721
- color-scheme: light dark;
1722
- }
1723
- body {
1724
- margin: 0;
1725
- font-family: system-ui, sans-serif;
1726
- min-height: 100vh;
1727
- display: grid;
1728
- place-items: center;
1729
- padding: 2rem;
1730
- }
1731
- main {
1732
- max-width: 40rem;
1733
- text-align: center;
1734
- }
1735
- h1 {
1736
- font-size: 3rem;
1737
- margin: 0 0 1rem;
1738
- }
1739
- p {
1740
- margin: 0.5rem 0;
1741
- line-height: 1.5;
1742
- }
1743
- a {
1744
- color: inherit;
1745
- }
1746
- </style>
1747
- </head>
1748
- <body>
1749
- <main>
1750
- <h1>404</h1>
1751
- <p>Page not found.</p>
1752
- <p><a href="/">Go back home</a></p>
1753
- </main>
1754
- </body>
1755
- </html>
1756
- `;
1757
- this.emitFile({
1758
- type: "asset",
1759
- fileName: "404.html",
1760
- source: default404
1761
- });
1762
- logDebug(options.debug, "generated default 404.html");
1763
1858
  }
1764
- const sitemapBase = options.site ?? "";
1859
+ this.emitFile({
1860
+ type: "asset",
1861
+ fileName: "404.html",
1862
+ source: notFoundHtml
1863
+ });
1864
+ const sitemapBase = (options.site ?? "").replace(/\/+$/, "");
1765
1865
  const sitemapRoutes = [...new Set(pages.map((p) => p.routePath))].filter(
1766
1866
  (route) => !route.includes(":") && !route.includes("*")
1767
1867
  );
1768
1868
  if (sitemapBase && sitemapRoutes.length > 0) {
1769
1869
  const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
1770
1870
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
1771
- ${sitemapRoutes.map((route) => ` <url><loc>${sitemapBase}${route}</loc></url>`).join("\n")}
1871
+ ${sitemapRoutes.map(
1872
+ (route) => ` <url><loc>${escapeXml(`${sitemapBase}${route}`)}</loc></url>`
1873
+ ).join("\n")}
1772
1874
  </urlset>
1773
1875
  `;
1774
1876
  this.emitFile({
@@ -1781,10 +1883,11 @@ ${sitemapRoutes.map((route) => ` <url><loc>${sitemapBase}${route}</loc></url>`)
1781
1883
  const rss = options.rss;
1782
1884
  if (rss?.site) {
1783
1885
  const routePrefix = rss.routePrefix ?? "/blog";
1886
+ const rssSite = rss.site.replace(/\/+$/, "");
1784
1887
  const rssItems = pages.filter((page) => page.routePath.startsWith(routePrefix)).map((page) => {
1785
- const url = `${rss.site}${page.routePath}`;
1888
+ const url = escapeXml(`${rssSite}${page.routePath}`);
1786
1889
  return ` <item>
1787
- <title>${page.routePath}</title>
1890
+ <title>${escapeXml(page.routePath)}</title>
1788
1891
  <link>${url}</link>
1789
1892
  <guid>${url}</guid>
1790
1893
  </item>`;
@@ -1792,9 +1895,9 @@ ${sitemapRoutes.map((route) => ` <url><loc>${sitemapBase}${route}</loc></url>`)
1792
1895
  const rssXml = `<?xml version="1.0" encoding="UTF-8"?>
1793
1896
  <rss version="2.0">
1794
1897
  <channel>
1795
- <title>${rss.title ?? PLUGIN_NAME}</title>
1796
- <link>${rss.site}</link>
1797
- <description>${rss.description ?? "RSS feed"}</description>
1898
+ <title>${escapeXml(rss.title ?? PLUGIN_NAME)}</title>
1899
+ <link>${escapeXml(rssSite)}</link>
1900
+ <description>${escapeXml(rss.description ?? "RSS feed")}</description>
1798
1901
  ${rssItems}
1799
1902
  </channel>
1800
1903
  </rss>
@@ -1812,8 +1915,16 @@ ${rssItems}
1812
1915
  }
1813
1916
  }
1814
1917
  } finally {
1815
- await closePageModuleLoader();
1918
+ await closeBuildPipeline();
1919
+ }
1920
+ },
1921
+ async buildEnd(error) {
1922
+ if (error) {
1923
+ await closeBuildPipeline();
1816
1924
  }
1925
+ },
1926
+ async closeBundle() {
1927
+ await closeBuildPipeline();
1817
1928
  }
1818
1929
  };
1819
1930
  }
@@ -1832,8 +1943,20 @@ function createDefaultCacheKey(input, init) {
1832
1943
  });
1833
1944
  return createHash("sha256").update(raw).digest("hex");
1834
1945
  }
1946
+ var SAFE_FILE_KEY_RE = /^[A-Za-z0-9._-]{1,200}$/;
1947
+ function toSafeFileKey(cacheKey) {
1948
+ if (SAFE_FILE_KEY_RE.test(cacheKey)) {
1949
+ return cacheKey;
1950
+ }
1951
+ return createHash("sha256").update(cacheKey).digest("hex");
1952
+ }
1835
1953
  function getCacheFilePath(cacheKey) {
1836
- return path10.join(process.cwd(), CACHE_DIR_NAME, "fetch", `${cacheKey}.json`);
1954
+ return path10.join(
1955
+ process.cwd(),
1956
+ CACHE_DIR_NAME,
1957
+ "fetch",
1958
+ `${toSafeFileKey(cacheKey)}.json`
1959
+ );
1837
1960
  }
1838
1961
  function getEffectiveCacheMode(mode) {
1839
1962
  if (mode === "memory" || mode === "fs" || mode === "none") {
@@ -1841,6 +1964,16 @@ function getEffectiveCacheMode(mode) {
1841
1964
  }
1842
1965
  return process.env.NODE_ENV === "production" ? "fs" : "memory";
1843
1966
  }
1967
+ var BODY_ENCODING_HEADERS = /* @__PURE__ */ new Set([
1968
+ "content-encoding",
1969
+ "content-length",
1970
+ "transfer-encoding"
1971
+ ]);
1972
+ function sanitizeHeaders(headers) {
1973
+ return [...headers.entries()].filter(
1974
+ ([name]) => !BODY_ENCODING_HEADERS.has(name.toLowerCase())
1975
+ );
1976
+ }
1844
1977
  function toResponse(cached) {
1845
1978
  return new Response(cached.body, {
1846
1979
  status: cached.status,
@@ -1885,22 +2018,25 @@ async function fetchWithCache(input, init, options = {}) {
1885
2018
  }
1886
2019
  const res = await fetch(input, init);
1887
2020
  const body = await res.text();
1888
- const record = {
1889
- timestamp: Date.now(),
1890
- status: res.status,
1891
- statusText: res.statusText,
1892
- headers: [...res.headers.entries()],
1893
- body
1894
- };
1895
- if (cacheMode === "memory") {
1896
- memoryCache.set(cacheKey, record);
1897
- } else if (cacheMode === "fs") {
1898
- await fs7.writeFile(filePath, JSON.stringify(record), "utf8");
2021
+ const headers = sanitizeHeaders(res.headers);
2022
+ if (res.ok) {
2023
+ const record = {
2024
+ timestamp: Date.now(),
2025
+ status: res.status,
2026
+ statusText: res.statusText,
2027
+ headers,
2028
+ body
2029
+ };
2030
+ if (cacheMode === "memory") {
2031
+ memoryCache.set(cacheKey, record);
2032
+ } else if (cacheMode === "fs") {
2033
+ await fs7.writeFile(filePath, JSON.stringify(record), "utf8");
2034
+ }
1899
2035
  }
1900
2036
  return new Response(body, {
1901
2037
  status: res.status,
1902
2038
  statusText: res.statusText,
1903
- headers: res.headers
2039
+ headers
1904
2040
  });
1905
2041
  }
1906
2042
  export {