vite-plugin-html-pages 1.2.2 → 1.2.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/index.js CHANGED
@@ -1,16 +1,82 @@
1
1
  // src/plugin.ts
2
2
  import pLimit from "p-limit";
3
3
 
4
+ // src/static-assets.ts
5
+ import fs from "fs/promises";
6
+ import path from "path";
7
+ import fg from "fast-glob";
8
+ import { transformWithEsbuild } from "vite";
9
+ function normalizeSlashes(value) {
10
+ return value.replace(/\\/g, "/");
11
+ }
12
+ function hasAnySuffix(value, suffixes) {
13
+ return suffixes.some((suffix) => value.endsWith(suffix));
14
+ }
15
+ function toOutputFileName(relativePathFromSrc) {
16
+ if (relativePathFromSrc.endsWith(".ts")) {
17
+ return relativePathFromSrc.slice(0, -3) + ".js";
18
+ }
19
+ return relativePathFromSrc;
20
+ }
21
+ async function collectStaticAssets(args) {
22
+ const { root, pagesDir, pageExtensions } = args;
23
+ const srcDir = path.join(root, pagesDir);
24
+ const entries = await fg("**/*", {
25
+ cwd: srcDir,
26
+ onlyFiles: true,
27
+ dot: false,
28
+ absolute: false
29
+ });
30
+ const assets = [];
31
+ for (const entry of entries) {
32
+ const rel = normalizeSlashes(entry);
33
+ if (hasAnySuffix(rel, pageExtensions)) {
34
+ continue;
35
+ }
36
+ const absolutePath = path.join(srcDir, rel);
37
+ if (rel.endsWith(".ts")) {
38
+ assets.push({
39
+ absolutePath,
40
+ relativePathFromSrc: rel,
41
+ outputFileName: normalizeSlashes(toOutputFileName(rel)),
42
+ kind: "ts"
43
+ });
44
+ continue;
45
+ }
46
+ assets.push({
47
+ absolutePath,
48
+ relativePathFromSrc: rel,
49
+ outputFileName: normalizeSlashes(rel),
50
+ kind: "copy"
51
+ });
52
+ }
53
+ return assets;
54
+ }
55
+ async function buildStaticAssetSource(asset) {
56
+ if (asset.kind === "copy") {
57
+ return fs.readFile(asset.absolutePath);
58
+ }
59
+ const source = await fs.readFile(asset.absolutePath, "utf8");
60
+ const result = await transformWithEsbuild(source, asset.absolutePath, {
61
+ loader: "ts",
62
+ format: "esm",
63
+ target: "es2020",
64
+ sourcemap: false,
65
+ minify: false
66
+ });
67
+ return result.code;
68
+ }
69
+
4
70
  // src/discover.ts
5
- import path2 from "path";
71
+ import path3 from "path";
6
72
 
7
73
  // src/path-utils.ts
8
- import path from "path";
74
+ import path2 from "path";
9
75
  function toPosix(value) {
10
76
  return value.replace(/\\/g, "/");
11
77
  }
12
78
  function normalizeFsPath(value) {
13
- return path.normalize(value);
79
+ return path2.normalize(value);
14
80
  }
15
81
  function normalizeRoutePath(value) {
16
82
  const normalized = toPosix(value).replace(/\/+/g, "/");
@@ -83,32 +149,6 @@ function expandStaticPaths(basePage, rows, cleanUrls) {
83
149
  };
84
150
  });
85
151
  }
86
- function routeMatch(pattern, urlPath) {
87
- const a = normalizeRoutePath(pattern).split("/").filter(Boolean);
88
- const b = normalizeRoutePath(urlPath).split("/").filter(Boolean);
89
- const params = {};
90
- for (let i = 0; i < a.length; i++) {
91
- const patternSeg = a[i];
92
- const urlSeg = b[i];
93
- if (patternSeg.startsWith("*?:")) {
94
- params[patternSeg.slice(3)] = i < b.length ? b.slice(i).map(decodeURIComponent).join("/") : "";
95
- return params;
96
- }
97
- if (patternSeg.startsWith("*:")) {
98
- const rest = b.slice(i);
99
- if (rest.length === 0) return null;
100
- params[patternSeg.slice(2)] = rest.map(decodeURIComponent).join("/");
101
- return params;
102
- }
103
- if (!urlSeg) return null;
104
- if (patternSeg.startsWith(":")) {
105
- params[patternSeg.slice(1)] = decodeURIComponent(urlSeg);
106
- continue;
107
- }
108
- if (patternSeg !== urlSeg) return null;
109
- }
110
- return a.length === b.length ? params : null;
111
- }
112
152
  function compareRoutePriority(a, b) {
113
153
  const aSegs = normalizeRoutePath(a).split("/").filter(Boolean);
114
154
  const bSegs = normalizeRoutePath(b).split("/").filter(Boolean);
@@ -152,21 +192,21 @@ function buildDefaultIncludeGlobs(pagesDir, pageExtensions) {
152
192
  }
153
193
  async function discoverEntryPages(root, options) {
154
194
  const fgModule = await import("fast-glob");
155
- const fg = fgModule.default ?? fgModule;
195
+ const fg2 = fgModule.default ?? fgModule;
156
196
  const pagesDir = options.pagesDir ?? "src";
157
197
  const pageExtensions = options.pageExtensions?.length ? options.pageExtensions : [".ht.js", ".html.js", ".ht.ts", ".html.ts"];
158
198
  const include = Array.isArray(options.include) ? options.include : options.include ? [options.include] : buildDefaultIncludeGlobs(pagesDir, pageExtensions);
159
199
  const exclude = Array.isArray(options.exclude) ? options.exclude : options.exclude ? [options.exclude] : [];
160
- const pagesRoot = normalizeFsPath(path2.join(root, pagesDir));
161
- const files = await fg.glob(include, {
200
+ const pagesRoot = normalizeFsPath(path3.join(root, pagesDir));
201
+ const files = await fg2.glob(include, {
162
202
  cwd: root,
163
203
  ignore: exclude,
164
204
  absolute: true
165
205
  });
166
206
  return files.sort().map((absolutePath) => {
167
207
  const entryPath = normalizeFsPath(absolutePath);
168
- const relativePath = toPosix(path2.relative(root, entryPath));
169
- const relativeFromPagesDir = toPosix(path2.relative(pagesRoot, entryPath));
208
+ const relativePath = toPosix(path3.relative(root, entryPath));
209
+ const relativeFromPagesDir = toPosix(path3.relative(pagesRoot, entryPath));
170
210
  if (relativeFromPagesDir.startsWith("../") || relativeFromPagesDir === "..") {
171
211
  throw new Error(
172
212
  `[${PLUGIN_NAME}] Page is outside pagesDir: ${entryPath} (pagesDir: ${pagesDir})`
@@ -189,6 +229,10 @@ async function discoverEntryPages(root, options) {
189
229
  });
190
230
  }
191
231
 
232
+ // src/dev-server.ts
233
+ import fs2 from "fs";
234
+ import path5 from "path";
235
+
192
236
  // src/errors.ts
193
237
  function invalidHtmlReturn(page, value) {
194
238
  return new Error(
@@ -239,57 +283,8 @@ async function renderPage(page, mod, dev = false) {
239
283
  }
240
284
  }
241
285
 
242
- // src/dev-server.ts
243
- function isDynamicOnly(mod) {
244
- return mod.dynamic === true || mod.prerender === false;
245
- }
246
- function installDevServer(args) {
247
- const { server, getPages, getEntries } = args;
248
- server.middlewares.use(async (req, res, next) => {
249
- try {
250
- if (!req.url || req.method !== "GET") return next();
251
- const pathname = req.url.split("?")[0];
252
- const pages = await getPages();
253
- const staticPage = pages.find((p) => p.routePath === pathname);
254
- if (staticPage) {
255
- const mod = await server.ssrLoadModule(
256
- `/${staticPage.relativePath}`
257
- );
258
- const html = await renderPage(staticPage, mod, true);
259
- res.statusCode = 200;
260
- res.setHeader("Content-Type", "text/html; charset=utf-8");
261
- res.end(html);
262
- return;
263
- }
264
- const entries = await getEntries();
265
- for (const entry of entries) {
266
- const mod = await server.ssrLoadModule(
267
- `/${entry.relativePath}`
268
- );
269
- if (!isDynamicOnly(mod)) continue;
270
- const params = routeMatch(entry.routePattern, pathname);
271
- if (!params) continue;
272
- const page = {
273
- ...entry,
274
- routePath: pathname,
275
- fileName: "",
276
- params
277
- };
278
- const html = await renderPage(page, mod, true);
279
- res.statusCode = 200;
280
- res.setHeader("Content-Type", "text/html; charset=utf-8");
281
- res.end(html);
282
- return;
283
- }
284
- next();
285
- } catch (error) {
286
- next(error);
287
- }
288
- });
289
- }
290
-
291
286
  // src/module-loader.ts
292
- import path3 from "path";
287
+ import path4 from "path";
293
288
  import { createServer } from "vite";
294
289
  var buildServer = null;
295
290
  async function createPageModuleLoader(args) {
@@ -316,7 +311,7 @@ async function createPageModuleLoader(args) {
316
311
  buildServer = await createServer(config);
317
312
  }
318
313
  return async (entryPath) => {
319
- const relativePath = "/" + path3.relative(root, entryPath).replace(/\\/g, "/");
314
+ const relativePath = "/" + path4.relative(root, entryPath).replace(/\\/g, "/");
320
315
  const mod = await buildServer.ssrLoadModule(relativePath);
321
316
  return mod;
322
317
  };
@@ -328,6 +323,71 @@ async function closePageModuleLoader() {
328
323
  }
329
324
  }
330
325
 
326
+ // src/dev-server.ts
327
+ function isStaticAssetRequest(url) {
328
+ return url.endsWith(".css") || url.endsWith(".js") || url.endsWith(".mjs") || url.endsWith(".ts") || url.endsWith(".png") || url.endsWith(".jpg") || url.endsWith(".jpeg") || url.endsWith(".gif") || url.endsWith(".svg") || url.endsWith(".webp") || url.endsWith(".ico") || url.endsWith(".woff") || url.endsWith(".woff2") || url.endsWith(".ttf") || url.endsWith(".otf");
329
+ }
330
+ function shouldSkipHtmlRouting(url) {
331
+ return url.startsWith("/@vite") || url.startsWith("/@fs/") || url.startsWith("/node_modules/") || url.startsWith("/src/") || url === "/favicon.ico" || isStaticAssetRequest(url);
332
+ }
333
+ function tryRewriteRootAssetToSrc(server, url) {
334
+ if (!url.startsWith("/")) return null;
335
+ if (!isStaticAssetRequest(url)) return null;
336
+ if (url.startsWith("/src/")) return null;
337
+ const root = server.config.root;
338
+ const candidate = path5.join(root, "src", url.slice(1));
339
+ if (fs2.existsSync(candidate)) {
340
+ return `/src/${url.slice(1)}`;
341
+ }
342
+ return null;
343
+ }
344
+ function shouldUseDynamicRendering(mod) {
345
+ return mod.dynamic === true || mod.prerender === false;
346
+ }
347
+ function installDevServer(args) {
348
+ const { server, getPages } = args;
349
+ server.middlewares.use(async (req, res, next) => {
350
+ try {
351
+ const originalUrl = req.url ?? "/";
352
+ const url = originalUrl.split("?")[0];
353
+ const rewrittenAssetUrl = tryRewriteRootAssetToSrc(server, url);
354
+ if (rewrittenAssetUrl) {
355
+ req.url = rewrittenAssetUrl + originalUrl.slice(url.length);
356
+ return next();
357
+ }
358
+ if (shouldSkipHtmlRouting(url)) {
359
+ return next();
360
+ }
361
+ const pages = await getPages();
362
+ const page = pages.find((p) => p.routePath === url);
363
+ if (!page) {
364
+ return next();
365
+ }
366
+ const loadModule = await createPageModuleLoader({
367
+ mode: "dev",
368
+ root: server.config.root,
369
+ server
370
+ });
371
+ const mod = await loadModule(page.entryPath, page.relativePath);
372
+ if (!mod) {
373
+ return next();
374
+ }
375
+ if (!shouldUseDynamicRendering(mod) && page.dynamic) {
376
+ return next();
377
+ }
378
+ const html = await renderPage(page, mod, true);
379
+ res.statusCode = 200;
380
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
381
+ res.end(html);
382
+ } catch (error) {
383
+ server.config.logger.error(
384
+ `[${PLUGIN_NAME}] dev server render failed: ${error instanceof Error ? error.stack ?? error.message : String(error)}`
385
+ );
386
+ next(error);
387
+ }
388
+ });
389
+ }
390
+
331
391
  // src/page-index.ts
332
392
  async function buildPageIndex(args) {
333
393
  const { entries, modulesByEntry, cleanUrls } = args;
@@ -374,156 +434,15 @@ async function buildPageIndex(args) {
374
434
  return pages;
375
435
  }
376
436
 
377
- // src/assets.ts
378
- import fs from "fs";
379
- import path4 from "path";
380
- var EXTERNAL_URL_RE = /^(?:[a-z]+:)?\/\//i;
381
- function isLocalAssetUrl(url) {
382
- return !!url && !url.startsWith("data:") && !url.startsWith("mailto:") && !url.startsWith("tel:") && !url.startsWith("#") && !EXTERNAL_URL_RE.test(url);
383
- }
384
- function stripQueryAndHash(url) {
385
- return url.split("#")[0].split("?")[0];
386
- }
387
- function extractHtmlAssets(html) {
388
- const assets = [];
389
- for (const match of html.matchAll(
390
- /<link\b[^>]*\brel=["']stylesheet["'][^>]*\bhref=["']([^"']+)["'][^>]*>/gi
391
- )) {
392
- assets.push({ kind: "css", url: match[1] });
393
- }
394
- for (const match of html.matchAll(
395
- /<script\b[^>]*\bsrc=["']([^"']+)["'][^>]*>/gi
396
- )) {
397
- assets.push({ kind: "js", url: match[1] });
398
- }
399
- return dedupeExtractedAssets(assets);
400
- }
401
- function dedupeExtractedAssets(assets) {
402
- const seen = /* @__PURE__ */ new Set();
403
- const out = [];
404
- for (const asset of assets) {
405
- const key = `${asset.kind}:${asset.url}`;
406
- if (seen.has(key)) continue;
407
- seen.add(key);
408
- out.push(asset);
409
- }
410
- return out;
411
- }
412
- function resolveLocalAssetPath(args) {
413
- const { root, pagesDir, pageDir, url } = args;
414
- if (!isLocalAssetUrl(url)) return null;
415
- const cleanUrl = stripQueryAndHash(url);
416
- let abs;
417
- if (cleanUrl.startsWith("/")) {
418
- abs = path4.join(root, pagesDir, cleanUrl.slice(1));
419
- } else {
420
- const baseDir = pageDir ?? path4.join(root, pagesDir);
421
- abs = path4.resolve(baseDir, cleanUrl);
422
- }
423
- return fs.existsSync(abs) ? abs : null;
424
- }
425
- function emitHtmlAsset(args) {
426
- const { ctx, kind, absolutePath } = args;
427
- if (kind === "css" || kind === "js") {
428
- return ctx.emitFile({
429
- type: "chunk",
430
- id: absolutePath,
431
- name: path4.basename(absolutePath, path4.extname(absolutePath))
432
- });
433
- }
434
- throw new Error(`[vite-plugin-html-pages] Unsupported asset kind: ${kind}`);
435
- }
436
- function replaceAllLiteral(input, search, replacement) {
437
- return input.split(search).join(replacement);
438
- }
439
- function rewriteHtmlAssetUrls(html, replacements) {
440
- let out = html;
441
- for (const [originalUrl, builtUrl] of replacements) {
442
- out = replaceAllLiteral(
443
- out,
444
- `href="${originalUrl}"`,
445
- `href="${builtUrl}"`
446
- );
447
- out = replaceAllLiteral(
448
- out,
449
- `href='${originalUrl}'`,
450
- `href='${builtUrl}'`
451
- );
452
- out = replaceAllLiteral(
453
- out,
454
- `src="${originalUrl}"`,
455
- `src="${builtUrl}"`
456
- );
457
- out = replaceAllLiteral(
458
- out,
459
- `src='${originalUrl}'`,
460
- `src='${builtUrl}'`
461
- );
462
- }
463
- return out;
464
- }
465
- async function collectHtmlAssetRefs(args) {
466
- const { ctx, root, pagesDir, htmlByPageKey } = args;
467
- const refs = /* @__PURE__ */ new Map();
468
- for (const { html, pageDir } of htmlByPageKey.values()) {
469
- const assets = extractHtmlAssets(html);
470
- for (const asset of assets) {
471
- const abs = resolveLocalAssetPath({
472
- root,
473
- pagesDir,
474
- pageDir,
475
- url: asset.url
476
- });
477
- if (!abs) continue;
478
- const key = `${asset.kind}:${asset.url}`;
479
- if (refs.has(key)) continue;
480
- const refId = emitHtmlAsset({
481
- ctx,
482
- kind: asset.kind,
483
- absolutePath: abs
484
- });
485
- refs.set(key, {
486
- kind: asset.kind,
487
- originalUrl: asset.url,
488
- absolutePath: abs,
489
- refId
490
- });
491
- }
492
- }
493
- return refs;
494
- }
495
- function buildHtmlAssetReplacementMap(args) {
496
- const { ctx, refs, bundle } = args;
497
- const replacements = /* @__PURE__ */ new Map();
498
- for (const ref of refs.values()) {
499
- if (ref.kind === "js") {
500
- const fileName = ctx.getFileName(ref.refId);
501
- replacements.set(ref.originalUrl, `/${fileName}`);
502
- continue;
503
- }
504
- if (ref.kind === "css") {
505
- const jsEntryFile = ctx.getFileName(ref.refId);
506
- const jsChunk = bundle[jsEntryFile];
507
- if (jsChunk && jsChunk.type === "chunk" && "viteMetadata" in jsChunk && jsChunk.viteMetadata?.importedCss && jsChunk.viteMetadata.importedCss.size > 0) {
508
- const cssFile = [...jsChunk.viteMetadata.importedCss][0];
509
- replacements.set(ref.originalUrl, `/${cssFile}`);
510
- continue;
511
- }
512
- replacements.set(ref.originalUrl, `/${jsEntryFile}`);
513
- }
514
- }
515
- return replacements;
516
- }
517
-
518
437
  // src/plugin.ts
519
- import fs2 from "fs";
520
- import path5 from "path";
438
+ import fs3 from "fs";
439
+ import path6 from "path";
521
440
  var hasWarnedESM = false;
522
441
  function warnIfNotESM(root) {
523
442
  try {
524
- const pkgPath = path5.join(root, "package.json");
525
- if (!fs2.existsSync(pkgPath)) return;
526
- const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf8"));
443
+ const pkgPath = path6.join(root, "package.json");
444
+ if (!fs3.existsSync(pkgPath)) return;
445
+ const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf8"));
527
446
  if (pkg.type !== "module") {
528
447
  console.warn(
529
448
  `[${PLUGIN_NAME}] \u26A0\uFE0F It is recommended to add "type": "module" to your package.json for optimal performance and to avoid Node ESM warnings.`
@@ -543,9 +462,9 @@ function htPages(options = {}) {
543
462
  let root = process.cwd();
544
463
  let server = null;
545
464
  let devPages = [];
546
- let htmlAssetRefs = /* @__PURE__ */ new Map();
547
465
  const cleanUrls = options.cleanUrls ?? true;
548
466
  const pagesDir = options.pagesDir ?? "src";
467
+ const pageExtensions = options.pageExtensions?.length ? options.pageExtensions : [".ht.js", ".html.js", ".ht.ts", ".html.ts"];
549
468
  function logDebug(enabled, ...args) {
550
469
  if (!enabled) return;
551
470
  console.log(`[${PLUGIN_NAME}]`, ...args);
@@ -634,38 +553,21 @@ function htPages(options = {}) {
634
553
  for (const entry of entries) {
635
554
  this.addWatchFile(entry.entryPath);
636
555
  }
637
- if (server) {
638
- return;
639
- }
640
- htmlAssetRefs.clear();
641
- const { modulesByEntry, pages } = await buildPagesPipeline();
642
- const htmlByPageKey = /* @__PURE__ */ new Map();
643
- for (const page of pages) {
644
- const mod = modulesByEntry.get(page.entryPath);
645
- if (!mod) {
646
- throw new Error(
647
- `[${PLUGIN_NAME}] Missing module for page entry: ${page.entryPath}`
648
- );
649
- }
650
- const html = await renderPage(page, mod, false);
651
- htmlByPageKey.set(page.entryPath, {
652
- html,
653
- pageDir: path5.dirname(page.absolutePath)
654
- });
655
- }
656
- htmlAssetRefs = await collectHtmlAssetRefs({
657
- ctx: this,
556
+ const staticAssets = await collectStaticAssets({
658
557
  root,
659
558
  pagesDir,
660
- htmlByPageKey
559
+ pageExtensions
661
560
  });
561
+ for (const asset of staticAssets) {
562
+ this.addWatchFile(asset.absolutePath);
563
+ }
662
564
  logDebug(
663
565
  options.debug,
664
- "collected html assets",
665
- [...htmlAssetRefs.values()].map((ref) => ({
666
- kind: ref.kind,
667
- originalUrl: ref.originalUrl,
668
- absolutePath: ref.absolutePath
566
+ "static assets",
567
+ staticAssets.map((asset) => ({
568
+ kind: asset.kind,
569
+ input: asset.relativePathFromSrc,
570
+ output: asset.outputFileName
669
571
  }))
670
572
  );
671
573
  },
@@ -694,23 +596,31 @@ function htPages(options = {}) {
694
596
  async generateBundle(_, bundle) {
695
597
  try {
696
598
  const { modulesByEntry, pages } = await buildPagesPipeline();
697
- const assetReplacements = buildHtmlAssetReplacementMap({
698
- ctx: this,
699
- refs: htmlAssetRefs,
700
- bundle
599
+ const staticAssets = await collectStaticAssets({
600
+ root,
601
+ pagesDir,
602
+ pageExtensions
701
603
  });
702
604
  logDebug(
703
605
  options.debug,
704
- "asset replacements",
705
- [...assetReplacements.entries()]
606
+ "emitting pages",
607
+ pages.map((p) => p.fileName)
706
608
  );
707
609
  logDebug(
708
610
  options.debug,
709
- "emitting pages",
710
- pages.map((p) => p.fileName)
611
+ "emitting static assets",
612
+ staticAssets.map((asset) => asset.outputFileName)
711
613
  );
712
614
  const limit = pLimit(options.renderConcurrency ?? 8);
713
615
  const batchSize = options.renderBatchSize ?? Math.max(options.renderConcurrency ?? 8, 32);
616
+ for (const asset of staticAssets) {
617
+ const source = await buildStaticAssetSource(asset);
618
+ this.emitFile({
619
+ type: "asset",
620
+ fileName: asset.outputFileName,
621
+ source
622
+ });
623
+ }
714
624
  for (const batch of chunkArray(pages, batchSize)) {
715
625
  await Promise.all(
716
626
  batch.map(
@@ -721,8 +631,7 @@ function htPages(options = {}) {
721
631
  `[${PLUGIN_NAME}] Missing module for page entry: ${page.entryPath}`
722
632
  );
723
633
  }
724
- let html = await renderPage(page, mod, false);
725
- html = rewriteHtmlAssetUrls(html, assetReplacements);
634
+ const html = await renderPage(page, mod, false);
726
635
  this.emitFile({
727
636
  type: "asset",
728
637
  fileName: options.mapOutputPath?.(page) ?? page.fileName,
@@ -740,8 +649,7 @@ function htPages(options = {}) {
740
649
  `[${PLUGIN_NAME}] Missing module for 404 page entry: ${notFoundPage.entryPath}`
741
650
  );
742
651
  }
743
- let html = await renderPage(notFoundPage, mod, false);
744
- html = rewriteHtmlAssetUrls(html, assetReplacements);
652
+ const html = await renderPage(notFoundPage, mod, false);
745
653
  this.emitFile({
746
654
  type: "asset",
747
655
  fileName: "404.html",
@@ -750,49 +658,49 @@ function htPages(options = {}) {
750
658
  logDebug(options.debug, "generated 404.html from user page");
751
659
  } else {
752
660
  const default404 = `<!doctype html>
753
- <html lang="en">
754
- <head>
755
- <meta charset="UTF-8" />
756
- <meta name="viewport" content="width=device-width, initial-scale=1" />
757
- <title>404 - Page Not Found</title>
758
- <style>
759
- :root {
760
- color-scheme: light dark;
761
- }
762
- body {
763
- margin: 0;
764
- font-family: system-ui, sans-serif;
765
- min-height: 100vh;
766
- display: grid;
767
- place-items: center;
768
- padding: 2rem;
769
- }
770
- main {
771
- max-width: 40rem;
772
- text-align: center;
773
- }
774
- h1 {
775
- font-size: 3rem;
776
- margin: 0 0 1rem;
777
- }
778
- p {
779
- margin: 0.5rem 0;
780
- line-height: 1.5;
781
- }
782
- a {
783
- color: inherit;
784
- }
785
- </style>
786
- </head>
787
- <body>
788
- <main>
789
- <h1>404</h1>
790
- <p>Page not found.</p>
791
- <p><a href="/">Go back home</a></p>
792
- </main>
793
- </body>
794
- </html>
795
- `;
661
+ <html lang="en">
662
+ <head>
663
+ <meta charset="UTF-8" />
664
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
665
+ <title>404 - Page Not Found</title>
666
+ <style>
667
+ :root {
668
+ color-scheme: light dark;
669
+ }
670
+ body {
671
+ margin: 0;
672
+ font-family: system-ui, sans-serif;
673
+ min-height: 100vh;
674
+ display: grid;
675
+ place-items: center;
676
+ padding: 2rem;
677
+ }
678
+ main {
679
+ max-width: 40rem;
680
+ text-align: center;
681
+ }
682
+ h1 {
683
+ font-size: 3rem;
684
+ margin: 0 0 1rem;
685
+ }
686
+ p {
687
+ margin: 0.5rem 0;
688
+ line-height: 1.5;
689
+ }
690
+ a {
691
+ color: inherit;
692
+ }
693
+ </style>
694
+ </head>
695
+ <body>
696
+ <main>
697
+ <h1>404</h1>
698
+ <p>Page not found.</p>
699
+ <p><a href="/">Go back home</a></p>
700
+ </main>
701
+ </body>
702
+ </html>
703
+ `;
796
704
  this.emitFile({
797
705
  type: "asset",
798
706
  fileName: "404.html",
@@ -857,8 +765,8 @@ ${rssItems}
857
765
  }
858
766
 
859
767
  // src/fetch-cache.ts
860
- import fs3 from "fs/promises";
861
- import path6 from "path";
768
+ import fs4 from "fs/promises";
769
+ import path7 from "path";
862
770
  import { createHash } from "crypto";
863
771
  var memoryCache = /* @__PURE__ */ new Map();
864
772
  function createDefaultCacheKey(input, init) {
@@ -871,7 +779,7 @@ function createDefaultCacheKey(input, init) {
871
779
  return createHash("sha256").update(raw).digest("hex");
872
780
  }
873
781
  function getCacheFilePath(cacheKey) {
874
- return path6.join(process.cwd(), CACHE_DIR_NAME, "fetch", `${cacheKey}.json`);
782
+ return path7.join(process.cwd(), CACHE_DIR_NAME, "fetch", `${cacheKey}.json`);
875
783
  }
876
784
  function getEffectiveCacheMode(mode) {
877
785
  if (mode === "memory" || mode === "fs" || mode === "none") {
@@ -909,10 +817,10 @@ async function fetchWithCache(input, init, options = {}) {
909
817
  }
910
818
  const filePath = getCacheFilePath(cacheKey);
911
819
  if (cacheMode === "fs") {
912
- await fs3.mkdir(path6.dirname(filePath), { recursive: true });
820
+ await fs4.mkdir(path7.dirname(filePath), { recursive: true });
913
821
  if (!options.forceRefresh) {
914
822
  try {
915
- const raw = await fs3.readFile(filePath, "utf8");
823
+ const raw = await fs4.readFile(filePath, "utf8");
916
824
  const cached = JSON.parse(raw);
917
825
  if (isFresh(cached, maxAge)) {
918
826
  return toResponse(cached);
@@ -933,7 +841,7 @@ async function fetchWithCache(input, init, options = {}) {
933
841
  if (cacheMode === "memory") {
934
842
  memoryCache.set(cacheKey, record);
935
843
  } else if (cacheMode === "fs") {
936
- await fs3.writeFile(filePath, JSON.stringify(record), "utf8");
844
+ await fs4.writeFile(filePath, JSON.stringify(record), "utf8");
937
845
  }
938
846
  return new Response(body, {
939
847
  status: res.status,