vite-plugin-html-pages 1.3.4 → 1.3.6

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.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { Plugin as Plugin$1 } from 'vite';
2
- import { Plugin } from 'rollup';
1
+ import { Plugin } from 'vite';
3
2
 
4
3
  interface StaticParamRecord {
5
4
  [key: string]: string | number | boolean;
6
5
  }
6
+ type HtPageParams = Record<string, string | string[] | undefined>;
7
7
  interface HtPageInfo {
8
8
  id: string;
9
9
  entryPath: string;
@@ -14,18 +14,28 @@ interface HtPageInfo {
14
14
  fileName: string;
15
15
  dynamic: boolean;
16
16
  paramNames: string[];
17
- params: Record<string, string>;
17
+ paramDefinitions: RouteParamDefinition[];
18
+ params: HtPageParams;
18
19
  }
19
- interface HtPageRenderContext {
20
+ type HtPageRenderContext = {
20
21
  page: HtPageInfo;
21
- params: Record<string, string>;
22
+ params: HtPageParams;
22
23
  data?: unknown;
23
24
  dev: boolean;
24
- }
25
+ };
25
26
  interface HtPageModule {
26
- default?: string | ((ctx: HtPageRenderContext) => string | Promise<string>);
27
- data?: (ctx: HtPageRenderContext) => unknown | Promise<unknown>;
28
- generateStaticParams?: () => StaticParamRecord[] | Promise<StaticParamRecord[]>;
27
+ default?: ((ctx: {
28
+ page: HtPageInfo;
29
+ params: Record<string, string | string[] | undefined>;
30
+ data?: unknown;
31
+ dev: boolean;
32
+ }) => string | Promise<string>) | string;
33
+ data?: (ctx: {
34
+ page: HtPageInfo;
35
+ params: Record<string, string | string[] | undefined>;
36
+ dev: boolean;
37
+ }) => unknown | Promise<unknown>;
38
+ generateStaticParams?: () => Array<Record<string, string | number | boolean>> | Promise<Array<Record<string, string | number | boolean>>>;
29
39
  dynamic?: boolean;
30
40
  prerender?: boolean;
31
41
  }
@@ -35,12 +45,10 @@ interface HtPagesPluginOptions {
35
45
  exclude?: string | string[];
36
46
  pagesDir?: string;
37
47
  pageExtensions?: string[];
38
- renderConcurrency?: number;
39
- renderBatchSize?: number;
40
48
  cleanUrls?: boolean;
41
- ssrPlugins?: Plugin[];
42
- mapOutputPath?: (page: HtPageInfo) => string;
43
49
  debug?: boolean;
50
+ renderConcurrency?: number;
51
+ renderBatchSize?: number;
44
52
  site?: string;
45
53
  missingAssets?: 'error' | 'warn';
46
54
  rss?: {
@@ -49,9 +57,14 @@ interface HtPagesPluginOptions {
49
57
  description?: string;
50
58
  routePrefix?: string;
51
59
  };
60
+ mapOutputPath?: (page: HtPageInfo) => string;
52
61
  }
62
+ type RouteParamDefinition = {
63
+ name: string;
64
+ type: 'single' | 'catch-all' | 'optional-catch-all';
65
+ };
53
66
 
54
- declare function htPages(options?: HtPagesPluginOptions): Plugin$1;
67
+ declare function htPages(options?: HtPagesPluginOptions): Plugin;
55
68
 
56
69
  type FetchCacheMode = 'auto' | 'memory' | 'fs' | 'none';
57
70
  interface FetchWithCacheOptions {
package/dist/index.js CHANGED
@@ -28,11 +28,7 @@ function stripPageSuffix(filePath, extensions) {
28
28
  var DYNAMIC_SEGMENT_RE = /\[([A-Za-z0-9_]+)\]/g;
29
29
  var CATCH_ALL_SEGMENT_RE = /\[\.\.\.([A-Za-z0-9_]+)\]/g;
30
30
  var OPTIONAL_CATCH_ALL_SEGMENT_RE = /\[\.\.\.([A-Za-z0-9_]+)\]\?/g;
31
- var ANY_PARAM_RE = /\[(?:\.\.\.)?([A-Za-z0-9_]+)\]\??/g;
32
31
  var ROUTE_GROUP_RE = /(^|\/)\(([^)]+)\)(?=\/|$)/g;
33
- function getParamNames(relativeFromPagesDir) {
34
- return [...relativeFromPagesDir.matchAll(ANY_PARAM_RE)].map((m) => m[1]);
35
- }
36
32
  function isDynamicPage(relativeFromPagesDir) {
37
33
  return /\[(?:\.\.\.)?[A-Za-z0-9_]+\]\??/.test(relativeFromPagesDir);
38
34
  }
@@ -111,9 +107,37 @@ function compareRoutePriority(a, b) {
111
107
  return bSegs.length - aSegs.length;
112
108
  }
113
109
 
110
+ // src/route-params.ts
111
+ function parseRouteParamSegment(segment) {
112
+ if (segment.startsWith("[...") && segment.endsWith("]?")) {
113
+ return {
114
+ name: segment.slice(4, -2),
115
+ type: "optional-catch-all"
116
+ };
117
+ }
118
+ if (segment.startsWith("[...") && segment.endsWith("]")) {
119
+ return {
120
+ name: segment.slice(4, -1),
121
+ type: "catch-all"
122
+ };
123
+ }
124
+ if (segment.startsWith("[") && segment.endsWith("]")) {
125
+ return {
126
+ name: segment.slice(1, -1),
127
+ type: "single"
128
+ };
129
+ }
130
+ return null;
131
+ }
132
+ function extractRouteParamDefinitions(routePattern) {
133
+ return routePattern.split("/").filter(Boolean).map((segment) => parseRouteParamSegment(segment)).filter((value) => value != null);
134
+ }
135
+
114
136
  // src/constants.ts
115
137
  var PLUGIN_NAME = "vite-plugin-html-pages";
116
138
  var VIRTUAL_BUILD_ENTRY_ID = `\0${PLUGIN_NAME}:build-entry`;
139
+ var VIRTUAL_PAGE_HELPER_ID = `${PLUGIN_NAME}/page`;
140
+ var RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX = `\0${PLUGIN_NAME}/page:`;
117
141
  var VIRTUAL_MANIFEST_ID = `\0virtual:${PLUGIN_NAME}-manifest`;
118
142
  var CACHE_DIR_NAME = `node_modules/.cache/${PLUGIN_NAME}`;
119
143
 
@@ -148,6 +172,7 @@ async function discoverEntryPages(root, options) {
148
172
  }
149
173
  const dynamic = isDynamicPage(relativeFromPagesDir);
150
174
  const routePattern = toRoutePattern(relativeFromPagesDir, pageExtensions);
175
+ const paramDefinitions = extractRouteParamDefinitions(routePattern);
151
176
  return {
152
177
  id: entryPath,
153
178
  entryPath,
@@ -157,7 +182,8 @@ async function discoverEntryPages(root, options) {
157
182
  routePath: routePattern,
158
183
  fileName: "",
159
184
  dynamic,
160
- paramNames: getParamNames(relativeFromPagesDir),
185
+ paramNames: paramDefinitions.map((p) => p.name),
186
+ paramDefinitions,
161
187
  params: {}
162
188
  };
163
189
  });
@@ -310,9 +336,14 @@ function installDevServer(args) {
310
336
  return next();
311
337
  }
312
338
  const html = await renderPage(page, mod, true);
339
+ const transformedHtml = await server.transformIndexHtml(
340
+ url,
341
+ html,
342
+ req.originalUrl
343
+ );
313
344
  res.statusCode = 200;
314
345
  res.setHeader("Content-Type", "text/html; charset=utf-8");
315
- res.end(html);
346
+ res.end(transformedHtml);
316
347
  } catch (error) {
317
348
  server.config.logger.error(
318
349
  `[${PLUGIN_NAME}] dev server render failed: ${error instanceof Error ? error.stack ?? error.message : String(error)}`
@@ -632,6 +663,39 @@ function validateHtmlAssetReferences(options) {
632
663
  }
633
664
  }
634
665
 
666
+ // src/page-helper-generator.ts
667
+ function paramsTypeFromDefinitions(paramDefinitions) {
668
+ if (paramDefinitions.length === 0) {
669
+ return "{}";
670
+ }
671
+ const fields = paramDefinitions.map((param) => {
672
+ if (param.type === "single") {
673
+ return `${JSON.stringify(param.name)}: string`;
674
+ }
675
+ if (param.type === "catch-all") {
676
+ return `${JSON.stringify(param.name)}: string[]`;
677
+ }
678
+ return `${JSON.stringify(param.name)}?: string[]`;
679
+ });
680
+ return `{ ${fields.join("; ")} }`;
681
+ }
682
+ function generateTypedPageHelper(page) {
683
+ const paramsType = page ? paramsTypeFromDefinitions(page.paramDefinitions ?? []) : "{}";
684
+ return `
685
+ export type PageParams = ${paramsType};
686
+
687
+ export type PageContext = {
688
+ params: PageParams;
689
+ data?: unknown;
690
+ dev: boolean;
691
+ };
692
+
693
+ export function definePage<T extends (ctx: PageContext) => any>(fn: T): T {
694
+ return fn;
695
+ }
696
+ `;
697
+ }
698
+
635
699
  // src/plugin.ts
636
700
  import fs4 from "fs";
637
701
  import path7 from "path";
@@ -730,14 +794,26 @@ function htPages(options = {}) {
730
794
  }
731
795
  };
732
796
  },
733
- resolveId(id) {
797
+ resolveId(id, importer) {
734
798
  if (id === VIRTUAL_BUILD_ENTRY_ID) return id;
799
+ if (id === VIRTUAL_PAGE_HELPER_ID && importer) {
800
+ return `${RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX}${importer}`;
801
+ }
735
802
  return null;
736
803
  },
737
- load(id) {
804
+ async load(id) {
738
805
  if (id === VIRTUAL_BUILD_ENTRY_ID) {
739
806
  return "export default {};";
740
807
  }
808
+ if (id.startsWith(RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX)) {
809
+ const importer = id.slice(RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX.length);
810
+ const { pages } = await buildPagesPipeline();
811
+ const normalizedImporter = path7.resolve(importer);
812
+ const page = pages.find(
813
+ (candidate) => path7.resolve(candidate.absolutePath) === normalizedImporter
814
+ );
815
+ return generateTypedPageHelper(page);
816
+ }
741
817
  return null;
742
818
  },
743
819
  configResolved(resolved) {
@@ -788,10 +864,7 @@ function htPages(options = {}) {
788
864
  }
789
865
  logDebug(options.debug, "file changed", file);
790
866
  await loadDevPages();
791
- server?.ws.send({
792
- type: "full-reload",
793
- path: "*"
794
- });
867
+ server?.ws.send({ type: "full-reload" });
795
868
  };
796
869
  server.watcher.on("add", reload);
797
870
  server.watcher.on("change", reload);
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/dev-server.ts","../src/errors.ts","../src/render-runtime.ts","../src/module-loader.ts","../src/page-index.ts","../src/static-assets.ts","../src/html-asset-validator.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 buildProcessedStaticAssets,\n collectStaticAssets,\n copyStaticAssetSource,\n} from './static-assets';\nimport { validateHtmlAssetReferences } from './html-asset-validator';\nimport type { HtPageInfo, HtPageModule, HtPagesPluginOptions } from './types';\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 watcherAttached = false;\n\n const cleanUrls = options.cleanUrls ?? true;\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 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 const staticAssets = await collectStaticAssets({\n root,\n pagesDir,\n pageExtensions,\n });\n\n for (const asset of staticAssets) {\n this.addWatchFile(asset.absolutePath);\n }\n\n logDebug(\n options.debug,\n 'static assets',\n staticAssets.map((asset) => ({\n kind: asset.kind,\n input: asset.relativePathFromSrc,\n output: asset.outputFileName,\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 if (!watcherAttached) {\n watcherAttached = true;\n \n const reload = async (file: string) => {\n if (!file.includes(`${path.sep}src${path.sep}`) && !file.includes('/src/')) {\n return;\n }\n \n logDebug(options.debug, 'file changed', file);\n \n await loadDevPages();\n \n server?.ws.send({\n type: 'full-reload',\n path: '*',\n });\n };\n \n server.watcher.on('add', reload);\n server.watcher.on('change', reload);\n server.watcher.on('unlink', reload);\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 \n // server.ws.send({\n // type: 'full-reload',\n // });\n \n // return [];\n // },\n\n async generateBundle(_, bundle) {\n try {\n const { modulesByEntry, pages } = await buildPagesPipeline();\n\n const staticAssets = await collectStaticAssets({\n root,\n pagesDir,\n pageExtensions,\n });\n\n logDebug(\n options.debug,\n 'emitting pages',\n pages.map((p) => p.fileName),\n );\n\n logDebug(\n options.debug,\n 'emitting static assets',\n staticAssets.map((asset) => ({\n kind: asset.kind,\n input: asset.relativePathFromSrc,\n output: asset.outputFileName,\n })),\n );\n\n const limit = pLimit(options.renderConcurrency ?? 8);\n const batchSize =\n options.renderBatchSize ??\n Math.max(options.renderConcurrency ?? 8, 32);\n\n const processedOutputs = await buildProcessedStaticAssets({\n root,\n pagesDir,\n assets: staticAssets,\n minify: true,\n sourcemap: false,\n });\n\n for (const [fileName, source] of processedOutputs) {\n this.emitFile({\n type: 'asset',\n fileName,\n source,\n });\n }\n\n for (const asset of staticAssets) {\n if (asset.kind !== 'copy') continue;\n\n const source = await copyStaticAssetSource(asset);\n\n this.emitFile({\n type: 'asset',\n fileName: asset.outputFileName,\n source,\n });\n }\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 const html = await renderPage(page, mod, false);\n\n validateHtmlAssetReferences({\n root,\n pagesDir,\n html,\n pluginName: PLUGIN_NAME,\n pageLabel: page.relativePath,\n missingAssets: options.missingAssets ?? 'error',\n });\n \n this.emitFile({\n type: 'asset',\n fileName: options.mapOutputPath?.(page) ?? page.fileName,\n source: html,\n });\n }),\n ),\n );\n }\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 const html = await renderPage(notFoundPage, mod, false);\n\n validateHtmlAssetReferences({\n root,\n pagesDir,\n html,\n pluginName: PLUGIN_NAME,\n pageLabel: notFoundPage.relativePath,\n missingAssets: options.missingAssets ?? 'error',\n });\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 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 const rss = options.rss;\n\n if (rss?.site) {\n const routePrefix = rss.routePrefix ?? '/blog';\n \n const rssItems = pages\n .filter((page) => page.routePath.startsWith(routePrefix))\n .map((page) => {\n const url = `${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 rssXml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<rss version=\"2.0\">\\n<channel>\\n <title>${rss.title ?? PLUGIN_NAME}</title>\\n <link>${rss.site}</link>\\n <description>${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: rssXml,\n });\n \n logDebug(options.debug, 'generated rss.xml');\n }\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;\n","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/promises';\nimport path from 'node:path';\nimport fg from 'fast-glob';\nimport * as esbuild from 'esbuild';\nimport fsSync from 'node:fs';\n\nexport interface StaticAssetFile {\n absolutePath: string;\n relativePathFromSrc: string;\n outputFileName: string;\n kind: 'copy' | 'process';\n}\n\nexport interface CollectStaticAssetsArgs {\n root: string;\n pagesDir: string;\n pageExtensions: string[];\n}\n\nfunction normalizeSlashes(value: string): string {\n return value.replace(/\\\\/g, '/');\n}\n\nfunction hasAnySuffix(value: string, suffixes: string[]): boolean {\n return suffixes.some((suffix) => value.endsWith(suffix));\n}\n\nfunction shouldIgnoreFile(rel: string): boolean {\n return (\n rel.endsWith('.d.ts') ||\n rel.endsWith('.map') ||\n rel.endsWith('.tsbuildinfo') ||\n rel.startsWith('.') ||\n rel.includes('/.')\n );\n}\n\nfunction isProcessableAsset(rel: string): boolean {\n return (\n rel.endsWith('.js') ||\n rel.endsWith('.mjs') ||\n rel.endsWith('.ts') ||\n rel.endsWith('.css')\n );\n}\n\nfunction toOutputFileName(relativePathFromSrc: string): string {\n if (relativePathFromSrc.endsWith('.ts')) {\n return relativePathFromSrc.slice(0, -3) + '.js';\n }\n return relativePathFromSrc;\n}\n\nexport async function collectStaticAssets(\n args: CollectStaticAssetsArgs,\n): Promise<StaticAssetFile[]> {\n const { root, pagesDir, pageExtensions } = args;\n const srcDir = path.join(root, pagesDir);\n\n const entries = await fg('**/*', {\n cwd: srcDir,\n onlyFiles: true,\n dot: false,\n absolute: false,\n });\n\n const assets: StaticAssetFile[] = [];\n\n for (const entry of entries) {\n const rel = normalizeSlashes(entry);\n\n if (shouldIgnoreFile(rel)) continue;\n if (hasAnySuffix(rel, pageExtensions)) continue;\n\n const absolutePath = path.join(srcDir, rel);\n\n assets.push({\n absolutePath,\n relativePathFromSrc: rel,\n outputFileName: normalizeSlashes(toOutputFileName(rel)),\n kind: isProcessableAsset(rel) ? 'process' : 'copy',\n });\n }\n\n return assets;\n}\n\nexport async function copyStaticAssetSource(\n asset: StaticAssetFile,\n): Promise<Uint8Array> {\n return fs.readFile(asset.absolutePath);\n}\n\nexport async function buildProcessedStaticAssets(args: {\n root: string;\n pagesDir: string;\n assets: StaticAssetFile[];\n minify?: boolean;\n sourcemap?: boolean;\n }): Promise<Map<string, string | Uint8Array>> {\n const { root, pagesDir, assets, minify = true, sourcemap = false } = args;\n \n const processable = assets.filter((a) => a.kind === 'process');\n const out = new Map<string, string | Uint8Array>();\n \n if (processable.length === 0) {\n return out;\n }\n \n const srcDir = path.join(root, pagesDir);\n const distDir = path.join(root, 'dist');\n const warnedMissingAssets = new Set<string>();\n const result = await esbuild.build({\n entryPoints: processable.map((a) => a.absolutePath),\n absWorkingDir: root,\n outbase: srcDir,\n outdir: distDir,\n bundle: true,\n splitting: true,\n treeShaking: true,\n minify,\n sourcemap,\n format: 'esm',\n target: 'es2020',\n platform: 'browser',\n write: false,\n entryNames: '[dir]/[name]',\n assetNames: '[dir]/[name]',\n loader: {\n '.css': 'css',\n '.png': 'file',\n '.jpg': 'file',\n '.jpeg': 'file',\n '.gif': 'file',\n '.svg': 'file',\n '.webp': 'file',\n '.woff': 'file',\n '.woff2': 'file',\n '.ttf': 'file',\n '.otf': 'file',\n },\n plugins: [\n {\n name: 'html-pages-root-url-resolver',\n setup(build) {\n build.onResolve({ filter: /^\\// }, (resolveArgs) => {\n // Leave real filesystem absolute paths alone\n if (\n path.isAbsolute(resolveArgs.path) &&\n fsSync.existsSync(resolveArgs.path)\n ) {\n return { path: resolveArgs.path };\n }\n \n const cleanPath = resolveArgs.path.slice(1);\n \n const fromSrc = path.join(srcDir, cleanPath);\n if (fsSync.existsSync(fromSrc)) {\n return { path: fromSrc };\n }\n \n const fromPublic = path.join(root, 'public', cleanPath);\n if (fsSync.existsSync(fromPublic)) {\n return {\n path: resolveArgs.path,\n external: true,\n };\n }\n \n const isCssUrlToken = resolveArgs.kind === 'url-token';\n \n if (isCssUrlToken) {\n if (!warnedMissingAssets.has(resolveArgs.path)) {\n warnedMissingAssets.add(resolveArgs.path);\n console.warn(\n `[vite-plugin-html-pages] ⚠️ Missing CSS asset: ${resolveArgs.path}\\n` +\n ` Looked in:\\n` +\n ` - ${fromSrc}\\n` +\n ` - ${fromPublic}`\n );\n }\n \n // Keep the original root-relative URL in output CSS\n return {\n path: resolveArgs.path,\n external: true,\n };\n }\n \n // JS/CSS entry imports remain strict\n return {\n path: fromSrc,\n };\n });\n },\n },\n ],\n });\n \n for (const file of result.outputFiles) {\n const rel = normalizeSlashes(path.relative(distDir, file.path));\n out.set(rel, file.text ?? file.contents);\n }\n \n return out;\n }\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nexport interface HtmlAssetValidationOptions {\n root: string;\n pagesDir: string;\n html: string;\n pluginName: string;\n pageLabel?: string;\n missingAssets?: 'error' | 'warn';\n}\n\nfunction stripQueryAndHash(url: string): string {\n return url.split('#')[0].split('?')[0];\n}\n\nfunction isLocalRootUrl(url: string): boolean {\n return !!url && url.startsWith('/') && !url.startsWith('//');\n}\n\nfunction fileExistsForPublicUrl(root: string, pagesDir: string, url: string): boolean {\n const clean = stripQueryAndHash(url).slice(1);\n\n const fromSrc = path.join(root, pagesDir, clean);\n if (fs.existsSync(fromSrc)) return true;\n\n const fromPublic = path.join(root, 'public', clean);\n if (fs.existsSync(fromPublic)) return true;\n\n return false;\n}\n\nfunction collectScriptSrcs(html: string): string[] {\n const out: string[] = [];\n\n for (const match of html.matchAll(\n /<script\\b[^>]*\\bsrc=[\"']([^\"']+)[\"'][^>]*>/gi,\n )) {\n out.push(match[1]);\n }\n\n return out;\n}\n\nfunction collectStylesheetHrefs(html: string): string[] {\n const out: string[] = [];\n\n for (const match of html.matchAll(\n /<link\\b[^>]*\\brel=[\"']stylesheet[\"'][^>]*\\bhref=[\"']([^\"']+)[\"'][^>]*>/gi,\n )) {\n out.push(match[1]);\n }\n\n return out;\n}\n\nfunction collectLiteralDynamicImports(html: string): string[] {\n const out: string[] = [];\n\n for (const match of html.matchAll(\n /import\\s*\\(\\s*[\"']([^\"'`]+)[\"']\\s*\\)/gi,\n )) {\n out.push(match[1]);\n }\n\n return out;\n}\n\nfunction unique(values: string[]): string[] {\n return [...new Set(values)];\n}\n\nfunction formatPageLabel(pageLabel?: string): string {\n return pageLabel ? ` (${pageLabel})` : '';\n}\n\nfunction missingAssetMessage(args: {\n pluginName: string;\n kind: string;\n url: string;\n root: string;\n pagesDir: string;\n pageLabel?: string;\n}): string {\n const { pluginName, kind, url, root, pagesDir, pageLabel } = args;\n const clean = stripQueryAndHash(url).slice(1);\n const pageSuffix = formatPageLabel(pageLabel);\n\n return (\n `[${pluginName}] Missing ${kind}${pageSuffix}: ${url}\\n` +\n `Expected one of:\\n` +\n `- ${path.join(root, pagesDir, clean)}\\n` +\n `- ${path.join(root, 'public', clean)}`\n );\n}\n\nfunction reportMissing(args: {\n mode: 'error' | 'warn';\n pluginName: string;\n kind: string;\n url: string;\n root: string;\n pagesDir: string;\n pageLabel?: string;\n}) {\n const message = missingAssetMessage(args);\n\n if (args.mode === 'warn') {\n console.warn(`⚠️ ${message}`);\n return;\n }\n\n throw new Error(message);\n}\n\nexport function validateHtmlAssetReferences(\n options: HtmlAssetValidationOptions,\n): void {\n const {\n root,\n pagesDir,\n html,\n pluginName,\n pageLabel,\n missingAssets = 'error',\n } = options;\n\n const scriptSrcs = unique(collectScriptSrcs(html)).filter(isLocalRootUrl);\n const stylesheetHrefs = unique(collectStylesheetHrefs(html)).filter(isLocalRootUrl);\n const literalDynamicImports = unique(collectLiteralDynamicImports(html)).filter(\n isLocalRootUrl,\n );\n\n for (const url of scriptSrcs) {\n if (!fileExistsForPublicUrl(root, pagesDir, url)) {\n reportMissing({\n mode: missingAssets,\n pluginName,\n kind: 'JavaScript asset',\n url,\n root,\n pagesDir,\n pageLabel,\n });\n }\n }\n\n for (const url of stylesheetHrefs) {\n if (!fileExistsForPublicUrl(root, pagesDir, url)) {\n reportMissing({\n mode: missingAssets,\n pluginName,\n kind: 'stylesheet asset',\n url,\n root,\n pagesDir,\n pageLabel,\n });\n }\n }\n\n for (const url of literalDynamicImports) {\n if (!fileExistsForPublicUrl(root, pagesDir, url)) {\n console.warn(\n `⚠️ ${missingAssetMessage({\n pluginName,\n kind: 'literal dynamic import',\n url,\n root,\n pagesDir,\n pageLabel,\n })}`,\n );\n }\n }\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","fg","path","path","path","path","fs","path","build","fs","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/route-params.ts","../src/constants.ts","../src/dev-server.ts","../src/errors.ts","../src/render-runtime.ts","../src/module-loader.ts","../src/page-index.ts","../src/static-assets.ts","../src/html-asset-validator.ts","../src/page-helper-generator.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 buildProcessedStaticAssets,\n collectStaticAssets,\n copyStaticAssetSource,\n} from './static-assets';\nimport { validateHtmlAssetReferences } from './html-asset-validator';\nimport type { HtPageInfo, HtPageModule, HtPagesPluginOptions } from './types';\nimport {\n PLUGIN_NAME,\n VIRTUAL_BUILD_ENTRY_ID,\n VIRTUAL_PAGE_HELPER_ID,\n RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX,\n} from './constants';\nimport { generateTypedPageHelper } from './page-helper-generator';\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 watcherAttached = false;\n\n const cleanUrls = options.cleanUrls ?? true;\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 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, importer) {\n if (id === VIRTUAL_BUILD_ENTRY_ID) return id;\n \n if (id === VIRTUAL_PAGE_HELPER_ID && importer) {\n return `${RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX}${importer}`;\n }\n \n return null;\n },\n\n async load(id) {\n if (id === VIRTUAL_BUILD_ENTRY_ID) {\n return 'export default {};';\n }\n \n if (id.startsWith(RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX)) {\n const importer = id.slice(RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX.length);\n const { pages } = await buildPagesPipeline();\n \n const normalizedImporter = path.resolve(importer);\n \n const page = pages.find(\n (candidate) => path.resolve(candidate.absolutePath) === normalizedImporter,\n );\n \n return generateTypedPageHelper(page);\n }\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 const staticAssets = await collectStaticAssets({\n root,\n pagesDir,\n pageExtensions,\n });\n\n for (const asset of staticAssets) {\n this.addWatchFile(asset.absolutePath);\n }\n\n logDebug(\n options.debug,\n 'static assets',\n staticAssets.map((asset) => ({\n kind: asset.kind,\n input: asset.relativePathFromSrc,\n output: asset.outputFileName,\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 if (!watcherAttached) {\n watcherAttached = true;\n \n const reload = async (file: string) => {\n if (!file.includes(`${path.sep}src${path.sep}`) && !file.includes('/src/')) {\n return;\n }\n \n logDebug(options.debug, 'file changed', file);\n \n await loadDevPages();\n \n server?.ws.send({ type: 'full-reload' });\n\n };\n \n server.watcher.on('add', reload);\n server.watcher.on('change', reload);\n server.watcher.on('unlink', reload);\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 \n // server.ws.send({\n // type: 'full-reload',\n // });\n \n // return [];\n // },\n\n async generateBundle(_, bundle) {\n try {\n const { modulesByEntry, pages } = await buildPagesPipeline();\n\n const staticAssets = await collectStaticAssets({\n root,\n pagesDir,\n pageExtensions,\n });\n\n logDebug(\n options.debug,\n 'emitting pages',\n pages.map((p) => p.fileName),\n );\n\n logDebug(\n options.debug,\n 'emitting static assets',\n staticAssets.map((asset) => ({\n kind: asset.kind,\n input: asset.relativePathFromSrc,\n output: asset.outputFileName,\n })),\n );\n\n const limit = pLimit(options.renderConcurrency ?? 8);\n const batchSize =\n options.renderBatchSize ??\n Math.max(options.renderConcurrency ?? 8, 32);\n\n const processedOutputs = await buildProcessedStaticAssets({\n root,\n pagesDir,\n assets: staticAssets,\n minify: true,\n sourcemap: false,\n });\n\n for (const [fileName, source] of processedOutputs) {\n this.emitFile({\n type: 'asset',\n fileName,\n source,\n });\n }\n\n for (const asset of staticAssets) {\n if (asset.kind !== 'copy') continue;\n\n const source = await copyStaticAssetSource(asset);\n\n this.emitFile({\n type: 'asset',\n fileName: asset.outputFileName,\n source,\n });\n }\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 const html = await renderPage(page, mod, false);\n\n validateHtmlAssetReferences({\n root,\n pagesDir,\n html,\n pluginName: PLUGIN_NAME,\n pageLabel: page.relativePath,\n missingAssets: options.missingAssets ?? 'error',\n });\n \n this.emitFile({\n type: 'asset',\n fileName: options.mapOutputPath?.(page) ?? page.fileName,\n source: html,\n });\n }),\n ),\n );\n }\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 const html = await renderPage(notFoundPage, mod, false);\n\n validateHtmlAssetReferences({\n root,\n pagesDir,\n html,\n pluginName: PLUGIN_NAME,\n pageLabel: notFoundPage.relativePath,\n missingAssets: options.missingAssets ?? 'error',\n });\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 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 const rss = options.rss;\n\n if (rss?.site) {\n const routePrefix = rss.routePrefix ?? '/blog';\n \n const rssItems = pages\n .filter((page) => page.routePath.startsWith(routePrefix))\n .map((page) => {\n const url = `${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 rssXml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<rss version=\"2.0\">\\n<channel>\\n <title>${rss.title ?? PLUGIN_NAME}</title>\\n <link>${rss.site}</link>\\n <description>${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: rssXml,\n });\n \n logDebug(options.debug, 'generated rss.xml');\n }\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;\n","import path from 'node:path';\nimport { normalizeFsPath, toPosix } from './path-utils';\nimport { isDynamicPage, toRoutePattern } from './route-utils';\nimport { extractRouteParamDefinitions } from './route-params';\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 const paramDefinitions = extractRouteParamDefinitions(routePattern);\n\n return {\n id: entryPath,\n entryPath,\n absolutePath: entryPath,\n relativePath,\n routePattern,\n routePath: routePattern,\n fileName: '',\n dynamic,\n paramNames: paramDefinitions.map((p) => p.name),\n paramDefinitions,\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}","import type { RouteParamDefinition } from './types';\n\nexport function parseRouteParamSegment(\n segment: string,\n): RouteParamDefinition | null {\n if (segment.startsWith('[...') && segment.endsWith(']?')) {\n return {\n name: segment.slice(4, -2),\n type: 'optional-catch-all',\n };\n }\n\n if (segment.startsWith('[...') && segment.endsWith(']')) {\n return {\n name: segment.slice(4, -1),\n type: 'catch-all',\n };\n }\n\n if (segment.startsWith('[') && segment.endsWith(']')) {\n return {\n name: segment.slice(1, -1),\n type: 'single',\n };\n }\n\n return null;\n}\n\nexport function extractRouteParamDefinitions(\n routePattern: string,\n): RouteParamDefinition[] {\n return routePattern\n .split('/')\n .filter(Boolean)\n .map((segment) => parseRouteParamSegment(segment))\n .filter((value): value is RouteParamDefinition => value != null);\n}","export const PLUGIN_NAME = 'vite-plugin-html-pages';\nexport const VIRTUAL_BUILD_ENTRY_ID = `\\0${PLUGIN_NAME}:build-entry`;\nexport const VIRTUAL_PAGE_HELPER_ID = `${PLUGIN_NAME}/page`;\nexport const RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX =`\\0${PLUGIN_NAME}/page:`;\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 const transformedHtml = await server.transformIndexHtml(\n url,\n html,\n req.originalUrl,\n );\n \n res.statusCode = 200;\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.end(transformedHtml);\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/promises';\nimport path from 'node:path';\nimport fg from 'fast-glob';\nimport * as esbuild from 'esbuild';\nimport fsSync from 'node:fs';\n\nexport interface StaticAssetFile {\n absolutePath: string;\n relativePathFromSrc: string;\n outputFileName: string;\n kind: 'copy' | 'process';\n}\n\nexport interface CollectStaticAssetsArgs {\n root: string;\n pagesDir: string;\n pageExtensions: string[];\n}\n\nfunction normalizeSlashes(value: string): string {\n return value.replace(/\\\\/g, '/');\n}\n\nfunction hasAnySuffix(value: string, suffixes: string[]): boolean {\n return suffixes.some((suffix) => value.endsWith(suffix));\n}\n\nfunction shouldIgnoreFile(rel: string): boolean {\n return (\n rel.endsWith('.d.ts') ||\n rel.endsWith('.map') ||\n rel.endsWith('.tsbuildinfo') ||\n rel.startsWith('.') ||\n rel.includes('/.')\n );\n}\n\nfunction isProcessableAsset(rel: string): boolean {\n return (\n rel.endsWith('.js') ||\n rel.endsWith('.mjs') ||\n rel.endsWith('.ts') ||\n rel.endsWith('.css')\n );\n}\n\nfunction toOutputFileName(relativePathFromSrc: string): string {\n if (relativePathFromSrc.endsWith('.ts')) {\n return relativePathFromSrc.slice(0, -3) + '.js';\n }\n return relativePathFromSrc;\n}\n\nexport async function collectStaticAssets(\n args: CollectStaticAssetsArgs,\n): Promise<StaticAssetFile[]> {\n const { root, pagesDir, pageExtensions } = args;\n const srcDir = path.join(root, pagesDir);\n\n const entries = await fg('**/*', {\n cwd: srcDir,\n onlyFiles: true,\n dot: false,\n absolute: false,\n });\n\n const assets: StaticAssetFile[] = [];\n\n for (const entry of entries) {\n const rel = normalizeSlashes(entry);\n\n if (shouldIgnoreFile(rel)) continue;\n if (hasAnySuffix(rel, pageExtensions)) continue;\n\n const absolutePath = path.join(srcDir, rel);\n\n assets.push({\n absolutePath,\n relativePathFromSrc: rel,\n outputFileName: normalizeSlashes(toOutputFileName(rel)),\n kind: isProcessableAsset(rel) ? 'process' : 'copy',\n });\n }\n\n return assets;\n}\n\nexport async function copyStaticAssetSource(\n asset: StaticAssetFile,\n): Promise<Uint8Array> {\n return fs.readFile(asset.absolutePath);\n}\n\nexport async function buildProcessedStaticAssets(args: {\n root: string;\n pagesDir: string;\n assets: StaticAssetFile[];\n minify?: boolean;\n sourcemap?: boolean;\n }): Promise<Map<string, string | Uint8Array>> {\n const { root, pagesDir, assets, minify = true, sourcemap = false } = args;\n \n const processable = assets.filter((a) => a.kind === 'process');\n const out = new Map<string, string | Uint8Array>();\n \n if (processable.length === 0) {\n return out;\n }\n \n const srcDir = path.join(root, pagesDir);\n const distDir = path.join(root, 'dist');\n const warnedMissingAssets = new Set<string>();\n const result = await esbuild.build({\n entryPoints: processable.map((a) => a.absolutePath),\n absWorkingDir: root,\n outbase: srcDir,\n outdir: distDir,\n bundle: true,\n splitting: true,\n treeShaking: true,\n minify,\n sourcemap,\n format: 'esm',\n target: 'es2020',\n platform: 'browser',\n write: false,\n entryNames: '[dir]/[name]',\n assetNames: '[dir]/[name]',\n loader: {\n '.css': 'css',\n '.png': 'file',\n '.jpg': 'file',\n '.jpeg': 'file',\n '.gif': 'file',\n '.svg': 'file',\n '.webp': 'file',\n '.woff': 'file',\n '.woff2': 'file',\n '.ttf': 'file',\n '.otf': 'file',\n },\n plugins: [\n {\n name: 'html-pages-root-url-resolver',\n setup(build) {\n build.onResolve({ filter: /^\\// }, (resolveArgs) => {\n // Leave real filesystem absolute paths alone\n if (\n path.isAbsolute(resolveArgs.path) &&\n fsSync.existsSync(resolveArgs.path)\n ) {\n return { path: resolveArgs.path };\n }\n \n const cleanPath = resolveArgs.path.slice(1);\n \n const fromSrc = path.join(srcDir, cleanPath);\n if (fsSync.existsSync(fromSrc)) {\n return { path: fromSrc };\n }\n \n const fromPublic = path.join(root, 'public', cleanPath);\n if (fsSync.existsSync(fromPublic)) {\n return {\n path: resolveArgs.path,\n external: true,\n };\n }\n \n const isCssUrlToken = resolveArgs.kind === 'url-token';\n \n if (isCssUrlToken) {\n if (!warnedMissingAssets.has(resolveArgs.path)) {\n warnedMissingAssets.add(resolveArgs.path);\n console.warn(\n `[vite-plugin-html-pages] ⚠️ Missing CSS asset: ${resolveArgs.path}\\n` +\n ` Looked in:\\n` +\n ` - ${fromSrc}\\n` +\n ` - ${fromPublic}`\n );\n }\n \n // Keep the original root-relative URL in output CSS\n return {\n path: resolveArgs.path,\n external: true,\n };\n }\n \n // JS/CSS entry imports remain strict\n return {\n path: fromSrc,\n };\n });\n },\n },\n ],\n });\n \n for (const file of result.outputFiles) {\n const rel = normalizeSlashes(path.relative(distDir, file.path));\n out.set(rel, file.text ?? file.contents);\n }\n \n return out;\n }\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nexport interface HtmlAssetValidationOptions {\n root: string;\n pagesDir: string;\n html: string;\n pluginName: string;\n pageLabel?: string;\n missingAssets?: 'error' | 'warn';\n}\n\nfunction stripQueryAndHash(url: string): string {\n return url.split('#')[0].split('?')[0];\n}\n\nfunction isLocalRootUrl(url: string): boolean {\n return !!url && url.startsWith('/') && !url.startsWith('//');\n}\n\nfunction fileExistsForPublicUrl(root: string, pagesDir: string, url: string): boolean {\n const clean = stripQueryAndHash(url).slice(1);\n\n const fromSrc = path.join(root, pagesDir, clean);\n if (fs.existsSync(fromSrc)) return true;\n\n const fromPublic = path.join(root, 'public', clean);\n if (fs.existsSync(fromPublic)) return true;\n\n return false;\n}\n\nfunction collectScriptSrcs(html: string): string[] {\n const out: string[] = [];\n\n for (const match of html.matchAll(\n /<script\\b[^>]*\\bsrc=[\"']([^\"']+)[\"'][^>]*>/gi,\n )) {\n out.push(match[1]);\n }\n\n return out;\n}\n\nfunction collectStylesheetHrefs(html: string): string[] {\n const out: string[] = [];\n\n for (const match of html.matchAll(\n /<link\\b[^>]*\\brel=[\"']stylesheet[\"'][^>]*\\bhref=[\"']([^\"']+)[\"'][^>]*>/gi,\n )) {\n out.push(match[1]);\n }\n\n return out;\n}\n\nfunction collectLiteralDynamicImports(html: string): string[] {\n const out: string[] = [];\n\n for (const match of html.matchAll(\n /import\\s*\\(\\s*[\"']([^\"'`]+)[\"']\\s*\\)/gi,\n )) {\n out.push(match[1]);\n }\n\n return out;\n}\n\nfunction unique(values: string[]): string[] {\n return [...new Set(values)];\n}\n\nfunction formatPageLabel(pageLabel?: string): string {\n return pageLabel ? ` (${pageLabel})` : '';\n}\n\nfunction missingAssetMessage(args: {\n pluginName: string;\n kind: string;\n url: string;\n root: string;\n pagesDir: string;\n pageLabel?: string;\n}): string {\n const { pluginName, kind, url, root, pagesDir, pageLabel } = args;\n const clean = stripQueryAndHash(url).slice(1);\n const pageSuffix = formatPageLabel(pageLabel);\n\n return (\n `[${pluginName}] Missing ${kind}${pageSuffix}: ${url}\\n` +\n `Expected one of:\\n` +\n `- ${path.join(root, pagesDir, clean)}\\n` +\n `- ${path.join(root, 'public', clean)}`\n );\n}\n\nfunction reportMissing(args: {\n mode: 'error' | 'warn';\n pluginName: string;\n kind: string;\n url: string;\n root: string;\n pagesDir: string;\n pageLabel?: string;\n}) {\n const message = missingAssetMessage(args);\n\n if (args.mode === 'warn') {\n console.warn(`⚠️ ${message}`);\n return;\n }\n\n throw new Error(message);\n}\n\nexport function validateHtmlAssetReferences(\n options: HtmlAssetValidationOptions,\n): void {\n const {\n root,\n pagesDir,\n html,\n pluginName,\n pageLabel,\n missingAssets = 'error',\n } = options;\n\n const scriptSrcs = unique(collectScriptSrcs(html)).filter(isLocalRootUrl);\n const stylesheetHrefs = unique(collectStylesheetHrefs(html)).filter(isLocalRootUrl);\n const literalDynamicImports = unique(collectLiteralDynamicImports(html)).filter(\n isLocalRootUrl,\n );\n\n for (const url of scriptSrcs) {\n if (!fileExistsForPublicUrl(root, pagesDir, url)) {\n reportMissing({\n mode: missingAssets,\n pluginName,\n kind: 'JavaScript asset',\n url,\n root,\n pagesDir,\n pageLabel,\n });\n }\n }\n\n for (const url of stylesheetHrefs) {\n if (!fileExistsForPublicUrl(root, pagesDir, url)) {\n reportMissing({\n mode: missingAssets,\n pluginName,\n kind: 'stylesheet asset',\n url,\n root,\n pagesDir,\n pageLabel,\n });\n }\n }\n\n for (const url of literalDynamicImports) {\n if (!fileExistsForPublicUrl(root, pagesDir, url)) {\n console.warn(\n `⚠️ ${missingAssetMessage({\n pluginName,\n kind: 'literal dynamic import',\n url,\n root,\n pagesDir,\n pageLabel,\n })}`,\n );\n }\n }\n}","import type { HtPageInfo, RouteParamDefinition } from './types';\n\nfunction paramsTypeFromDefinitions(\n paramDefinitions: RouteParamDefinition[],\n): string {\n if (paramDefinitions.length === 0) {\n return '{}';\n }\n\n const fields = paramDefinitions.map((param) => {\n if (param.type === 'single') {\n return `${JSON.stringify(param.name)}: string`;\n }\n\n if (param.type === 'catch-all') {\n return `${JSON.stringify(param.name)}: string[]`;\n }\n\n return `${JSON.stringify(param.name)}?: string[]`;\n });\n\n return `{ ${fields.join('; ')} }`;\n}\n\nexport function generateTypedPageHelper(page: HtPageInfo | undefined): string {\n const paramsType = page\n ? paramsTypeFromDefinitions(page.paramDefinitions ?? [])\n : '{}';\n\n return `\nexport type PageParams = ${paramsType};\n\nexport type PageContext = {\n params: PageParams;\n data?: unknown;\n dev: boolean;\n};\n\nexport function definePage<T extends (ctx: PageContext) => any>(fn: T): T {\n return fn;\n}\n`;\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","fg","path","path","path","path","fs","path","build","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.3.4",
3
+ "version": "1.3.6",
4
4
  "author": "Paul Browne",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/constants.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export const PLUGIN_NAME = 'vite-plugin-html-pages';
2
2
  export const VIRTUAL_BUILD_ENTRY_ID = `\0${PLUGIN_NAME}:build-entry`;
3
+ export const VIRTUAL_PAGE_HELPER_ID = `${PLUGIN_NAME}/page`;
4
+ export const RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX =`\0${PLUGIN_NAME}/page:`;
3
5
  export const VIRTUAL_MANIFEST_ID = `\0virtual:${PLUGIN_NAME}-manifest`;
4
6
  export const CACHE_DIR_NAME = `node_modules/.cache/${PLUGIN_NAME}`;
package/src/dev-server.ts CHANGED
@@ -104,10 +104,15 @@ export function installDevServer(args: {
104
104
  }
105
105
 
106
106
  const html = await renderPage(page, mod, true);
107
-
107
+ const transformedHtml = await server.transformIndexHtml(
108
+ url,
109
+ html,
110
+ req.originalUrl,
111
+ );
112
+
108
113
  res.statusCode = 200;
109
114
  res.setHeader('Content-Type', 'text/html; charset=utf-8');
110
- res.end(html);
115
+ res.end(transformedHtml);
111
116
  } catch (error) {
112
117
  server.config.logger.error(
113
118
  `[${PLUGIN_NAME}] dev server render failed: ${
package/src/discover.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { normalizeFsPath, toPosix } from './path-utils';
3
- import { getParamNames, isDynamicPage, toRoutePattern } from './route-utils';
3
+ import { isDynamicPage, toRoutePattern } from './route-utils';
4
+ import { extractRouteParamDefinitions } from './route-params';
4
5
  import type { HtPageInfo, HtPagesPluginOptions } from './types';
5
6
  import { PLUGIN_NAME } from './constants';
6
7
 
@@ -64,6 +65,7 @@ export async function discoverEntryPages(
64
65
 
65
66
  const dynamic = isDynamicPage(relativeFromPagesDir);
66
67
  const routePattern = toRoutePattern(relativeFromPagesDir, pageExtensions);
68
+ const paramDefinitions = extractRouteParamDefinitions(routePattern);
67
69
 
68
70
  return {
69
71
  id: entryPath,
@@ -74,7 +76,8 @@ export async function discoverEntryPages(
74
76
  routePath: routePattern,
75
77
  fileName: '',
76
78
  dynamic,
77
- paramNames: getParamNames(relativeFromPagesDir),
79
+ paramNames: paramDefinitions.map((p) => p.name),
80
+ paramDefinitions,
78
81
  params: {},
79
82
  } satisfies HtPageInfo;
80
83
  });
package/src/env.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ declare module 'vite-plugin-html-pages/page' {
2
+ export type PageParams = Record<string, string | string[] | undefined>;
3
+
4
+ export type PageContext = {
5
+ params: PageParams;
6
+ data?: unknown;
7
+ dev: boolean;
8
+ };
9
+
10
+ export function definePage<T extends (ctx: PageContext) => any>(fn: T): T;
11
+ }
@@ -0,0 +1,43 @@
1
+ import type { HtPageInfo, RouteParamDefinition } from './types';
2
+
3
+ function paramsTypeFromDefinitions(
4
+ paramDefinitions: RouteParamDefinition[],
5
+ ): string {
6
+ if (paramDefinitions.length === 0) {
7
+ return '{}';
8
+ }
9
+
10
+ const fields = paramDefinitions.map((param) => {
11
+ if (param.type === 'single') {
12
+ return `${JSON.stringify(param.name)}: string`;
13
+ }
14
+
15
+ if (param.type === 'catch-all') {
16
+ return `${JSON.stringify(param.name)}: string[]`;
17
+ }
18
+
19
+ return `${JSON.stringify(param.name)}?: string[]`;
20
+ });
21
+
22
+ return `{ ${fields.join('; ')} }`;
23
+ }
24
+
25
+ export function generateTypedPageHelper(page: HtPageInfo | undefined): string {
26
+ const paramsType = page
27
+ ? paramsTypeFromDefinitions(page.paramDefinitions ?? [])
28
+ : '{}';
29
+
30
+ return `
31
+ export type PageParams = ${paramsType};
32
+
33
+ export type PageContext = {
34
+ params: PageParams;
35
+ data?: unknown;
36
+ dev: boolean;
37
+ };
38
+
39
+ export function definePage<T extends (ctx: PageContext) => any>(fn: T): T {
40
+ return fn;
41
+ }
42
+ `;
43
+ }
package/src/plugin.ts CHANGED
@@ -13,7 +13,13 @@ import {
13
13
  } from './static-assets';
14
14
  import { validateHtmlAssetReferences } from './html-asset-validator';
15
15
  import type { HtPageInfo, HtPageModule, HtPagesPluginOptions } from './types';
16
- import { PLUGIN_NAME, VIRTUAL_BUILD_ENTRY_ID } from './constants';
16
+ import {
17
+ PLUGIN_NAME,
18
+ VIRTUAL_BUILD_ENTRY_ID,
19
+ VIRTUAL_PAGE_HELPER_ID,
20
+ RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX,
21
+ } from './constants';
22
+ import { generateTypedPageHelper } from './page-helper-generator';
17
23
 
18
24
  import fs from 'node:fs';
19
25
  import path from 'node:path';
@@ -142,15 +148,34 @@ export function htPages(options: HtPagesPluginOptions = {}): Plugin {
142
148
  };
143
149
  },
144
150
 
145
- resolveId(id) {
151
+ resolveId(id, importer) {
146
152
  if (id === VIRTUAL_BUILD_ENTRY_ID) return id;
153
+
154
+ if (id === VIRTUAL_PAGE_HELPER_ID && importer) {
155
+ return `${RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX}${importer}`;
156
+ }
157
+
147
158
  return null;
148
159
  },
149
160
 
150
- load(id) {
161
+ async load(id) {
151
162
  if (id === VIRTUAL_BUILD_ENTRY_ID) {
152
163
  return 'export default {};';
153
164
  }
165
+
166
+ if (id.startsWith(RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX)) {
167
+ const importer = id.slice(RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX.length);
168
+ const { pages } = await buildPagesPipeline();
169
+
170
+ const normalizedImporter = path.resolve(importer);
171
+
172
+ const page = pages.find(
173
+ (candidate) => path.resolve(candidate.absolutePath) === normalizedImporter,
174
+ );
175
+
176
+ return generateTypedPageHelper(page);
177
+ }
178
+
154
179
  return null;
155
180
  },
156
181
 
@@ -215,10 +240,8 @@ export function htPages(options: HtPagesPluginOptions = {}): Plugin {
215
240
 
216
241
  await loadDevPages();
217
242
 
218
- server?.ws.send({
219
- type: 'full-reload',
220
- path: '*',
221
- });
243
+ server?.ws.send({ type: 'full-reload' });
244
+
222
245
  };
223
246
 
224
247
  server.watcher.on('add', reload);
@@ -0,0 +1,38 @@
1
+ import type { RouteParamDefinition } from './types';
2
+
3
+ export function parseRouteParamSegment(
4
+ segment: string,
5
+ ): RouteParamDefinition | null {
6
+ if (segment.startsWith('[...') && segment.endsWith(']?')) {
7
+ return {
8
+ name: segment.slice(4, -2),
9
+ type: 'optional-catch-all',
10
+ };
11
+ }
12
+
13
+ if (segment.startsWith('[...') && segment.endsWith(']')) {
14
+ return {
15
+ name: segment.slice(4, -1),
16
+ type: 'catch-all',
17
+ };
18
+ }
19
+
20
+ if (segment.startsWith('[') && segment.endsWith(']')) {
21
+ return {
22
+ name: segment.slice(1, -1),
23
+ type: 'single',
24
+ };
25
+ }
26
+
27
+ return null;
28
+ }
29
+
30
+ export function extractRouteParamDefinitions(
31
+ routePattern: string,
32
+ ): RouteParamDefinition[] {
33
+ return routePattern
34
+ .split('/')
35
+ .filter(Boolean)
36
+ .map((segment) => parseRouteParamSegment(segment))
37
+ .filter((value): value is RouteParamDefinition => value != null);
38
+ }
package/src/types.ts CHANGED
@@ -4,6 +4,8 @@ export interface StaticParamRecord {
4
4
  [key: string]: string | number | boolean;
5
5
  }
6
6
 
7
+ export type HtPageParams = Record<string, string | string[] | undefined>;
8
+
7
9
  export interface HtPageInfo {
8
10
  id: string;
9
11
  entryPath: string;
@@ -14,24 +16,32 @@ export interface HtPageInfo {
14
16
  fileName: string;
15
17
  dynamic: boolean;
16
18
  paramNames: string[];
17
- params: Record<string, string>;
19
+ paramDefinitions: RouteParamDefinition[];
20
+ params: HtPageParams;
18
21
  }
19
22
 
20
- export interface HtPageRenderContext {
23
+ export type HtPageRenderContext = {
21
24
  page: HtPageInfo;
22
- params: Record<string, string>;
25
+ params: HtPageParams;
23
26
  data?: unknown;
24
27
  dev: boolean;
25
- }
28
+ };
26
29
 
27
30
  export interface HtPageModule {
28
- default?:
29
- | string
30
- | ((ctx: HtPageRenderContext) => string | Promise<string>);
31
- data?: (ctx: HtPageRenderContext) => unknown | Promise<unknown>;
31
+ default?: ((ctx: {
32
+ page: HtPageInfo;
33
+ params: Record<string, string | string[] | undefined>;
34
+ data?: unknown;
35
+ dev: boolean;
36
+ }) => string | Promise<string>) | string;
37
+ data?: (ctx: {
38
+ page: HtPageInfo;
39
+ params: Record<string, string | string[] | undefined>;
40
+ dev: boolean;
41
+ }) => unknown | Promise<unknown>;
32
42
  generateStaticParams?: () =>
33
- | StaticParamRecord[]
34
- | Promise<StaticParamRecord[]>;
43
+ | Array<Record<string, string | number | boolean>>
44
+ | Promise<Array<Record<string, string | number | boolean>>>;
35
45
  dynamic?: boolean;
36
46
  prerender?: boolean;
37
47
  }
@@ -39,15 +49,13 @@ export interface HtPageModule {
39
49
  export interface HtPagesPluginOptions {
40
50
  root?: string;
41
51
  include?: string | string[];
42
- exclude?: string | string[];
52
+ exclude?: string | string[];
43
53
  pagesDir?: string;
44
54
  pageExtensions?: string[];
45
- renderConcurrency?: number;
46
- renderBatchSize?: number;
47
55
  cleanUrls?: boolean;
48
- ssrPlugins?: RollupPlugin[];
49
- mapOutputPath?: (page: HtPageInfo) => string;
50
56
  debug?: boolean;
57
+ renderConcurrency?: number;
58
+ renderBatchSize?: number;
51
59
  site?: string;
52
60
  missingAssets?: 'error' | 'warn';
53
61
  rss?: {
@@ -56,4 +64,10 @@ export interface HtPagesPluginOptions {
56
64
  description?: string;
57
65
  routePrefix?: string;
58
66
  };
59
- }
67
+ mapOutputPath?: (page: HtPageInfo) => string;
68
+ }
69
+
70
+ export type RouteParamDefinition = {
71
+ name: string;
72
+ type: 'single' | 'catch-all' | 'optional-catch-all';
73
+ };