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