vite-plugin-html-pages 1.2.1 → 1.2.3

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
@@ -83,32 +83,6 @@ function expandStaticPaths(basePage, rows, cleanUrls) {
83
83
  };
84
84
  });
85
85
  }
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
86
  function compareRoutePriority(a, b) {
113
87
  const aSegs = normalizeRoutePath(a).split("/").filter(Boolean);
114
88
  const bSegs = normalizeRoutePath(b).split("/").filter(Boolean);
@@ -189,6 +163,10 @@ async function discoverEntryPages(root, options) {
189
163
  });
190
164
  }
191
165
 
166
+ // src/dev-server.ts
167
+ import fs from "fs";
168
+ import path4 from "path";
169
+
192
170
  // src/errors.ts
193
171
  function invalidHtmlReturn(page, value) {
194
172
  return new Error(
@@ -239,55 +217,6 @@ async function renderPage(page, mod, dev = false) {
239
217
  }
240
218
  }
241
219
 
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
220
  // src/module-loader.ts
292
221
  import path3 from "path";
293
222
  import { createServer } from "vite";
@@ -328,6 +257,71 @@ async function closePageModuleLoader() {
328
257
  }
329
258
  }
330
259
 
260
+ // src/dev-server.ts
261
+ function isStaticAssetRequest(url) {
262
+ 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");
263
+ }
264
+ function shouldSkipHtmlRouting(url) {
265
+ return url.startsWith("/@vite") || url.startsWith("/@fs/") || url.startsWith("/node_modules/") || url.startsWith("/src/") || url === "/favicon.ico" || isStaticAssetRequest(url);
266
+ }
267
+ function tryRewriteRootAssetToSrc(server, url) {
268
+ if (!url.startsWith("/")) return null;
269
+ if (!isStaticAssetRequest(url)) return null;
270
+ if (url.startsWith("/src/")) return null;
271
+ const root = server.config.root;
272
+ const candidate = path4.join(root, "src", url.slice(1));
273
+ if (fs.existsSync(candidate)) {
274
+ return `/src/${url.slice(1)}`;
275
+ }
276
+ return null;
277
+ }
278
+ function shouldUseDynamicRendering(mod) {
279
+ return mod.dynamic === true || mod.prerender === false;
280
+ }
281
+ function installDevServer(args) {
282
+ const { server, getPages } = args;
283
+ server.middlewares.use(async (req, res, next) => {
284
+ try {
285
+ const originalUrl = req.url ?? "/";
286
+ const url = originalUrl.split("?")[0];
287
+ const rewrittenAssetUrl = tryRewriteRootAssetToSrc(server, url);
288
+ if (rewrittenAssetUrl) {
289
+ req.url = rewrittenAssetUrl + originalUrl.slice(url.length);
290
+ return next();
291
+ }
292
+ if (shouldSkipHtmlRouting(url)) {
293
+ return next();
294
+ }
295
+ const pages = await getPages();
296
+ const page = pages.find((p) => p.routePath === url);
297
+ if (!page) {
298
+ return next();
299
+ }
300
+ const loadModule = await createPageModuleLoader({
301
+ mode: "dev",
302
+ root: server.config.root,
303
+ server
304
+ });
305
+ const mod = await loadModule(page.entryPath, page.relativePath);
306
+ if (!mod) {
307
+ return next();
308
+ }
309
+ if (!shouldUseDynamicRendering(mod) && page.dynamic) {
310
+ return next();
311
+ }
312
+ const html = await renderPage(page, mod, true);
313
+ res.statusCode = 200;
314
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
315
+ res.end(html);
316
+ } catch (error) {
317
+ server.config.logger.error(
318
+ `[${PLUGIN_NAME}] dev server render failed: ${error instanceof Error ? error.stack ?? error.message : String(error)}`
319
+ );
320
+ next(error);
321
+ }
322
+ });
323
+ }
324
+
331
325
  // src/page-index.ts
332
326
  async function buildPageIndex(args) {
333
327
  const { entries, modulesByEntry, cleanUrls } = args;
@@ -375,8 +369,8 @@ async function buildPageIndex(args) {
375
369
  }
376
370
 
377
371
  // src/assets.ts
378
- import fs from "fs";
379
- import path4 from "path";
372
+ import fs2 from "fs";
373
+ import path5 from "path";
380
374
  var EXTERNAL_URL_RE = /^(?:[a-z]+:)?\/\//i;
381
375
  function isLocalAssetUrl(url) {
382
376
  return !!url && !url.startsWith("data:") && !url.startsWith("mailto:") && !url.startsWith("tel:") && !url.startsWith("#") && !EXTERNAL_URL_RE.test(url);
@@ -415,12 +409,14 @@ function resolveLocalAssetPath(args) {
415
409
  const cleanUrl = stripQueryAndHash(url);
416
410
  let abs;
417
411
  if (cleanUrl.startsWith("/")) {
418
- abs = path4.join(root, pagesDir, cleanUrl.slice(1));
412
+ abs = path5.join(root, pagesDir, cleanUrl.slice(1));
413
+ } else if (cleanUrl.startsWith(`${pagesDir}/`)) {
414
+ abs = path5.join(root, cleanUrl);
419
415
  } else {
420
- const baseDir = pageDir ?? path4.join(root, pagesDir);
421
- abs = path4.resolve(baseDir, cleanUrl);
416
+ const baseDir = pageDir ?? path5.join(root, pagesDir);
417
+ abs = path5.resolve(baseDir, cleanUrl);
422
418
  }
423
- return fs.existsSync(abs) ? abs : null;
419
+ return fs2.existsSync(abs) ? abs : null;
424
420
  }
425
421
  function emitHtmlAsset(args) {
426
422
  const { ctx, kind, absolutePath } = args;
@@ -428,7 +424,7 @@ function emitHtmlAsset(args) {
428
424
  return ctx.emitFile({
429
425
  type: "chunk",
430
426
  id: absolutePath,
431
- name: path4.basename(absolutePath, path4.extname(absolutePath))
427
+ name: path5.basename(absolutePath, path5.extname(absolutePath))
432
428
  });
433
429
  }
434
430
  throw new Error(`[vite-plugin-html-pages] Unsupported asset kind: ${kind}`);
@@ -516,14 +512,14 @@ function buildHtmlAssetReplacementMap(args) {
516
512
  }
517
513
 
518
514
  // src/plugin.ts
519
- import fs2 from "fs";
520
- import path5 from "path";
515
+ import fs3 from "fs";
516
+ import path6 from "path";
521
517
  var hasWarnedESM = false;
522
518
  function warnIfNotESM(root) {
523
519
  try {
524
- const pkgPath = path5.join(root, "package.json");
525
- if (!fs2.existsSync(pkgPath)) return;
526
- const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf8"));
520
+ const pkgPath = path6.join(root, "package.json");
521
+ if (!fs3.existsSync(pkgPath)) return;
522
+ const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf8"));
527
523
  if (pkg.type !== "module") {
528
524
  console.warn(
529
525
  `[${PLUGIN_NAME}] \u26A0\uFE0F It is recommended to add "type": "module" to your package.json for optimal performance and to avoid Node ESM warnings.`
@@ -630,11 +626,15 @@ function htPages(options = {}) {
630
626
  }
631
627
  },
632
628
  async buildStart() {
633
- htmlAssetRefs.clear();
634
- const { entries, modulesByEntry, pages } = await buildPagesPipeline();
629
+ const entries = await discoverEntryPages(root, options);
635
630
  for (const entry of entries) {
636
631
  this.addWatchFile(entry.entryPath);
637
632
  }
633
+ if (server) {
634
+ return;
635
+ }
636
+ htmlAssetRefs.clear();
637
+ const { modulesByEntry, pages } = await buildPagesPipeline();
638
638
  const htmlByPageKey = /* @__PURE__ */ new Map();
639
639
  for (const page of pages) {
640
640
  const mod = modulesByEntry.get(page.entryPath);
@@ -646,7 +646,7 @@ function htPages(options = {}) {
646
646
  const html = await renderPage(page, mod, false);
647
647
  htmlByPageKey.set(page.entryPath, {
648
648
  html,
649
- pageDir: path5.dirname(page.absolutePath)
649
+ pageDir: path6.dirname(page.absolutePath)
650
650
  });
651
651
  }
652
652
  htmlAssetRefs = await collectHtmlAssetRefs({
@@ -853,8 +853,8 @@ ${rssItems}
853
853
  }
854
854
 
855
855
  // src/fetch-cache.ts
856
- import fs3 from "fs/promises";
857
- import path6 from "path";
856
+ import fs4 from "fs/promises";
857
+ import path7 from "path";
858
858
  import { createHash } from "crypto";
859
859
  var memoryCache = /* @__PURE__ */ new Map();
860
860
  function createDefaultCacheKey(input, init) {
@@ -867,7 +867,7 @@ function createDefaultCacheKey(input, init) {
867
867
  return createHash("sha256").update(raw).digest("hex");
868
868
  }
869
869
  function getCacheFilePath(cacheKey) {
870
- return path6.join(process.cwd(), CACHE_DIR_NAME, "fetch", `${cacheKey}.json`);
870
+ return path7.join(process.cwd(), CACHE_DIR_NAME, "fetch", `${cacheKey}.json`);
871
871
  }
872
872
  function getEffectiveCacheMode(mode) {
873
873
  if (mode === "memory" || mode === "fs" || mode === "none") {
@@ -905,10 +905,10 @@ async function fetchWithCache(input, init, options = {}) {
905
905
  }
906
906
  const filePath = getCacheFilePath(cacheKey);
907
907
  if (cacheMode === "fs") {
908
- await fs3.mkdir(path6.dirname(filePath), { recursive: true });
908
+ await fs4.mkdir(path7.dirname(filePath), { recursive: true });
909
909
  if (!options.forceRefresh) {
910
910
  try {
911
- const raw = await fs3.readFile(filePath, "utf8");
911
+ const raw = await fs4.readFile(filePath, "utf8");
912
912
  const cached = JSON.parse(raw);
913
913
  if (isFresh(cached, maxAge)) {
914
914
  return toResponse(cached);
@@ -929,7 +929,7 @@ async function fetchWithCache(input, init, options = {}) {
929
929
  if (cacheMode === "memory") {
930
930
  memoryCache.set(cacheKey, record);
931
931
  } else if (cacheMode === "fs") {
932
- await fs3.writeFile(filePath, JSON.stringify(record), "utf8");
932
+ await fs4.writeFile(filePath, JSON.stringify(record), "utf8");
933
933
  }
934
934
  return new Response(body, {
935
935
  status: res.status,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/plugin.ts","../src/discover.ts","../src/path-utils.ts","../src/route-utils.ts","../src/constants.ts","../src/errors.ts","../src/render-runtime.ts","../src/dev-server.ts","../src/module-loader.ts","../src/page-index.ts","../src/assets.ts","../src/fetch-cache.ts"],"sourcesContent":["import pLimit from 'p-limit';\nimport type { Plugin, ViteDevServer } from 'vite';\n\nimport { discoverEntryPages } from './discover';\nimport { installDevServer } from './dev-server';\nimport { createPageModuleLoader, closePageModuleLoader } from './module-loader';\nimport { buildPageIndex } from './page-index';\nimport { renderPage } from './render-runtime';\nimport {\n buildHtmlAssetReplacementMap,\n collectHtmlAssetRefs,\n rewriteHtmlAssetUrls,\n} from './assets';\n\nimport type { HtPageInfo, HtPageModule, HtPagesPluginOptions } from './types';\nimport type { HtmlAssetRef } from './assets';\nimport { PLUGIN_NAME, VIRTUAL_BUILD_ENTRY_ID } from './constants';\n\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nlet hasWarnedESM = false;\n\nfunction warnIfNotESM(root: string) {\n try {\n const pkgPath = path.join(root, 'package.json');\n\n if (!fs.existsSync(pkgPath)) return;\n\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));\n\n if (pkg.type !== 'module') {\n console.warn(\n `[${PLUGIN_NAME}] ⚠️ It is recommended to add \"type\": \"module\" to your package.json for optimal performance and to avoid Node ESM warnings.`,\n );\n }\n } catch {\n // silent — never break build\n }\n}\n\nfunction chunkArray<T>(items: T[], size: number): T[][] {\n const out: T[][] = [];\n for (let i = 0; i < items.length; i += size) {\n out.push(items.slice(i, i + size));\n }\n return out;\n}\n\nexport function htPages(options: HtPagesPluginOptions = {}): Plugin {\n let root = process.cwd();\n let server: ViteDevServer | null = null;\n let devPages: HtPageInfo[] = [];\n let htmlAssetRefs = new Map<string, HtmlAssetRef>();\n\n const cleanUrls = options.cleanUrls ?? true;\n const pagesDir = options.pagesDir ?? 'src';\n\n function logDebug(enabled: boolean | undefined, ...args: unknown[]) {\n if (!enabled) return;\n console.log(`[${PLUGIN_NAME}]`, ...args);\n }\n\n async function loadDevPages(): Promise<HtPageInfo[]> {\n const entries = await discoverEntryPages(root, options);\n const modulesByEntry = new Map<string, HtPageModule>();\n\n logDebug(\n options.debug,\n 'discovered entries',\n entries.map((e) => e.relativePath),\n );\n\n if (!server) return [];\n\n const loadModule = await createPageModuleLoader({\n mode: 'dev',\n root,\n server,\n });\n\n for (const entry of entries) {\n const mod = await loadModule(entry.entryPath, entry.relativePath);\n modulesByEntry.set(entry.entryPath, mod);\n }\n\n devPages = await buildPageIndex({\n entries,\n modulesByEntry,\n cleanUrls,\n });\n\n logDebug(\n options.debug,\n 'dev pages',\n devPages.map((p) => `${p.routePath} -> ${p.relativePath}`),\n );\n\n return devPages;\n }\n\n async function buildPagesPipeline() {\n const entries = await discoverEntryPages(root, options);\n const modulesByEntry = new Map<string, HtPageModule>();\n\n const loadModule = await createPageModuleLoader({\n mode: 'build',\n root,\n });\n\n for (const entry of entries) {\n const mod = await loadModule(entry.entryPath, entry.relativePath);\n modulesByEntry.set(entry.entryPath, mod);\n }\n\n const pages = await buildPageIndex({\n entries,\n modulesByEntry,\n cleanUrls,\n });\n\n return { entries, modulesByEntry, pages };\n }\n\n return {\n name: PLUGIN_NAME,\n\n config(userConfig, env) {\n if (env.command !== 'build') return;\n\n const hasExplicitInput = userConfig.build?.rollupOptions?.input != null;\n if (hasExplicitInput) return;\n\n return {\n build: {\n rollupOptions: {\n input: VIRTUAL_BUILD_ENTRY_ID,\n },\n },\n };\n },\n\n resolveId(id) {\n if (id === VIRTUAL_BUILD_ENTRY_ID) return id;\n return null;\n },\n\n load(id) {\n if (id === VIRTUAL_BUILD_ENTRY_ID) {\n return 'export default {};';\n }\n return null;\n },\n\n configResolved(resolved) {\n root = resolved.root;\n\n if (!hasWarnedESM) {\n warnIfNotESM(root);\n hasWarnedESM = true;\n }\n },\n\n async buildStart() {\n htmlAssetRefs.clear();\n\n const { entries, modulesByEntry, pages } = await buildPagesPipeline();\n\n for (const entry of entries) {\n this.addWatchFile(entry.entryPath);\n }\n\n const htmlByPageKey = new Map<string, { html: string; pageDir?: string }>();\n\n for (const page of pages) {\n const mod = modulesByEntry.get(page.entryPath);\n\n if (!mod) {\n throw new Error(\n `[${PLUGIN_NAME}] Missing module for page entry: ${page.entryPath}`,\n );\n }\n\n const html = await renderPage(page, mod, false);\n\n htmlByPageKey.set(page.entryPath, {\n html,\n pageDir: path.dirname(page.absolutePath),\n });\n }\n\n htmlAssetRefs = await collectHtmlAssetRefs({\n ctx: this,\n root,\n pagesDir,\n htmlByPageKey,\n });\n\n logDebug(\n options.debug,\n 'collected html assets',\n [...htmlAssetRefs.values()].map((ref) => ({\n kind: ref.kind,\n originalUrl: ref.originalUrl,\n absolutePath: ref.absolutePath,\n })),\n );\n },\n\n configureServer(_server) {\n server = _server;\n\n installDevServer({\n server,\n getPages: async () => {\n if (devPages.length > 0) return devPages;\n return loadDevPages();\n },\n getEntries: async () => discoverEntryPages(root, options),\n });\n\n loadDevPages().catch((error) => {\n server?.config.logger.error(\n `[${PLUGIN_NAME}] loadDevPages failed: ${\n error instanceof Error ? error.stack ?? error.message : String(error)\n }`,\n );\n });\n },\n\n async handleHotUpdate(ctx) {\n if (!server) return;\n\n logDebug(options.debug, 'file changed', ctx.file);\n\n await loadDevPages();\n return undefined;\n },\n\n async generateBundle(_, bundle) {\n try {\n const { modulesByEntry, pages } = await buildPagesPipeline();\n\n const assetReplacements = buildHtmlAssetReplacementMap({\n ctx: this,\n refs: htmlAssetRefs,\n bundle,\n });\n\n logDebug(\n options.debug,\n 'asset replacements',\n [...assetReplacements.entries()],\n );\n\n logDebug(\n options.debug,\n 'emitting pages',\n pages.map((p) => p.fileName),\n );\n\n const limit = pLimit(options.renderConcurrency ?? 8);\n const batchSize =\n options.renderBatchSize ??\n Math.max(options.renderConcurrency ?? 8, 32);\n\n // ---------------------------\n // 1. Render all pages\n // ---------------------------\n for (const batch of chunkArray(pages, batchSize)) {\n await Promise.all(\n batch.map((page) =>\n limit(async () => {\n const mod = modulesByEntry.get(page.entryPath);\n\n if (!mod) {\n throw new Error(\n `[${PLUGIN_NAME}] Missing module for page entry: ${page.entryPath}`,\n );\n }\n\n let html = await renderPage(page, mod, false);\n html = rewriteHtmlAssetUrls(html, assetReplacements);\n\n this.emitFile({\n type: 'asset',\n fileName: options.mapOutputPath?.(page) ?? page.fileName,\n source: html,\n });\n }),\n ),\n );\n }\n\n // ---------------------------\n // 2. 404.html\n // ---------------------------\n const notFoundPage = pages.find((p) => p.routePath === '/404');\n\n if (notFoundPage) {\n const mod = modulesByEntry.get(notFoundPage.entryPath);\n\n if (!mod) {\n throw new Error(\n `[${PLUGIN_NAME}] Missing module for 404 page entry: ${notFoundPage.entryPath}`,\n );\n }\n\n let html = await renderPage(notFoundPage, mod, false);\n html = rewriteHtmlAssetUrls(html, assetReplacements);\n\n this.emitFile({\n type: 'asset',\n fileName: '404.html',\n source: html,\n });\n\n logDebug(options.debug, 'generated 404.html from user page');\n } else {\n const default404 = `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>404 - Page Not Found</title>\n <style>\n :root {\n color-scheme: light dark;\n }\n body {\n margin: 0;\n font-family: system-ui, sans-serif;\n min-height: 100vh;\n display: grid;\n place-items: center;\n padding: 2rem;\n }\n main {\n max-width: 40rem;\n text-align: center;\n }\n h1 {\n font-size: 3rem;\n margin: 0 0 1rem;\n }\n p {\n margin: 0.5rem 0;\n line-height: 1.5;\n }\n a {\n color: inherit;\n }\n </style>\n </head>\n <body>\n <main>\n <h1>404</h1>\n <p>Page not found.</p>\n <p><a href=\"/\">Go back home</a></p>\n </main>\n </body>\n</html>\n`;\n\n this.emitFile({\n type: 'asset',\n fileName: '404.html',\n source: default404,\n });\n\n logDebug(options.debug, 'generated default 404.html');\n }\n\n // ---------------------------\n // 3. Sitemap\n // ---------------------------\n const sitemapBase = options.site ?? '';\n\n const sitemapRoutes = [...new Set(pages.map((p) => p.routePath))].filter(\n (route) => !route.includes(':') && !route.includes('*'),\n );\n\n if (sitemapBase && sitemapRoutes.length > 0) {\n const sitemap = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\\n${sitemapRoutes\n .map((route) => ` <url><loc>${sitemapBase}${route}</loc></url>`)\n .join('\\n')}\\n</urlset>\\n`;\n\n this.emitFile({\n type: 'asset',\n fileName: 'sitemap.xml',\n source: sitemap,\n });\n\n logDebug(options.debug, 'generated sitemap.xml');\n }\n\n // ---------------------------\n // 4. RSS\n // ---------------------------\n if (options.rss?.site) {\n const routePrefix = options.rss.routePrefix ?? '/blog';\n\n const rssItems = pages\n .filter((page) => page.routePath.startsWith(routePrefix))\n .map((page) => {\n const url = `${options.rss!.site}${page.routePath}`;\n\n return ` <item>\\n <title>${page.routePath}</title>\\n <link>${url}</link>\\n <guid>${url}</guid>\\n </item>`;\n })\n .join('\\n');\n\n const rss = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<rss version=\"2.0\">\\n<channel>\\n <title>${options.rss.title ?? PLUGIN_NAME}</title>\\n <link>${options.rss.site}</link>\\n <description>${options.rss.description ?? 'RSS feed'}</description>\\n${rssItems}\\n</channel>\\n</rss>\\n`;\n\n this.emitFile({\n type: 'asset',\n fileName: 'rss.xml',\n source: rss,\n });\n\n logDebug(options.debug, 'generated rss.xml');\n }\n\n // ---------------------------\n // 5. Remove virtual entry chunk\n // ---------------------------\n for (const [fileName, output] of Object.entries(bundle)) {\n if (\n output.type === 'chunk' &&\n output.facadeModuleId === VIRTUAL_BUILD_ENTRY_ID\n ) {\n delete bundle[fileName];\n }\n }\n } finally {\n await closePageModuleLoader();\n }\n },\n };\n}\n\nexport default htPages;","import path from 'node:path';\nimport { normalizeFsPath, toPosix } from './path-utils';\nimport { getParamNames, isDynamicPage, toRoutePattern } from './route-utils';\nimport type { HtPageInfo, HtPagesPluginOptions } from './types';\nimport { PLUGIN_NAME } from './constants';\n\nfunction buildDefaultIncludeGlobs(\n pagesDir: string,\n pageExtensions: string[],\n): string[] {\n return pageExtensions.map((ext) => {\n const cleanExt = ext.startsWith('.') ? ext.slice(1) : ext;\n return `${pagesDir}/**/*.${cleanExt}`;\n });\n}\n\nexport async function discoverEntryPages(\n root: string,\n options: HtPagesPluginOptions,\n): Promise<HtPageInfo[]> {\n const fgModule = await import('fast-glob');\n const fg = (fgModule.default ?? fgModule) as typeof import('fast-glob');\n\n const pagesDir = options.pagesDir ?? 'src';\n const pageExtensions = options.pageExtensions?.length\n ? options.pageExtensions\n : ['.ht.js', '.html.js', '.ht.ts', '.html.ts'];\n\n const include = Array.isArray(options.include)\n ? options.include\n : options.include\n ? [options.include]\n : buildDefaultIncludeGlobs(pagesDir, pageExtensions);\n\n const exclude = Array.isArray(options.exclude)\n ? options.exclude\n : options.exclude\n ? [options.exclude]\n : [];\n\n const pagesRoot = normalizeFsPath(path.join(root, pagesDir));\n\n const files = await fg.glob(include, {\n cwd: root,\n ignore: exclude,\n absolute: true,\n });\n\n return files\n .sort()\n .map((absolutePath) => {\n const entryPath = normalizeFsPath(absolutePath);\n const relativePath = toPosix(path.relative(root, entryPath));\n const relativeFromPagesDir = toPosix(path.relative(pagesRoot, entryPath));\n\n if (\n relativeFromPagesDir.startsWith('../') ||\n relativeFromPagesDir === '..'\n ) {\n throw new Error(\n `[${PLUGIN_NAME}] Page is outside pagesDir: ${entryPath} (pagesDir: ${pagesDir})`,\n );\n }\n\n const dynamic = isDynamicPage(relativeFromPagesDir);\n const routePattern = toRoutePattern(relativeFromPagesDir, pageExtensions);\n\n return {\n id: entryPath,\n entryPath,\n absolutePath: entryPath,\n relativePath,\n routePattern,\n routePath: routePattern,\n fileName: '',\n dynamic,\n paramNames: getParamNames(relativeFromPagesDir),\n params: {},\n } satisfies HtPageInfo;\n });\n}","import path from 'node:path';\n\nexport function toPosix(value: string): string {\n return value.replace(/\\\\/g, '/');\n}\n\nexport function normalizeFsPath(value: string): string {\n return path.normalize(value);\n}\n\nexport function normalizeRoutePath(value: string): string {\n const normalized = toPosix(value).replace(/\\/+/g, '/');\n if (!normalized || normalized === '/') return '/';\n return normalized.startsWith('/') ? normalized : `/${normalized}`;\n}\n\nexport function stripPageSuffix(\n filePath: string,\n extensions: string[],\n): string {\n const normalized = toPosix(filePath);\n\n const match = [...extensions]\n .sort((a, b) => b.length - a.length)\n .find((ext) => normalized.endsWith(ext));\n\n if (!match) return normalized;\n\n return normalized.slice(0, -match.length);\n}","import { normalizeRoutePath, stripPageSuffix, toPosix } from './path-utils';\nimport type { HtPageInfo, StaticParamRecord } from './types';\n\nconst DYNAMIC_SEGMENT_RE = /\\[([A-Za-z0-9_]+)\\]/g;\nconst CATCH_ALL_SEGMENT_RE = /\\[\\.\\.\\.([A-Za-z0-9_]+)\\]/g;\nconst OPTIONAL_CATCH_ALL_SEGMENT_RE = /\\[\\.\\.\\.([A-Za-z0-9_]+)\\]\\?/g;\nconst ANY_PARAM_RE = /\\[(?:\\.\\.\\.)?([A-Za-z0-9_]+)\\]\\??/g;\nconst ROUTE_GROUP_RE = /(^|\\/)\\(([^)]+)\\)(?=\\/|$)/g;\n\nexport function getParamNames(relativeFromPagesDir: string): string[] {\n return [...relativeFromPagesDir.matchAll(ANY_PARAM_RE)].map((m) => m[1]);\n}\n\nexport function isDynamicPage(relativeFromPagesDir: string): boolean {\n return /\\[(?:\\.\\.\\.)?[A-Za-z0-9_]+\\]\\??/.test(relativeFromPagesDir);\n}\n\nexport function toRoutePattern(\n relativeFromPagesDir: string,\n extensions: string[],\n): string {\n const noExt = stripPageSuffix(toPosix(relativeFromPagesDir), extensions);\n\n const withoutGroups = noExt.replace(ROUTE_GROUP_RE, '$1');\n const withoutIndex = withoutGroups.replace(/\\/index$/i, '').replace(/^index$/i, '');\n\n const raw = withoutIndex\n .replace(OPTIONAL_CATCH_ALL_SEGMENT_RE, '*?:$1')\n .replace(CATCH_ALL_SEGMENT_RE, '*:$1')\n .replace(DYNAMIC_SEGMENT_RE, ':$1');\n\n return normalizeRoutePath(raw || '/');\n}\n\nexport function fillParams(\n pattern: string,\n params: Record<string, string>,\n): string {\n const result = pattern\n .replace(/\\*\\?:([A-Za-z0-9_]+)/g, (_, key) => {\n const value = params[key];\n if (value == null || value === '') {\n return '';\n }\n\n return String(value)\n .split('/')\n .map((part) => encodeURIComponent(part))\n .join('/');\n })\n .replace(/\\*:([A-Za-z0-9_]+)/g, (_, key) => {\n if (!(key in params)) {\n throw new Error(`Missing catch-all route param \"${key}\"`);\n }\n\n return String(params[key])\n .split('/')\n .map((part) => encodeURIComponent(part))\n .join('/');\n })\n .replace(/:([A-Za-z0-9_]+)/g, (_, key) => {\n if (!(key in params)) {\n throw new Error(`Missing route param \"${key}\"`);\n }\n\n return encodeURIComponent(params[key]);\n });\n\n return normalizeRoutePath(result || '/');\n}\n\nexport function fileNameFromRoute(\n routePath: string,\n cleanUrls: boolean,\n): string {\n const normalized = normalizeRoutePath(routePath);\n\n if (normalized === '/') return 'index.html';\n\n const base = normalized.slice(1);\n return cleanUrls ? `${base}/index.html` : `${base}.html`;\n}\n\nexport function expandStaticPaths(\n basePage: Omit<HtPageInfo, 'routePath' | 'fileName' | 'params'>,\n rows: StaticParamRecord[],\n cleanUrls: boolean,\n): HtPageInfo[] {\n return rows.map((row) => {\n const params = Object.fromEntries(\n Object.entries(row).map(([k, v]) => [k, String(v)]),\n );\n\n const routePath = fillParams(basePage.routePattern, params);\n\n return {\n ...basePage,\n routePath,\n fileName: fileNameFromRoute(routePath, cleanUrls),\n params,\n };\n });\n}\n\nexport function routeMatch(\n pattern: string,\n urlPath: string,\n): Record<string, string> | null {\n const a = normalizeRoutePath(pattern).split('/').filter(Boolean);\n const b = normalizeRoutePath(urlPath).split('/').filter(Boolean);\n const params: Record<string, string> = {};\n\n for (let i = 0; i < a.length; i++) {\n const patternSeg = a[i];\n const urlSeg = b[i];\n\n if (patternSeg.startsWith('*?:')) {\n params[patternSeg.slice(3)] =\n i < b.length ? b.slice(i).map(decodeURIComponent).join('/') : '';\n return params;\n }\n\n if (patternSeg.startsWith('*:')) {\n const rest = b.slice(i);\n if (rest.length === 0) return null;\n\n params[patternSeg.slice(2)] = rest.map(decodeURIComponent).join('/');\n return params;\n }\n\n if (!urlSeg) return null;\n\n if (patternSeg.startsWith(':')) {\n params[patternSeg.slice(1)] = decodeURIComponent(urlSeg);\n continue;\n }\n\n if (patternSeg !== urlSeg) return null;\n }\n\n return a.length === b.length ? params : null;\n}\n\nexport function compareRoutePriority(a: string, b: string): number {\n const aSegs = normalizeRoutePath(a).split('/').filter(Boolean);\n const bSegs = normalizeRoutePath(b).split('/').filter(Boolean);\n const len = Math.max(aSegs.length, bSegs.length);\n\n for (let i = 0; i < len; i++) {\n const aa = aSegs[i];\n const bb = bSegs[i];\n\n if (aa == null) return 1;\n if (bb == null) return -1;\n\n const aOptionalCatchAll = aa.startsWith('*?:');\n const bOptionalCatchAll = bb.startsWith('*?:');\n if (aOptionalCatchAll !== bOptionalCatchAll) {\n return aOptionalCatchAll ? 1 : -1;\n }\n\n const aCatchAll = aa.startsWith('*:');\n const bCatchAll = bb.startsWith('*:');\n if (aCatchAll !== bCatchAll) {\n return aCatchAll ? 1 : -1;\n }\n\n const aDynamic = aa.startsWith(':');\n const bDynamic = bb.startsWith(':');\n if (aDynamic !== bDynamic) {\n return aDynamic ? 1 : -1;\n }\n }\n\n return bSegs.length - aSegs.length;\n}","export const PLUGIN_NAME = 'vite-plugin-html-pages';\nexport const VIRTUAL_BUILD_ENTRY_ID = `\\0${PLUGIN_NAME}:build-entry`;\nexport const VIRTUAL_MANIFEST_ID = `\\0virtual:${PLUGIN_NAME}-manifest`;\nexport const CACHE_DIR_NAME = `node_modules/.cache/${PLUGIN_NAME}`;","import type { HtPageInfo } from './types';\nimport { PLUGIN_NAME } from './constants';\nexport function invalidHtmlReturn(\n page: HtPageInfo,\n value: unknown,\n): Error {\n return new Error(\n `[${PLUGIN_NAME}] Page \"${page.relativePath}\" must resolve to an HTML string, got ${typeof value}`,\n );\n}\n\nexport function missingDefaultExport(page: HtPageInfo): Error {\n return new Error(\n `[${PLUGIN_NAME}] Page \"${page.relativePath}\" does not export a default renderer`,\n );\n}\n\nexport function pageError(page: HtPageInfo, cause: unknown): Error {\n const message = `[${PLUGIN_NAME}] Failed to render \"${page.relativePath}\" at route \"${page.routePath}\"`;\n\n if (cause instanceof Error) {\n const err = new Error(`${message}: ${cause.message}`);\n\n if (cause.stack) {\n err.stack = `${err.stack}\\nCaused by:\\n${cause.stack}`;\n }\n\n return err;\n }\n\n return new Error(`${message}: ${String(cause)}`);\n}","import { invalidHtmlReturn, pageError, missingDefaultExport } from './errors';\nimport type { HtPageInfo, HtPageModule, HtPageRenderContext } from './types';\n\nexport async function renderPage(\n page: HtPageInfo,\n mod: HtPageModule,\n dev = false,\n): Promise<string> {\n const ctx: HtPageRenderContext = {\n page,\n params: page.params,\n dev,\n };\n\n try {\n if (typeof mod.data === 'function') {\n ctx.data = await mod.data(ctx);\n }\n\n const entry = mod.default;\n\n if (entry == null) {\n throw missingDefaultExport(page);\n }\n\n const html = typeof entry === 'function' ? await entry(ctx) : entry;\n\n if (typeof html !== 'string') {\n throw invalidHtmlReturn(page, html);\n }\n\n return html;\n } catch (error) {\n throw pageError(page, error);\n }\n}","import type { ViteDevServer } from 'vite';\nimport { renderPage } from './render-runtime';\nimport { routeMatch } from './route-utils';\nimport type { HtPageInfo, HtPageModule } from './types';\n\nfunction isDynamicOnly(mod: HtPageModule): boolean {\n return mod.dynamic === true || mod.prerender === false;\n}\n\nexport function installDevServer(args: {\n server: ViteDevServer;\n getPages: () => Promise<HtPageInfo[]>;\n getEntries: () => Promise<HtPageInfo[]>;\n}): void {\n const { server, getPages, getEntries } = args;\n\n server.middlewares.use(async (req, res, next) => {\n try {\n if (!req.url || req.method !== 'GET') return next();\n\n const pathname = req.url.split('?')[0];\n\n const pages = await getPages();\n const staticPage = pages.find((p) => p.routePath === pathname);\n\n if (staticPage) {\n const mod = (await server.ssrLoadModule(\n `/${staticPage.relativePath}`,\n )) as HtPageModule;\n\n const html = await renderPage(staticPage, mod, true);\n\n res.statusCode = 200;\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.end(html);\n return;\n }\n\n const entries = await getEntries();\n\n for (const entry of entries) {\n const mod = (await server.ssrLoadModule(\n `/${entry.relativePath}`,\n )) as HtPageModule;\n\n if (!isDynamicOnly(mod)) continue;\n\n const params = routeMatch(entry.routePattern, pathname);\n if (!params) continue;\n\n const page: HtPageInfo = {\n ...entry,\n routePath: pathname,\n fileName: '',\n params,\n };\n\n const html = await renderPage(page, mod, true);\n\n res.statusCode = 200;\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.end(html);\n return;\n }\n\n next();\n } catch (error) {\n next(error);\n }\n });\n}","import path from 'node:path';\nimport { createServer, type InlineConfig, type ViteDevServer } from 'vite';\nimport type { HtPageModule } from './types';\n\nexport type PageModuleLoader = (\n entryPath: string,\n relativePath: string,\n) => Promise<HtPageModule>;\n\nlet buildServer: ViteDevServer | null = null;\n\nexport async function createPageModuleLoader(args: {\n mode: 'dev' | 'build';\n root: string;\n server?: ViteDevServer | null;\n}): Promise<PageModuleLoader> {\n const { mode, root, server } = args;\n\n if (mode === 'dev') {\n if (!server) {\n throw new Error('[vite-plugin-html-pages] dev server not available');\n }\n\n return async (_entryPath, relativePath) => {\n const mod = await server.ssrLoadModule(`/${relativePath}`);\n return mod as HtPageModule;\n };\n }\n\n if (!buildServer) {\n const config: InlineConfig = {\n root,\n configFile: false,\n logLevel: 'error',\n appType: 'custom',\n server: {\n middlewareMode: true,\n },\n };\n\n buildServer = await createServer(config);\n }\n\n return async (entryPath) => {\n const relativePath =\n '/' + path.relative(root, entryPath).replace(/\\\\/g, '/');\n\n const mod = await buildServer!.ssrLoadModule(relativePath);\n return mod as HtPageModule;\n };\n}\n\nexport async function closePageModuleLoader(): Promise<void> {\n if (buildServer) {\n await buildServer.close();\n buildServer = null;\n }\n}","import {\n compareRoutePriority,\n expandStaticPaths,\n fileNameFromRoute,\n} from './route-utils';\nimport type { HtPageInfo, HtPageModule, StaticParamRecord } from './types';\nimport { PLUGIN_NAME } from './constants';\nexport async function buildPageIndex(args: {\n entries: HtPageInfo[];\n modulesByEntry: Map<string, HtPageModule>;\n cleanUrls: boolean;\n}): Promise<HtPageInfo[]> {\n const { entries, modulesByEntry, cleanUrls } = args;\n const pages: HtPageInfo[] = [];\n\n for (const entry of entries) {\n const mod = modulesByEntry.get(entry.entryPath) ?? {};\n\n if (entry.dynamic) {\n const rows =\n (mod.generateStaticParams\n ? await mod.generateStaticParams()\n : []) ?? [];\n\n pages.push(\n ...expandStaticPaths(\n {\n id: entry.id,\n entryPath: entry.entryPath,\n absolutePath: entry.absolutePath,\n relativePath: entry.relativePath,\n routePattern: entry.routePattern,\n dynamic: entry.dynamic,\n paramNames: entry.paramNames,\n } as Omit<HtPageInfo, 'routePath' | 'fileName' | 'params'>,\n Array.isArray(rows) ? rows : [],\n cleanUrls,\n ),\n );\n } else {\n pages.push({\n ...entry,\n routePath: entry.routePattern,\n fileName: fileNameFromRoute(entry.routePattern, cleanUrls),\n params: {},\n });\n }\n }\n\n pages.sort((a, b) => compareRoutePriority(a.routePattern, b.routePattern));\n\n const seenRoutes = new Map<string, HtPageInfo>();\n\n for (const page of pages) {\n const existing = seenRoutes.get(page.routePath);\n\n if (existing) {\n throw new Error(\n `[${PLUGIN_NAME}] Duplicate route generated: \"${page.routePath}\" from \"${existing.relativePath}\" and \"${page.relativePath}\"`,\n );\n }\n\n seenRoutes.set(page.routePath, page);\n }\n\n return pages;\n}","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { OutputAsset, OutputBundle, OutputChunk, PluginContext } from 'rollup';\n\nexport type HtmlAssetKind = 'css' | 'js';\n\nexport interface HtmlAssetRef {\n kind: HtmlAssetKind;\n originalUrl: string;\n absolutePath: string;\n refId: string;\n}\n\nexport interface ExtractedHtmlAsset {\n kind: HtmlAssetKind;\n url: string;\n}\n\nconst EXTERNAL_URL_RE = /^(?:[a-z]+:)?\\/\\//i;\n\nexport function isLocalAssetUrl(url: string): boolean {\n return (\n !!url &&\n !url.startsWith('data:') &&\n !url.startsWith('mailto:') &&\n !url.startsWith('tel:') &&\n !url.startsWith('#') &&\n !EXTERNAL_URL_RE.test(url)\n );\n}\n\nexport function stripQueryAndHash(url: string): string {\n return url.split('#')[0].split('?')[0];\n}\n\nexport function extractHtmlAssets(html: string): ExtractedHtmlAsset[] {\n const assets: ExtractedHtmlAsset[] = [];\n\n for (const match of html.matchAll(\n /<link\\b[^>]*\\brel=[\"']stylesheet[\"'][^>]*\\bhref=[\"']([^\"']+)[\"'][^>]*>/gi,\n )) {\n assets.push({ kind: 'css', url: match[1] });\n }\n\n for (const match of html.matchAll(\n /<script\\b[^>]*\\bsrc=[\"']([^\"']+)[\"'][^>]*>/gi,\n )) {\n assets.push({ kind: 'js', url: match[1] });\n }\n\n return dedupeExtractedAssets(assets);\n}\n\nfunction dedupeExtractedAssets(\n assets: ExtractedHtmlAsset[],\n): ExtractedHtmlAsset[] {\n const seen = new Set<string>();\n const out: ExtractedHtmlAsset[] = [];\n\n for (const asset of assets) {\n const key = `${asset.kind}:${asset.url}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push(asset);\n }\n\n return out;\n}\n\nexport function resolveLocalAssetPath(args: {\n root: string;\n pagesDir: string;\n pageDir?: string;\n url: string;\n}): string | null {\n const { root, pagesDir, pageDir, url } = args;\n\n if (!isLocalAssetUrl(url)) return null;\n\n const cleanUrl = stripQueryAndHash(url);\n\n let abs: string;\n if (cleanUrl.startsWith('/')) {\n abs = path.join(root, pagesDir, cleanUrl.slice(1));\n } else {\n const baseDir = pageDir ?? path.join(root, pagesDir);\n abs = path.resolve(baseDir, cleanUrl);\n }\n\n return fs.existsSync(abs) ? abs : null;\n}\n\nexport function emitHtmlAsset(args: {\n ctx: PluginContext;\n kind: HtmlAssetKind;\n absolutePath: string;\n}): string {\n const { ctx, kind, absolutePath } = args;\n\n if (kind === 'css' || kind === 'js') {\n return ctx.emitFile({\n type: 'chunk',\n id: absolutePath,\n name: path.basename(absolutePath, path.extname(absolutePath)),\n });\n }\n\n throw new Error(`[vite-plugin-html-pages] Unsupported asset kind: ${kind}`);\n}\n\nfunction replaceAllLiteral(\n input: string,\n search: string,\n replacement: string,\n ): string {\n return input.split(search).join(replacement);\n}\n \nexport function rewriteHtmlAssetUrls(\n html: string,\n replacements: Map<string, string>,\n): string {\n let out = html;\n \n for (const [originalUrl, builtUrl] of replacements) {\n out = replaceAllLiteral(\n out,\n `href=\"${originalUrl}\"`,\n `href=\"${builtUrl}\"`,\n );\n out = replaceAllLiteral(\n out,\n `href='${originalUrl}'`,\n `href='${builtUrl}'`,\n );\n out = replaceAllLiteral(\n out,\n `src=\"${originalUrl}\"`,\n `src=\"${builtUrl}\"`,\n );\n out = replaceAllLiteral(\n out,\n `src='${originalUrl}'`,\n `src='${builtUrl}'`,\n );\n }\n \n return out;\n}\n\nexport async function collectHtmlAssetRefs(args: {\n ctx: PluginContext;\n root: string;\n pagesDir: string;\n htmlByPageKey: Map<string, { html: string; pageDir?: string }>;\n}): Promise<Map<string, HtmlAssetRef>> {\n const { ctx, root, pagesDir, htmlByPageKey } = args;\n const refs = new Map<string, HtmlAssetRef>();\n\n for (const { html, pageDir } of htmlByPageKey.values()) {\n const assets = extractHtmlAssets(html);\n\n for (const asset of assets) {\n const abs = resolveLocalAssetPath({\n root,\n pagesDir,\n pageDir,\n url: asset.url,\n });\n\n if (!abs) continue;\n\n const key = `${asset.kind}:${asset.url}`;\n if (refs.has(key)) continue;\n\n const refId = emitHtmlAsset({\n ctx,\n kind: asset.kind,\n absolutePath: abs,\n });\n\n refs.set(key, {\n kind: asset.kind,\n originalUrl: asset.url,\n absolutePath: abs,\n refId,\n });\n }\n }\n\n return refs;\n}\n\nexport function buildHtmlAssetReplacementMap(args: {\n ctx: PluginContext;\n refs: Map<string, HtmlAssetRef>;\n bundle: OutputBundle;\n }): Map<string, string> {\n const { ctx, refs, bundle } = args;\n const replacements = new Map<string, string>();\n \n for (const ref of refs.values()) {\n if (ref.kind === 'js') {\n const fileName = ctx.getFileName(ref.refId);\n replacements.set(ref.originalUrl, `/${fileName}`);\n continue;\n }\n \n if (ref.kind === 'css') {\n const jsEntryFile = ctx.getFileName(ref.refId);\n const jsChunk = bundle[jsEntryFile];\n \n if (\n jsChunk &&\n jsChunk.type === 'chunk' &&\n 'viteMetadata' in jsChunk &&\n jsChunk.viteMetadata?.importedCss &&\n jsChunk.viteMetadata.importedCss.size > 0\n ) {\n const cssFile = [...jsChunk.viteMetadata.importedCss][0];\n replacements.set(ref.originalUrl, `/${cssFile}`);\n continue;\n }\n \n // Fallback: if no extracted CSS asset was found, keep the JS file\n replacements.set(ref.originalUrl, `/${jsEntryFile}`);\n }\n }\n \n return replacements;\n }","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { createHash } from 'node:crypto';\nimport { CACHE_DIR_NAME } from './constants';\n\nexport type FetchCacheMode = 'auto' | 'memory' | 'fs' | 'none';\nexport interface FetchWithCacheOptions {\n maxAge?: number;\n cacheKey?: string;\n forceRefresh?: boolean;\n cache?: FetchCacheMode;\n}\n\ntype CachedResponseRecord = {\n timestamp: number;\n status: number;\n statusText: string;\n headers: [string, string][];\n body: string;\n};\n\nconst memoryCache = new Map<string, CachedResponseRecord>();\n\nfunction createDefaultCacheKey(\n input: RequestInfo | URL,\n init?: RequestInit,\n): string {\n const raw = JSON.stringify({\n url: String(input),\n method: init?.method ?? 'GET',\n headers: init?.headers ?? {},\n body: init?.body ?? null,\n });\n\n return createHash('sha256').update(raw).digest('hex');\n}\n\nfunction getCacheFilePath(cacheKey: string): string {\n return path.join(process.cwd(), CACHE_DIR_NAME, 'fetch', `${cacheKey}.json`);\n}\n\nfunction getEffectiveCacheMode(\n mode: FetchCacheMode | undefined,\n): Exclude<FetchCacheMode, 'auto'> {\n if (mode === 'memory' || mode === 'fs' || mode === 'none') {\n return mode;\n }\n\n return process.env.NODE_ENV === 'production' ? 'fs' : 'memory';\n}\n\nfunction toResponse(cached: CachedResponseRecord): Response {\n return new Response(cached.body, {\n status: cached.status,\n statusText: cached.statusText,\n headers: cached.headers,\n });\n}\n\nfunction isFresh(cached: CachedResponseRecord, maxAgeSeconds: number): boolean {\n const ageSeconds = (Date.now() - cached.timestamp) / 1000;\n return ageSeconds <= maxAgeSeconds;\n}\n\nexport function clearMemoryFetchCache(): void {\n memoryCache.clear();\n}\n\nexport function deleteMemoryFetchCache(cacheKey: string): void {\n memoryCache.delete(cacheKey);\n}\n\nexport async function fetchWithCache(\n input: RequestInfo | URL,\n init?: RequestInit,\n options: FetchWithCacheOptions = {},\n): Promise<Response> {\n const maxAge = options.maxAge ?? 60 * 60;\n const method = (init?.method ?? 'GET').toUpperCase();\n\n if (method !== 'GET' && !options.cacheKey) {\n return fetch(input, init);\n }\n\n const cacheMode = getEffectiveCacheMode(options.cache);\n const cacheKey = options.cacheKey ?? createDefaultCacheKey(input, init);\n\n if (cacheMode === 'none') {\n return fetch(input, init);\n }\n\n if (cacheMode === 'memory' && !options.forceRefresh) {\n const cached = memoryCache.get(cacheKey);\n\n if (cached && isFresh(cached, maxAge)) {\n return toResponse(cached);\n }\n }\n\n const filePath = getCacheFilePath(cacheKey);\n\n if (cacheMode === 'fs') {\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n\n if (!options.forceRefresh) {\n try {\n const raw = await fs.readFile(filePath, 'utf8');\n const cached = JSON.parse(raw) as CachedResponseRecord;\n\n if (isFresh(cached, maxAge)) {\n return toResponse(cached);\n }\n } catch {\n // cache miss or invalid cache; fetch fresh\n }\n }\n }\n\n const res = await fetch(input, init);\n const body = await res.text();\n\n const record: CachedResponseRecord = {\n timestamp: Date.now(),\n status: res.status,\n statusText: res.statusText,\n headers: [...res.headers.entries()],\n body,\n };\n\n if (cacheMode === 'memory') {\n memoryCache.set(cacheKey, record);\n } else if (cacheMode === 'fs') {\n await fs.writeFile(filePath, JSON.stringify(record), 'utf8');\n }\n\n return new Response(body, {\n status: res.status,\n statusText: res.statusText,\n headers: res.headers,\n });\n}\n"],"mappings":"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","names":["path","path","path","path","fs","path","fs","path","path","fs","path"]}
1
+ {"version":3,"sources":["../src/plugin.ts","../src/discover.ts","../src/path-utils.ts","../src/route-utils.ts","../src/constants.ts","../src/dev-server.ts","../src/errors.ts","../src/render-runtime.ts","../src/module-loader.ts","../src/page-index.ts","../src/assets.ts","../src/fetch-cache.ts"],"sourcesContent":["import pLimit from 'p-limit';\nimport type { Plugin, ViteDevServer } from 'vite';\n\nimport { discoverEntryPages } from './discover';\nimport { installDevServer } from './dev-server';\nimport { createPageModuleLoader, closePageModuleLoader } from './module-loader';\nimport { buildPageIndex } from './page-index';\nimport { renderPage } from './render-runtime';\nimport {\n buildHtmlAssetReplacementMap,\n collectHtmlAssetRefs,\n rewriteHtmlAssetUrls,\n} from './assets';\n\nimport type { HtPageInfo, HtPageModule, HtPagesPluginOptions } from './types';\nimport type { HtmlAssetRef } from './assets';\nimport { PLUGIN_NAME, VIRTUAL_BUILD_ENTRY_ID } from './constants';\n\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nlet hasWarnedESM = false;\n\nfunction warnIfNotESM(root: string) {\n try {\n const pkgPath = path.join(root, 'package.json');\n\n if (!fs.existsSync(pkgPath)) return;\n\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));\n\n if (pkg.type !== 'module') {\n console.warn(\n `[${PLUGIN_NAME}] ⚠️ It is recommended to add \"type\": \"module\" to your package.json for optimal performance and to avoid Node ESM warnings.`,\n );\n }\n } catch {\n // silent — never break build\n }\n}\n\nfunction chunkArray<T>(items: T[], size: number): T[][] {\n const out: T[][] = [];\n for (let i = 0; i < items.length; i += size) {\n out.push(items.slice(i, i + size));\n }\n return out;\n}\n\nexport function htPages(options: HtPagesPluginOptions = {}): Plugin {\n let root = process.cwd();\n let server: ViteDevServer | null = null;\n let devPages: HtPageInfo[] = [];\n let htmlAssetRefs = new Map<string, HtmlAssetRef>();\n\n const cleanUrls = options.cleanUrls ?? true;\n const pagesDir = options.pagesDir ?? 'src';\n\n function logDebug(enabled: boolean | undefined, ...args: unknown[]) {\n if (!enabled) return;\n console.log(`[${PLUGIN_NAME}]`, ...args);\n }\n\n async function loadDevPages(): Promise<HtPageInfo[]> {\n const entries = await discoverEntryPages(root, options);\n const modulesByEntry = new Map<string, HtPageModule>();\n\n logDebug(\n options.debug,\n 'discovered entries',\n entries.map((e) => e.relativePath),\n );\n\n if (!server) return [];\n\n const loadModule = await createPageModuleLoader({\n mode: 'dev',\n root,\n server,\n });\n\n for (const entry of entries) {\n const mod = await loadModule(entry.entryPath, entry.relativePath);\n modulesByEntry.set(entry.entryPath, mod);\n }\n\n devPages = await buildPageIndex({\n entries,\n modulesByEntry,\n cleanUrls,\n });\n\n logDebug(\n options.debug,\n 'dev pages',\n devPages.map((p) => `${p.routePath} -> ${p.relativePath}`),\n );\n\n return devPages;\n }\n\n async function buildPagesPipeline() {\n const entries = await discoverEntryPages(root, options);\n const modulesByEntry = new Map<string, HtPageModule>();\n\n const loadModule = await createPageModuleLoader({\n mode: 'build',\n root,\n });\n\n for (const entry of entries) {\n const mod = await loadModule(entry.entryPath, entry.relativePath);\n modulesByEntry.set(entry.entryPath, mod);\n }\n\n const pages = await buildPageIndex({\n entries,\n modulesByEntry,\n cleanUrls,\n });\n\n return { entries, modulesByEntry, pages };\n }\n\n return {\n name: PLUGIN_NAME,\n\n config(userConfig, env) {\n if (env.command !== 'build') return;\n\n const hasExplicitInput = userConfig.build?.rollupOptions?.input != null;\n if (hasExplicitInput) return;\n\n return {\n build: {\n rollupOptions: {\n input: VIRTUAL_BUILD_ENTRY_ID,\n },\n },\n };\n },\n\n resolveId(id) {\n if (id === VIRTUAL_BUILD_ENTRY_ID) return id;\n return null;\n },\n\n load(id) {\n if (id === VIRTUAL_BUILD_ENTRY_ID) {\n return 'export default {};';\n }\n return null;\n },\n\n configResolved(resolved) {\n root = resolved.root;\n\n if (!hasWarnedESM) {\n warnIfNotESM(root);\n hasWarnedESM = true;\n }\n },\n\n async buildStart() {\n const entries = await discoverEntryPages(root, options);\n \n for (const entry of entries) {\n this.addWatchFile(entry.entryPath);\n }\n \n // emitFile() is build-only\n if (server) {\n return;\n }\n \n htmlAssetRefs.clear();\n \n const { modulesByEntry, pages } = await buildPagesPipeline();\n \n const htmlByPageKey = new Map<string, { html: string; pageDir?: string }>();\n \n for (const page of pages) {\n const mod = modulesByEntry.get(page.entryPath);\n \n if (!mod) {\n throw new Error(\n `[${PLUGIN_NAME}] Missing module for page entry: ${page.entryPath}`,\n );\n }\n \n const html = await renderPage(page, mod, false);\n \n htmlByPageKey.set(page.entryPath, {\n html,\n pageDir: path.dirname(page.absolutePath),\n });\n }\n \n htmlAssetRefs = await collectHtmlAssetRefs({\n ctx: this,\n root,\n pagesDir,\n htmlByPageKey,\n });\n \n logDebug(\n options.debug,\n 'collected html assets',\n [...htmlAssetRefs.values()].map((ref) => ({\n kind: ref.kind,\n originalUrl: ref.originalUrl,\n absolutePath: ref.absolutePath,\n })),\n );\n },\n\n configureServer(_server) {\n server = _server;\n\n installDevServer({\n server,\n getPages: async () => {\n if (devPages.length > 0) return devPages;\n return loadDevPages();\n },\n getEntries: async () => discoverEntryPages(root, options),\n });\n\n loadDevPages().catch((error) => {\n server?.config.logger.error(\n `[${PLUGIN_NAME}] loadDevPages failed: ${\n error instanceof Error ? error.stack ?? error.message : String(error)\n }`,\n );\n });\n },\n\n async handleHotUpdate(ctx) {\n if (!server) return;\n\n logDebug(options.debug, 'file changed', ctx.file);\n\n await loadDevPages();\n return undefined;\n },\n\n async generateBundle(_, bundle) {\n try {\n const { modulesByEntry, pages } = await buildPagesPipeline();\n\n const assetReplacements = buildHtmlAssetReplacementMap({\n ctx: this,\n refs: htmlAssetRefs,\n bundle,\n });\n\n logDebug(\n options.debug,\n 'asset replacements',\n [...assetReplacements.entries()],\n );\n\n logDebug(\n options.debug,\n 'emitting pages',\n pages.map((p) => p.fileName),\n );\n\n const limit = pLimit(options.renderConcurrency ?? 8);\n const batchSize =\n options.renderBatchSize ??\n Math.max(options.renderConcurrency ?? 8, 32);\n\n // ---------------------------\n // 1. Render all pages\n // ---------------------------\n for (const batch of chunkArray(pages, batchSize)) {\n await Promise.all(\n batch.map((page) =>\n limit(async () => {\n const mod = modulesByEntry.get(page.entryPath);\n\n if (!mod) {\n throw new Error(\n `[${PLUGIN_NAME}] Missing module for page entry: ${page.entryPath}`,\n );\n }\n\n let html = await renderPage(page, mod, false);\n html = rewriteHtmlAssetUrls(html, assetReplacements);\n\n this.emitFile({\n type: 'asset',\n fileName: options.mapOutputPath?.(page) ?? page.fileName,\n source: html,\n });\n }),\n ),\n );\n }\n\n // ---------------------------\n // 2. 404.html\n // ---------------------------\n const notFoundPage = pages.find((p) => p.routePath === '/404');\n\n if (notFoundPage) {\n const mod = modulesByEntry.get(notFoundPage.entryPath);\n\n if (!mod) {\n throw new Error(\n `[${PLUGIN_NAME}] Missing module for 404 page entry: ${notFoundPage.entryPath}`,\n );\n }\n\n let html = await renderPage(notFoundPage, mod, false);\n html = rewriteHtmlAssetUrls(html, assetReplacements);\n\n this.emitFile({\n type: 'asset',\n fileName: '404.html',\n source: html,\n });\n\n logDebug(options.debug, 'generated 404.html from user page');\n } else {\n const default404 = `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>404 - Page Not Found</title>\n <style>\n :root {\n color-scheme: light dark;\n }\n body {\n margin: 0;\n font-family: system-ui, sans-serif;\n min-height: 100vh;\n display: grid;\n place-items: center;\n padding: 2rem;\n }\n main {\n max-width: 40rem;\n text-align: center;\n }\n h1 {\n font-size: 3rem;\n margin: 0 0 1rem;\n }\n p {\n margin: 0.5rem 0;\n line-height: 1.5;\n }\n a {\n color: inherit;\n }\n </style>\n </head>\n <body>\n <main>\n <h1>404</h1>\n <p>Page not found.</p>\n <p><a href=\"/\">Go back home</a></p>\n </main>\n </body>\n</html>\n`;\n\n this.emitFile({\n type: 'asset',\n fileName: '404.html',\n source: default404,\n });\n\n logDebug(options.debug, 'generated default 404.html');\n }\n\n // ---------------------------\n // 3. Sitemap\n // ---------------------------\n const sitemapBase = options.site ?? '';\n\n const sitemapRoutes = [...new Set(pages.map((p) => p.routePath))].filter(\n (route) => !route.includes(':') && !route.includes('*'),\n );\n\n if (sitemapBase && sitemapRoutes.length > 0) {\n const sitemap = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\\n${sitemapRoutes\n .map((route) => ` <url><loc>${sitemapBase}${route}</loc></url>`)\n .join('\\n')}\\n</urlset>\\n`;\n\n this.emitFile({\n type: 'asset',\n fileName: 'sitemap.xml',\n source: sitemap,\n });\n\n logDebug(options.debug, 'generated sitemap.xml');\n }\n\n // ---------------------------\n // 4. RSS\n // ---------------------------\n if (options.rss?.site) {\n const routePrefix = options.rss.routePrefix ?? '/blog';\n\n const rssItems = pages\n .filter((page) => page.routePath.startsWith(routePrefix))\n .map((page) => {\n const url = `${options.rss!.site}${page.routePath}`;\n\n return ` <item>\\n <title>${page.routePath}</title>\\n <link>${url}</link>\\n <guid>${url}</guid>\\n </item>`;\n })\n .join('\\n');\n\n const rss = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<rss version=\"2.0\">\\n<channel>\\n <title>${options.rss.title ?? PLUGIN_NAME}</title>\\n <link>${options.rss.site}</link>\\n <description>${options.rss.description ?? 'RSS feed'}</description>\\n${rssItems}\\n</channel>\\n</rss>\\n`;\n\n this.emitFile({\n type: 'asset',\n fileName: 'rss.xml',\n source: rss,\n });\n\n logDebug(options.debug, 'generated rss.xml');\n }\n\n // ---------------------------\n // 5. Remove virtual entry chunk\n // ---------------------------\n for (const [fileName, output] of Object.entries(bundle)) {\n if (\n output.type === 'chunk' &&\n output.facadeModuleId === VIRTUAL_BUILD_ENTRY_ID\n ) {\n delete bundle[fileName];\n }\n }\n } finally {\n await closePageModuleLoader();\n }\n },\n };\n}\n\nexport default htPages;","import path from 'node:path';\nimport { normalizeFsPath, toPosix } from './path-utils';\nimport { getParamNames, isDynamicPage, toRoutePattern } from './route-utils';\nimport type { HtPageInfo, HtPagesPluginOptions } from './types';\nimport { PLUGIN_NAME } from './constants';\n\nfunction buildDefaultIncludeGlobs(\n pagesDir: string,\n pageExtensions: string[],\n): string[] {\n return pageExtensions.map((ext) => {\n const cleanExt = ext.startsWith('.') ? ext.slice(1) : ext;\n return `${pagesDir}/**/*.${cleanExt}`;\n });\n}\n\nexport async function discoverEntryPages(\n root: string,\n options: HtPagesPluginOptions,\n): Promise<HtPageInfo[]> {\n const fgModule = await import('fast-glob');\n const fg = (fgModule.default ?? fgModule) as typeof import('fast-glob');\n\n const pagesDir = options.pagesDir ?? 'src';\n const pageExtensions = options.pageExtensions?.length\n ? options.pageExtensions\n : ['.ht.js', '.html.js', '.ht.ts', '.html.ts'];\n\n const include = Array.isArray(options.include)\n ? options.include\n : options.include\n ? [options.include]\n : buildDefaultIncludeGlobs(pagesDir, pageExtensions);\n\n const exclude = Array.isArray(options.exclude)\n ? options.exclude\n : options.exclude\n ? [options.exclude]\n : [];\n\n const pagesRoot = normalizeFsPath(path.join(root, pagesDir));\n\n const files = await fg.glob(include, {\n cwd: root,\n ignore: exclude,\n absolute: true,\n });\n\n return files\n .sort()\n .map((absolutePath) => {\n const entryPath = normalizeFsPath(absolutePath);\n const relativePath = toPosix(path.relative(root, entryPath));\n const relativeFromPagesDir = toPosix(path.relative(pagesRoot, entryPath));\n\n if (\n relativeFromPagesDir.startsWith('../') ||\n relativeFromPagesDir === '..'\n ) {\n throw new Error(\n `[${PLUGIN_NAME}] Page is outside pagesDir: ${entryPath} (pagesDir: ${pagesDir})`,\n );\n }\n\n const dynamic = isDynamicPage(relativeFromPagesDir);\n const routePattern = toRoutePattern(relativeFromPagesDir, pageExtensions);\n\n return {\n id: entryPath,\n entryPath,\n absolutePath: entryPath,\n relativePath,\n routePattern,\n routePath: routePattern,\n fileName: '',\n dynamic,\n paramNames: getParamNames(relativeFromPagesDir),\n params: {},\n } satisfies HtPageInfo;\n });\n}","import path from 'node:path';\n\nexport function toPosix(value: string): string {\n return value.replace(/\\\\/g, '/');\n}\n\nexport function normalizeFsPath(value: string): string {\n return path.normalize(value);\n}\n\nexport function normalizeRoutePath(value: string): string {\n const normalized = toPosix(value).replace(/\\/+/g, '/');\n if (!normalized || normalized === '/') return '/';\n return normalized.startsWith('/') ? normalized : `/${normalized}`;\n}\n\nexport function stripPageSuffix(\n filePath: string,\n extensions: string[],\n): string {\n const normalized = toPosix(filePath);\n\n const match = [...extensions]\n .sort((a, b) => b.length - a.length)\n .find((ext) => normalized.endsWith(ext));\n\n if (!match) return normalized;\n\n return normalized.slice(0, -match.length);\n}","import { normalizeRoutePath, stripPageSuffix, toPosix } from './path-utils';\nimport type { HtPageInfo, StaticParamRecord } from './types';\n\nconst DYNAMIC_SEGMENT_RE = /\\[([A-Za-z0-9_]+)\\]/g;\nconst CATCH_ALL_SEGMENT_RE = /\\[\\.\\.\\.([A-Za-z0-9_]+)\\]/g;\nconst OPTIONAL_CATCH_ALL_SEGMENT_RE = /\\[\\.\\.\\.([A-Za-z0-9_]+)\\]\\?/g;\nconst ANY_PARAM_RE = /\\[(?:\\.\\.\\.)?([A-Za-z0-9_]+)\\]\\??/g;\nconst ROUTE_GROUP_RE = /(^|\\/)\\(([^)]+)\\)(?=\\/|$)/g;\n\nexport function getParamNames(relativeFromPagesDir: string): string[] {\n return [...relativeFromPagesDir.matchAll(ANY_PARAM_RE)].map((m) => m[1]);\n}\n\nexport function isDynamicPage(relativeFromPagesDir: string): boolean {\n return /\\[(?:\\.\\.\\.)?[A-Za-z0-9_]+\\]\\??/.test(relativeFromPagesDir);\n}\n\nexport function toRoutePattern(\n relativeFromPagesDir: string,\n extensions: string[],\n): string {\n const noExt = stripPageSuffix(toPosix(relativeFromPagesDir), extensions);\n\n const withoutGroups = noExt.replace(ROUTE_GROUP_RE, '$1');\n const withoutIndex = withoutGroups.replace(/\\/index$/i, '').replace(/^index$/i, '');\n\n const raw = withoutIndex\n .replace(OPTIONAL_CATCH_ALL_SEGMENT_RE, '*?:$1')\n .replace(CATCH_ALL_SEGMENT_RE, '*:$1')\n .replace(DYNAMIC_SEGMENT_RE, ':$1');\n\n return normalizeRoutePath(raw || '/');\n}\n\nexport function fillParams(\n pattern: string,\n params: Record<string, string>,\n): string {\n const result = pattern\n .replace(/\\*\\?:([A-Za-z0-9_]+)/g, (_, key) => {\n const value = params[key];\n if (value == null || value === '') {\n return '';\n }\n\n return String(value)\n .split('/')\n .map((part) => encodeURIComponent(part))\n .join('/');\n })\n .replace(/\\*:([A-Za-z0-9_]+)/g, (_, key) => {\n if (!(key in params)) {\n throw new Error(`Missing catch-all route param \"${key}\"`);\n }\n\n return String(params[key])\n .split('/')\n .map((part) => encodeURIComponent(part))\n .join('/');\n })\n .replace(/:([A-Za-z0-9_]+)/g, (_, key) => {\n if (!(key in params)) {\n throw new Error(`Missing route param \"${key}\"`);\n }\n\n return encodeURIComponent(params[key]);\n });\n\n return normalizeRoutePath(result || '/');\n}\n\nexport function fileNameFromRoute(\n routePath: string,\n cleanUrls: boolean,\n): string {\n const normalized = normalizeRoutePath(routePath);\n\n if (normalized === '/') return 'index.html';\n\n const base = normalized.slice(1);\n return cleanUrls ? `${base}/index.html` : `${base}.html`;\n}\n\nexport function expandStaticPaths(\n basePage: Omit<HtPageInfo, 'routePath' | 'fileName' | 'params'>,\n rows: StaticParamRecord[],\n cleanUrls: boolean,\n): HtPageInfo[] {\n return rows.map((row) => {\n const params = Object.fromEntries(\n Object.entries(row).map(([k, v]) => [k, String(v)]),\n );\n\n const routePath = fillParams(basePage.routePattern, params);\n\n return {\n ...basePage,\n routePath,\n fileName: fileNameFromRoute(routePath, cleanUrls),\n params,\n };\n });\n}\n\nexport function routeMatch(\n pattern: string,\n urlPath: string,\n): Record<string, string> | null {\n const a = normalizeRoutePath(pattern).split('/').filter(Boolean);\n const b = normalizeRoutePath(urlPath).split('/').filter(Boolean);\n const params: Record<string, string> = {};\n\n for (let i = 0; i < a.length; i++) {\n const patternSeg = a[i];\n const urlSeg = b[i];\n\n if (patternSeg.startsWith('*?:')) {\n params[patternSeg.slice(3)] =\n i < b.length ? b.slice(i).map(decodeURIComponent).join('/') : '';\n return params;\n }\n\n if (patternSeg.startsWith('*:')) {\n const rest = b.slice(i);\n if (rest.length === 0) return null;\n\n params[patternSeg.slice(2)] = rest.map(decodeURIComponent).join('/');\n return params;\n }\n\n if (!urlSeg) return null;\n\n if (patternSeg.startsWith(':')) {\n params[patternSeg.slice(1)] = decodeURIComponent(urlSeg);\n continue;\n }\n\n if (patternSeg !== urlSeg) return null;\n }\n\n return a.length === b.length ? params : null;\n}\n\nexport function compareRoutePriority(a: string, b: string): number {\n const aSegs = normalizeRoutePath(a).split('/').filter(Boolean);\n const bSegs = normalizeRoutePath(b).split('/').filter(Boolean);\n const len = Math.max(aSegs.length, bSegs.length);\n\n for (let i = 0; i < len; i++) {\n const aa = aSegs[i];\n const bb = bSegs[i];\n\n if (aa == null) return 1;\n if (bb == null) return -1;\n\n const aOptionalCatchAll = aa.startsWith('*?:');\n const bOptionalCatchAll = bb.startsWith('*?:');\n if (aOptionalCatchAll !== bOptionalCatchAll) {\n return aOptionalCatchAll ? 1 : -1;\n }\n\n const aCatchAll = aa.startsWith('*:');\n const bCatchAll = bb.startsWith('*:');\n if (aCatchAll !== bCatchAll) {\n return aCatchAll ? 1 : -1;\n }\n\n const aDynamic = aa.startsWith(':');\n const bDynamic = bb.startsWith(':');\n if (aDynamic !== bDynamic) {\n return aDynamic ? 1 : -1;\n }\n }\n\n return bSegs.length - aSegs.length;\n}","export const PLUGIN_NAME = 'vite-plugin-html-pages';\nexport const VIRTUAL_BUILD_ENTRY_ID = `\\0${PLUGIN_NAME}:build-entry`;\nexport const VIRTUAL_MANIFEST_ID = `\\0virtual:${PLUGIN_NAME}-manifest`;\nexport const CACHE_DIR_NAME = `node_modules/.cache/${PLUGIN_NAME}`;","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { ViteDevServer } from 'vite';\n\nimport { renderPage } from './render-runtime';\nimport type { HtPageInfo, HtPageModule } from './types';\nimport { PLUGIN_NAME } from './constants';\nimport { createPageModuleLoader } from './module-loader';\n\nfunction isStaticAssetRequest(url: string): boolean {\n return (\n url.endsWith('.css') ||\n url.endsWith('.js') ||\n url.endsWith('.mjs') ||\n url.endsWith('.ts') ||\n url.endsWith('.png') ||\n url.endsWith('.jpg') ||\n url.endsWith('.jpeg') ||\n url.endsWith('.gif') ||\n url.endsWith('.svg') ||\n url.endsWith('.webp') ||\n url.endsWith('.ico') ||\n url.endsWith('.woff') ||\n url.endsWith('.woff2') ||\n url.endsWith('.ttf') ||\n url.endsWith('.otf')\n );\n}\n\nfunction shouldSkipHtmlRouting(url: string): boolean {\n return (\n url.startsWith('/@vite') ||\n url.startsWith('/@fs/') ||\n url.startsWith('/node_modules/') ||\n url.startsWith('/src/') ||\n url === '/favicon.ico' ||\n isStaticAssetRequest(url)\n );\n}\n\nfunction tryRewriteRootAssetToSrc(server: ViteDevServer, url: string): string | null {\n if (!url.startsWith('/')) return null;\n if (!isStaticAssetRequest(url)) return null;\n if (url.startsWith('/src/')) return null;\n\n const root = server.config.root;\n const candidate = path.join(root, 'src', url.slice(1));\n\n if (fs.existsSync(candidate)) {\n return `/src/${url.slice(1)}`;\n }\n\n return null;\n}\n\nfunction shouldUseDynamicRendering(mod: HtPageModule): boolean {\n return mod.dynamic === true || mod.prerender === false;\n}\n\nexport function installDevServer(args: {\n server: ViteDevServer;\n getPages: () => Promise<HtPageInfo[]>;\n getEntries?: () => Promise<HtPageInfo[]>;\n}) {\n const { server, getPages } = args;\n\n server.middlewares.use(async (req, res, next) => {\n try {\n const originalUrl = req.url ?? '/';\n const url = originalUrl.split('?')[0];\n\n const rewrittenAssetUrl = tryRewriteRootAssetToSrc(server, url);\n if (rewrittenAssetUrl) {\n req.url = rewrittenAssetUrl + originalUrl.slice(url.length);\n return next();\n }\n\n if (shouldSkipHtmlRouting(url)) {\n return next();\n }\n\n const pages = await getPages();\n\n const page = pages.find((p) => p.routePath === url);\n\n if (!page) {\n return next();\n }\n\n const loadModule = await createPageModuleLoader({\n mode: 'dev',\n root: server.config.root,\n server,\n });\n\n const mod = await loadModule(page.entryPath, page.relativePath);\n\n if (!mod) {\n return next();\n }\n\n if (!shouldUseDynamicRendering(mod) && page.dynamic) {\n return next();\n }\n\n const html = await renderPage(page, mod, true);\n\n res.statusCode = 200;\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.end(html);\n } catch (error) {\n server.config.logger.error(\n `[${PLUGIN_NAME}] dev server render failed: ${\n error instanceof Error ? error.stack ?? error.message : String(error)\n }`,\n );\n\n next(error as Error);\n }\n });\n}","import type { HtPageInfo } from './types';\nimport { PLUGIN_NAME } from './constants';\nexport function invalidHtmlReturn(\n page: HtPageInfo,\n value: unknown,\n): Error {\n return new Error(\n `[${PLUGIN_NAME}] Page \"${page.relativePath}\" must resolve to an HTML string, got ${typeof value}`,\n );\n}\n\nexport function missingDefaultExport(page: HtPageInfo): Error {\n return new Error(\n `[${PLUGIN_NAME}] Page \"${page.relativePath}\" does not export a default renderer`,\n );\n}\n\nexport function pageError(page: HtPageInfo, cause: unknown): Error {\n const message = `[${PLUGIN_NAME}] Failed to render \"${page.relativePath}\" at route \"${page.routePath}\"`;\n\n if (cause instanceof Error) {\n const err = new Error(`${message}: ${cause.message}`);\n\n if (cause.stack) {\n err.stack = `${err.stack}\\nCaused by:\\n${cause.stack}`;\n }\n\n return err;\n }\n\n return new Error(`${message}: ${String(cause)}`);\n}","import { invalidHtmlReturn, pageError, missingDefaultExport } from './errors';\nimport type { HtPageInfo, HtPageModule, HtPageRenderContext } from './types';\n\nexport async function renderPage(\n page: HtPageInfo,\n mod: HtPageModule,\n dev = false,\n): Promise<string> {\n const ctx: HtPageRenderContext = {\n page,\n params: page.params,\n dev,\n };\n\n try {\n if (typeof mod.data === 'function') {\n ctx.data = await mod.data(ctx);\n }\n\n const entry = mod.default;\n\n if (entry == null) {\n throw missingDefaultExport(page);\n }\n\n const html = typeof entry === 'function' ? await entry(ctx) : entry;\n\n if (typeof html !== 'string') {\n throw invalidHtmlReturn(page, html);\n }\n\n return html;\n } catch (error) {\n throw pageError(page, error);\n }\n}","import path from 'node:path';\nimport { createServer, type InlineConfig, type ViteDevServer } from 'vite';\nimport type { HtPageModule } from './types';\n\nexport type PageModuleLoader = (\n entryPath: string,\n relativePath: string,\n) => Promise<HtPageModule>;\n\nlet buildServer: ViteDevServer | null = null;\n\nexport async function createPageModuleLoader(args: {\n mode: 'dev' | 'build';\n root: string;\n server?: ViteDevServer | null;\n}): Promise<PageModuleLoader> {\n const { mode, root, server } = args;\n\n if (mode === 'dev') {\n if (!server) {\n throw new Error('[vite-plugin-html-pages] dev server not available');\n }\n\n return async (_entryPath, relativePath) => {\n const mod = await server.ssrLoadModule(`/${relativePath}`);\n return mod as HtPageModule;\n };\n }\n\n if (!buildServer) {\n const config: InlineConfig = {\n root,\n configFile: false,\n logLevel: 'error',\n appType: 'custom',\n server: {\n middlewareMode: true,\n },\n };\n\n buildServer = await createServer(config);\n }\n\n return async (entryPath) => {\n const relativePath =\n '/' + path.relative(root, entryPath).replace(/\\\\/g, '/');\n\n const mod = await buildServer!.ssrLoadModule(relativePath);\n return mod as HtPageModule;\n };\n}\n\nexport async function closePageModuleLoader(): Promise<void> {\n if (buildServer) {\n await buildServer.close();\n buildServer = null;\n }\n}","import {\n compareRoutePriority,\n expandStaticPaths,\n fileNameFromRoute,\n} from './route-utils';\nimport type { HtPageInfo, HtPageModule, StaticParamRecord } from './types';\nimport { PLUGIN_NAME } from './constants';\nexport async function buildPageIndex(args: {\n entries: HtPageInfo[];\n modulesByEntry: Map<string, HtPageModule>;\n cleanUrls: boolean;\n}): Promise<HtPageInfo[]> {\n const { entries, modulesByEntry, cleanUrls } = args;\n const pages: HtPageInfo[] = [];\n\n for (const entry of entries) {\n const mod = modulesByEntry.get(entry.entryPath) ?? {};\n\n if (entry.dynamic) {\n const rows =\n (mod.generateStaticParams\n ? await mod.generateStaticParams()\n : []) ?? [];\n\n pages.push(\n ...expandStaticPaths(\n {\n id: entry.id,\n entryPath: entry.entryPath,\n absolutePath: entry.absolutePath,\n relativePath: entry.relativePath,\n routePattern: entry.routePattern,\n dynamic: entry.dynamic,\n paramNames: entry.paramNames,\n } as Omit<HtPageInfo, 'routePath' | 'fileName' | 'params'>,\n Array.isArray(rows) ? rows : [],\n cleanUrls,\n ),\n );\n } else {\n pages.push({\n ...entry,\n routePath: entry.routePattern,\n fileName: fileNameFromRoute(entry.routePattern, cleanUrls),\n params: {},\n });\n }\n }\n\n pages.sort((a, b) => compareRoutePriority(a.routePattern, b.routePattern));\n\n const seenRoutes = new Map<string, HtPageInfo>();\n\n for (const page of pages) {\n const existing = seenRoutes.get(page.routePath);\n\n if (existing) {\n throw new Error(\n `[${PLUGIN_NAME}] Duplicate route generated: \"${page.routePath}\" from \"${existing.relativePath}\" and \"${page.relativePath}\"`,\n );\n }\n\n seenRoutes.set(page.routePath, page);\n }\n\n return pages;\n}","import fs from 'node:fs';\nimport path from 'node:path';\nimport type {\n OutputBundle,\n PluginContext,\n} from 'rollup';\n\nexport type HtmlAssetKind = 'css' | 'js';\n\nexport interface HtmlAssetRef {\n kind: HtmlAssetKind;\n originalUrl: string;\n absolutePath: string;\n refId: string;\n}\n\nexport interface ExtractedHtmlAsset {\n kind: HtmlAssetKind;\n url: string;\n}\n\nconst EXTERNAL_URL_RE = /^(?:[a-z]+:)?\\/\\//i;\n\nexport function isLocalAssetUrl(url: string): boolean {\n return (\n !!url &&\n !url.startsWith('data:') &&\n !url.startsWith('mailto:') &&\n !url.startsWith('tel:') &&\n !url.startsWith('#') &&\n !EXTERNAL_URL_RE.test(url)\n );\n}\n\nexport function stripQueryAndHash(url: string): string {\n return url.split('#')[0].split('?')[0];\n}\n\nexport function extractHtmlAssets(html: string): ExtractedHtmlAsset[] {\n const assets: ExtractedHtmlAsset[] = [];\n\n for (const match of html.matchAll(\n /<link\\b[^>]*\\brel=[\"']stylesheet[\"'][^>]*\\bhref=[\"']([^\"']+)[\"'][^>]*>/gi,\n )) {\n assets.push({ kind: 'css', url: match[1] });\n }\n\n for (const match of html.matchAll(\n /<script\\b[^>]*\\bsrc=[\"']([^\"']+)[\"'][^>]*>/gi,\n )) {\n assets.push({ kind: 'js', url: match[1] });\n }\n\n return dedupeExtractedAssets(assets);\n}\n\nfunction dedupeExtractedAssets(\n assets: ExtractedHtmlAsset[],\n): ExtractedHtmlAsset[] {\n const seen = new Set<string>();\n const out: ExtractedHtmlAsset[] = [];\n\n for (const asset of assets) {\n const key = `${asset.kind}:${asset.url}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push(asset);\n }\n\n return out;\n}\n\nexport function resolveLocalAssetPath(args: {\n root: string;\n pagesDir: string;\n pageDir?: string;\n url: string;\n}): string | null {\n const { root, pagesDir, pageDir, url } = args;\n\n if (!isLocalAssetUrl(url)) return null;\n\n const cleanUrl = stripQueryAndHash(url);\n\n let abs: string;\n\n if (cleanUrl.startsWith('/')) {\n abs = path.join(root, pagesDir, cleanUrl.slice(1));\n } else if (cleanUrl.startsWith(`${pagesDir}/`)) {\n abs = path.join(root, cleanUrl);\n } else {\n const baseDir = pageDir ?? path.join(root, pagesDir);\n abs = path.resolve(baseDir, cleanUrl);\n }\n\n return fs.existsSync(abs) ? abs : null;\n}\n\nexport function emitHtmlAsset(args: {\n ctx: PluginContext;\n kind: HtmlAssetKind;\n absolutePath: string;\n}): string {\n const { ctx, kind, absolutePath } = args;\n\n if (kind === 'css' || kind === 'js') {\n return ctx.emitFile({\n type: 'chunk',\n id: absolutePath,\n name: path.basename(absolutePath, path.extname(absolutePath)),\n });\n }\n\n throw new Error(`[vite-plugin-html-pages] Unsupported asset kind: ${kind}`);\n}\n\nfunction replaceAllLiteral(\n input: string,\n search: string,\n replacement: string,\n): string {\n return input.split(search).join(replacement);\n}\n\nexport function rewriteHtmlAssetUrls(\n html: string,\n replacements: Map<string, string>,\n): string {\n let out = html;\n\n for (const [originalUrl, builtUrl] of replacements) {\n out = replaceAllLiteral(\n out,\n `href=\"${originalUrl}\"`,\n `href=\"${builtUrl}\"`,\n );\n out = replaceAllLiteral(\n out,\n `href='${originalUrl}'`,\n `href='${builtUrl}'`,\n );\n out = replaceAllLiteral(\n out,\n `src=\"${originalUrl}\"`,\n `src=\"${builtUrl}\"`,\n );\n out = replaceAllLiteral(\n out,\n `src='${originalUrl}'`,\n `src='${builtUrl}'`,\n );\n }\n\n return out;\n}\n\nexport async function collectHtmlAssetRefs(args: {\n ctx: PluginContext;\n root: string;\n pagesDir: string;\n htmlByPageKey: Map<string, { html: string; pageDir?: string }>;\n}): Promise<Map<string, HtmlAssetRef>> {\n const { ctx, root, pagesDir, htmlByPageKey } = args;\n const refs = new Map<string, HtmlAssetRef>();\n\n for (const { html, pageDir } of htmlByPageKey.values()) {\n const assets = extractHtmlAssets(html);\n\n for (const asset of assets) {\n const abs = resolveLocalAssetPath({\n root,\n pagesDir,\n pageDir,\n url: asset.url,\n });\n\n if (!abs) continue;\n\n const key = `${asset.kind}:${asset.url}`;\n if (refs.has(key)) continue;\n\n const refId = emitHtmlAsset({\n ctx,\n kind: asset.kind,\n absolutePath: abs,\n });\n\n refs.set(key, {\n kind: asset.kind,\n originalUrl: asset.url,\n absolutePath: abs,\n refId,\n });\n }\n }\n\n return refs;\n}\n\nexport function buildHtmlAssetReplacementMap(args: {\n ctx: PluginContext;\n refs: Map<string, HtmlAssetRef>;\n bundle: OutputBundle;\n}): Map<string, string> {\n const { ctx, refs, bundle } = args;\n const replacements = new Map<string, string>();\n\n for (const ref of refs.values()) {\n if (ref.kind === 'js') {\n const fileName = ctx.getFileName(ref.refId);\n replacements.set(ref.originalUrl, `/${fileName}`);\n continue;\n }\n\n if (ref.kind === 'css') {\n const jsEntryFile = ctx.getFileName(ref.refId);\n const jsChunk = bundle[jsEntryFile];\n\n if (\n jsChunk &&\n jsChunk.type === 'chunk' &&\n 'viteMetadata' in jsChunk &&\n jsChunk.viteMetadata?.importedCss &&\n jsChunk.viteMetadata.importedCss.size > 0\n ) {\n const cssFile = [...jsChunk.viteMetadata.importedCss][0];\n replacements.set(ref.originalUrl, `/${cssFile}`);\n continue;\n }\n\n replacements.set(ref.originalUrl, `/${jsEntryFile}`);\n }\n }\n\n return replacements;\n}","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { createHash } from 'node:crypto';\nimport { CACHE_DIR_NAME } from './constants';\n\nexport type FetchCacheMode = 'auto' | 'memory' | 'fs' | 'none';\nexport interface FetchWithCacheOptions {\n maxAge?: number;\n cacheKey?: string;\n forceRefresh?: boolean;\n cache?: FetchCacheMode;\n}\n\ntype CachedResponseRecord = {\n timestamp: number;\n status: number;\n statusText: string;\n headers: [string, string][];\n body: string;\n};\n\nconst memoryCache = new Map<string, CachedResponseRecord>();\n\nfunction createDefaultCacheKey(\n input: RequestInfo | URL,\n init?: RequestInit,\n): string {\n const raw = JSON.stringify({\n url: String(input),\n method: init?.method ?? 'GET',\n headers: init?.headers ?? {},\n body: init?.body ?? null,\n });\n\n return createHash('sha256').update(raw).digest('hex');\n}\n\nfunction getCacheFilePath(cacheKey: string): string {\n return path.join(process.cwd(), CACHE_DIR_NAME, 'fetch', `${cacheKey}.json`);\n}\n\nfunction getEffectiveCacheMode(\n mode: FetchCacheMode | undefined,\n): Exclude<FetchCacheMode, 'auto'> {\n if (mode === 'memory' || mode === 'fs' || mode === 'none') {\n return mode;\n }\n\n return process.env.NODE_ENV === 'production' ? 'fs' : 'memory';\n}\n\nfunction toResponse(cached: CachedResponseRecord): Response {\n return new Response(cached.body, {\n status: cached.status,\n statusText: cached.statusText,\n headers: cached.headers,\n });\n}\n\nfunction isFresh(cached: CachedResponseRecord, maxAgeSeconds: number): boolean {\n const ageSeconds = (Date.now() - cached.timestamp) / 1000;\n return ageSeconds <= maxAgeSeconds;\n}\n\nexport function clearMemoryFetchCache(): void {\n memoryCache.clear();\n}\n\nexport function deleteMemoryFetchCache(cacheKey: string): void {\n memoryCache.delete(cacheKey);\n}\n\nexport async function fetchWithCache(\n input: RequestInfo | URL,\n init?: RequestInit,\n options: FetchWithCacheOptions = {},\n): Promise<Response> {\n const maxAge = options.maxAge ?? 60 * 60;\n const method = (init?.method ?? 'GET').toUpperCase();\n\n if (method !== 'GET' && !options.cacheKey) {\n return fetch(input, init);\n }\n\n const cacheMode = getEffectiveCacheMode(options.cache);\n const cacheKey = options.cacheKey ?? createDefaultCacheKey(input, init);\n\n if (cacheMode === 'none') {\n return fetch(input, init);\n }\n\n if (cacheMode === 'memory' && !options.forceRefresh) {\n const cached = memoryCache.get(cacheKey);\n\n if (cached && isFresh(cached, maxAge)) {\n return toResponse(cached);\n }\n }\n\n const filePath = getCacheFilePath(cacheKey);\n\n if (cacheMode === 'fs') {\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n\n if (!options.forceRefresh) {\n try {\n const raw = await fs.readFile(filePath, 'utf8');\n const cached = JSON.parse(raw) as CachedResponseRecord;\n\n if (isFresh(cached, maxAge)) {\n return toResponse(cached);\n }\n } catch {\n // cache miss or invalid cache; fetch fresh\n }\n }\n }\n\n const res = await fetch(input, init);\n const body = await res.text();\n\n const record: CachedResponseRecord = {\n timestamp: Date.now(),\n status: res.status,\n statusText: res.statusText,\n headers: [...res.headers.entries()],\n body,\n };\n\n if (cacheMode === 'memory') {\n memoryCache.set(cacheKey, record);\n } else if (cacheMode === 'fs') {\n await fs.writeFile(filePath, JSON.stringify(record), 'utf8');\n }\n\n return new Response(body, {\n status: res.status,\n statusText: res.statusText,\n headers: res.headers,\n });\n}\n"],"mappings":"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","names":["path","path","path","path","path","fs","path","fs","path","fs","path","path","fs","path"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-html-pages",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "author": "Paul Browne",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/assets.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import type { OutputAsset, OutputBundle, OutputChunk, PluginContext } from 'rollup';
3
+ import type {
4
+ OutputBundle,
5
+ PluginContext,
6
+ } from 'rollup';
4
7
 
5
8
  export type HtmlAssetKind = 'css' | 'js';
6
9
 
@@ -80,8 +83,11 @@ export function resolveLocalAssetPath(args: {
80
83
  const cleanUrl = stripQueryAndHash(url);
81
84
 
82
85
  let abs: string;
86
+
83
87
  if (cleanUrl.startsWith('/')) {
84
88
  abs = path.join(root, pagesDir, cleanUrl.slice(1));
89
+ } else if (cleanUrl.startsWith(`${pagesDir}/`)) {
90
+ abs = path.join(root, cleanUrl);
85
91
  } else {
86
92
  const baseDir = pageDir ?? path.join(root, pagesDir);
87
93
  abs = path.resolve(baseDir, cleanUrl);
@@ -109,43 +115,43 @@ export function emitHtmlAsset(args: {
109
115
  }
110
116
 
111
117
  function replaceAllLiteral(
112
- input: string,
113
- search: string,
114
- replacement: string,
115
- ): string {
116
- return input.split(search).join(replacement);
118
+ input: string,
119
+ search: string,
120
+ replacement: string,
121
+ ): string {
122
+ return input.split(search).join(replacement);
117
123
  }
118
-
124
+
119
125
  export function rewriteHtmlAssetUrls(
120
- html: string,
121
- replacements: Map<string, string>,
126
+ html: string,
127
+ replacements: Map<string, string>,
122
128
  ): string {
123
- let out = html;
124
-
125
- for (const [originalUrl, builtUrl] of replacements) {
126
- out = replaceAllLiteral(
127
- out,
128
- `href="${originalUrl}"`,
129
- `href="${builtUrl}"`,
130
- );
131
- out = replaceAllLiteral(
132
- out,
133
- `href='${originalUrl}'`,
134
- `href='${builtUrl}'`,
135
- );
136
- out = replaceAllLiteral(
137
- out,
138
- `src="${originalUrl}"`,
139
- `src="${builtUrl}"`,
140
- );
141
- out = replaceAllLiteral(
142
- out,
143
- `src='${originalUrl}'`,
144
- `src='${builtUrl}'`,
145
- );
146
- }
147
-
148
- return out;
129
+ let out = html;
130
+
131
+ for (const [originalUrl, builtUrl] of replacements) {
132
+ out = replaceAllLiteral(
133
+ out,
134
+ `href="${originalUrl}"`,
135
+ `href="${builtUrl}"`,
136
+ );
137
+ out = replaceAllLiteral(
138
+ out,
139
+ `href='${originalUrl}'`,
140
+ `href='${builtUrl}'`,
141
+ );
142
+ out = replaceAllLiteral(
143
+ out,
144
+ `src="${originalUrl}"`,
145
+ `src="${builtUrl}"`,
146
+ );
147
+ out = replaceAllLiteral(
148
+ out,
149
+ `src='${originalUrl}'`,
150
+ `src='${builtUrl}'`,
151
+ );
152
+ }
153
+
154
+ return out;
149
155
  }
150
156
 
151
157
  export async function collectHtmlAssetRefs(args: {
@@ -192,40 +198,39 @@ export async function collectHtmlAssetRefs(args: {
192
198
  }
193
199
 
194
200
  export function buildHtmlAssetReplacementMap(args: {
195
- ctx: PluginContext;
196
- refs: Map<string, HtmlAssetRef>;
197
- bundle: OutputBundle;
198
- }): Map<string, string> {
199
- const { ctx, refs, bundle } = args;
200
- const replacements = new Map<string, string>();
201
-
202
- for (const ref of refs.values()) {
203
- if (ref.kind === 'js') {
204
- const fileName = ctx.getFileName(ref.refId);
205
- replacements.set(ref.originalUrl, `/${fileName}`);
201
+ ctx: PluginContext;
202
+ refs: Map<string, HtmlAssetRef>;
203
+ bundle: OutputBundle;
204
+ }): Map<string, string> {
205
+ const { ctx, refs, bundle } = args;
206
+ const replacements = new Map<string, string>();
207
+
208
+ for (const ref of refs.values()) {
209
+ if (ref.kind === 'js') {
210
+ const fileName = ctx.getFileName(ref.refId);
211
+ replacements.set(ref.originalUrl, `/${fileName}`);
212
+ continue;
213
+ }
214
+
215
+ if (ref.kind === 'css') {
216
+ const jsEntryFile = ctx.getFileName(ref.refId);
217
+ const jsChunk = bundle[jsEntryFile];
218
+
219
+ if (
220
+ jsChunk &&
221
+ jsChunk.type === 'chunk' &&
222
+ 'viteMetadata' in jsChunk &&
223
+ jsChunk.viteMetadata?.importedCss &&
224
+ jsChunk.viteMetadata.importedCss.size > 0
225
+ ) {
226
+ const cssFile = [...jsChunk.viteMetadata.importedCss][0];
227
+ replacements.set(ref.originalUrl, `/${cssFile}`);
206
228
  continue;
207
229
  }
208
-
209
- if (ref.kind === 'css') {
210
- const jsEntryFile = ctx.getFileName(ref.refId);
211
- const jsChunk = bundle[jsEntryFile];
212
-
213
- if (
214
- jsChunk &&
215
- jsChunk.type === 'chunk' &&
216
- 'viteMetadata' in jsChunk &&
217
- jsChunk.viteMetadata?.importedCss &&
218
- jsChunk.viteMetadata.importedCss.size > 0
219
- ) {
220
- const cssFile = [...jsChunk.viteMetadata.importedCss][0];
221
- replacements.set(ref.originalUrl, `/${cssFile}`);
222
- continue;
223
- }
224
-
225
- // Fallback: if no extracted CSS asset was found, keep the JS file
226
- replacements.set(ref.originalUrl, `/${jsEntryFile}`);
227
- }
230
+
231
+ replacements.set(ref.originalUrl, `/${jsEntryFile}`);
228
232
  }
229
-
230
- return replacements;
231
- }
233
+ }
234
+
235
+ return replacements;
236
+ }
package/src/dev-server.ts CHANGED
@@ -1,71 +1,121 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
1
3
  import type { ViteDevServer } from 'vite';
4
+
2
5
  import { renderPage } from './render-runtime';
3
- import { routeMatch } from './route-utils';
4
6
  import type { HtPageInfo, HtPageModule } from './types';
7
+ import { PLUGIN_NAME } from './constants';
8
+ import { createPageModuleLoader } from './module-loader';
9
+
10
+ function isStaticAssetRequest(url: string): boolean {
11
+ return (
12
+ url.endsWith('.css') ||
13
+ url.endsWith('.js') ||
14
+ url.endsWith('.mjs') ||
15
+ url.endsWith('.ts') ||
16
+ url.endsWith('.png') ||
17
+ url.endsWith('.jpg') ||
18
+ url.endsWith('.jpeg') ||
19
+ url.endsWith('.gif') ||
20
+ url.endsWith('.svg') ||
21
+ url.endsWith('.webp') ||
22
+ url.endsWith('.ico') ||
23
+ url.endsWith('.woff') ||
24
+ url.endsWith('.woff2') ||
25
+ url.endsWith('.ttf') ||
26
+ url.endsWith('.otf')
27
+ );
28
+ }
29
+
30
+ function shouldSkipHtmlRouting(url: string): boolean {
31
+ return (
32
+ url.startsWith('/@vite') ||
33
+ url.startsWith('/@fs/') ||
34
+ url.startsWith('/node_modules/') ||
35
+ url.startsWith('/src/') ||
36
+ url === '/favicon.ico' ||
37
+ isStaticAssetRequest(url)
38
+ );
39
+ }
40
+
41
+ function tryRewriteRootAssetToSrc(server: ViteDevServer, url: string): string | null {
42
+ if (!url.startsWith('/')) return null;
43
+ if (!isStaticAssetRequest(url)) return null;
44
+ if (url.startsWith('/src/')) return null;
5
45
 
6
- function isDynamicOnly(mod: HtPageModule): boolean {
46
+ const root = server.config.root;
47
+ const candidate = path.join(root, 'src', url.slice(1));
48
+
49
+ if (fs.existsSync(candidate)) {
50
+ return `/src/${url.slice(1)}`;
51
+ }
52
+
53
+ return null;
54
+ }
55
+
56
+ function shouldUseDynamicRendering(mod: HtPageModule): boolean {
7
57
  return mod.dynamic === true || mod.prerender === false;
8
58
  }
9
59
 
10
60
  export function installDevServer(args: {
11
61
  server: ViteDevServer;
12
62
  getPages: () => Promise<HtPageInfo[]>;
13
- getEntries: () => Promise<HtPageInfo[]>;
14
- }): void {
15
- const { server, getPages, getEntries } = args;
63
+ getEntries?: () => Promise<HtPageInfo[]>;
64
+ }) {
65
+ const { server, getPages } = args;
16
66
 
17
67
  server.middlewares.use(async (req, res, next) => {
18
68
  try {
19
- if (!req.url || req.method !== 'GET') return next();
20
-
21
- const pathname = req.url.split('?')[0];
69
+ const originalUrl = req.url ?? '/';
70
+ const url = originalUrl.split('?')[0];
22
71
 
23
- const pages = await getPages();
24
- const staticPage = pages.find((p) => p.routePath === pathname);
25
-
26
- if (staticPage) {
27
- const mod = (await server.ssrLoadModule(
28
- `/${staticPage.relativePath}`,
29
- )) as HtPageModule;
30
-
31
- const html = await renderPage(staticPage, mod, true);
72
+ const rewrittenAssetUrl = tryRewriteRootAssetToSrc(server, url);
73
+ if (rewrittenAssetUrl) {
74
+ req.url = rewrittenAssetUrl + originalUrl.slice(url.length);
75
+ return next();
76
+ }
32
77
 
33
- res.statusCode = 200;
34
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
35
- res.end(html);
36
- return;
78
+ if (shouldSkipHtmlRouting(url)) {
79
+ return next();
37
80
  }
38
81
 
39
- const entries = await getEntries();
82
+ const pages = await getPages();
40
83
 
41
- for (const entry of entries) {
42
- const mod = (await server.ssrLoadModule(
43
- `/${entry.relativePath}`,
44
- )) as HtPageModule;
84
+ const page = pages.find((p) => p.routePath === url);
45
85
 
46
- if (!isDynamicOnly(mod)) continue;
86
+ if (!page) {
87
+ return next();
88
+ }
47
89
 
48
- const params = routeMatch(entry.routePattern, pathname);
49
- if (!params) continue;
90
+ const loadModule = await createPageModuleLoader({
91
+ mode: 'dev',
92
+ root: server.config.root,
93
+ server,
94
+ });
50
95
 
51
- const page: HtPageInfo = {
52
- ...entry,
53
- routePath: pathname,
54
- fileName: '',
55
- params,
56
- };
96
+ const mod = await loadModule(page.entryPath, page.relativePath);
57
97
 
58
- const html = await renderPage(page, mod, true);
98
+ if (!mod) {
99
+ return next();
100
+ }
59
101
 
60
- res.statusCode = 200;
61
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
62
- res.end(html);
63
- return;
102
+ if (!shouldUseDynamicRendering(mod) && page.dynamic) {
103
+ return next();
64
104
  }
65
105
 
66
- next();
106
+ const html = await renderPage(page, mod, true);
107
+
108
+ res.statusCode = 200;
109
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
110
+ res.end(html);
67
111
  } catch (error) {
68
- next(error);
112
+ server.config.logger.error(
113
+ `[${PLUGIN_NAME}] dev server render failed: ${
114
+ error instanceof Error ? error.stack ?? error.message : String(error)
115
+ }`,
116
+ );
117
+
118
+ next(error as Error);
69
119
  }
70
120
  });
71
121
  }
package/src/plugin.ts CHANGED
@@ -162,40 +162,47 @@ export function htPages(options: HtPagesPluginOptions = {}): Plugin {
162
162
  },
163
163
 
164
164
  async buildStart() {
165
- htmlAssetRefs.clear();
166
-
167
- const { entries, modulesByEntry, pages } = await buildPagesPipeline();
168
-
165
+ const entries = await discoverEntryPages(root, options);
166
+
169
167
  for (const entry of entries) {
170
168
  this.addWatchFile(entry.entryPath);
171
169
  }
172
-
170
+
171
+ // emitFile() is build-only
172
+ if (server) {
173
+ return;
174
+ }
175
+
176
+ htmlAssetRefs.clear();
177
+
178
+ const { modulesByEntry, pages } = await buildPagesPipeline();
179
+
173
180
  const htmlByPageKey = new Map<string, { html: string; pageDir?: string }>();
174
-
181
+
175
182
  for (const page of pages) {
176
183
  const mod = modulesByEntry.get(page.entryPath);
177
-
184
+
178
185
  if (!mod) {
179
186
  throw new Error(
180
187
  `[${PLUGIN_NAME}] Missing module for page entry: ${page.entryPath}`,
181
188
  );
182
189
  }
183
-
190
+
184
191
  const html = await renderPage(page, mod, false);
185
-
192
+
186
193
  htmlByPageKey.set(page.entryPath, {
187
194
  html,
188
195
  pageDir: path.dirname(page.absolutePath),
189
196
  });
190
197
  }
191
-
198
+
192
199
  htmlAssetRefs = await collectHtmlAssetRefs({
193
200
  ctx: this,
194
201
  root,
195
202
  pagesDir,
196
203
  htmlByPageKey,
197
204
  });
198
-
205
+
199
206
  logDebug(
200
207
  options.debug,
201
208
  'collected html assets',