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