vite-plugin-html-pages 1.2.3 → 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 +152 -240
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/plugin.ts +118 -134
- package/src/static-assets.ts +97 -0
- package/src/assets.ts +0 -236
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, "/");
|
|
@@ -126,21 +192,21 @@ function buildDefaultIncludeGlobs(pagesDir, pageExtensions) {
|
|
|
126
192
|
}
|
|
127
193
|
async function discoverEntryPages(root, options) {
|
|
128
194
|
const fgModule = await import("fast-glob");
|
|
129
|
-
const
|
|
195
|
+
const fg2 = fgModule.default ?? fgModule;
|
|
130
196
|
const pagesDir = options.pagesDir ?? "src";
|
|
131
197
|
const pageExtensions = options.pageExtensions?.length ? options.pageExtensions : [".ht.js", ".html.js", ".ht.ts", ".html.ts"];
|
|
132
198
|
const include = Array.isArray(options.include) ? options.include : options.include ? [options.include] : buildDefaultIncludeGlobs(pagesDir, pageExtensions);
|
|
133
199
|
const exclude = Array.isArray(options.exclude) ? options.exclude : options.exclude ? [options.exclude] : [];
|
|
134
|
-
const pagesRoot = normalizeFsPath(
|
|
135
|
-
const files = await
|
|
200
|
+
const pagesRoot = normalizeFsPath(path3.join(root, pagesDir));
|
|
201
|
+
const files = await fg2.glob(include, {
|
|
136
202
|
cwd: root,
|
|
137
203
|
ignore: exclude,
|
|
138
204
|
absolute: true
|
|
139
205
|
});
|
|
140
206
|
return files.sort().map((absolutePath) => {
|
|
141
207
|
const entryPath = normalizeFsPath(absolutePath);
|
|
142
|
-
const relativePath = toPosix(
|
|
143
|
-
const relativeFromPagesDir = toPosix(
|
|
208
|
+
const relativePath = toPosix(path3.relative(root, entryPath));
|
|
209
|
+
const relativeFromPagesDir = toPosix(path3.relative(pagesRoot, entryPath));
|
|
144
210
|
if (relativeFromPagesDir.startsWith("../") || relativeFromPagesDir === "..") {
|
|
145
211
|
throw new Error(
|
|
146
212
|
`[${PLUGIN_NAME}] Page is outside pagesDir: ${entryPath} (pagesDir: ${pagesDir})`
|
|
@@ -164,8 +230,8 @@ async function discoverEntryPages(root, options) {
|
|
|
164
230
|
}
|
|
165
231
|
|
|
166
232
|
// src/dev-server.ts
|
|
167
|
-
import
|
|
168
|
-
import
|
|
233
|
+
import fs2 from "fs";
|
|
234
|
+
import path5 from "path";
|
|
169
235
|
|
|
170
236
|
// src/errors.ts
|
|
171
237
|
function invalidHtmlReturn(page, value) {
|
|
@@ -218,7 +284,7 @@ async function renderPage(page, mod, dev = false) {
|
|
|
218
284
|
}
|
|
219
285
|
|
|
220
286
|
// src/module-loader.ts
|
|
221
|
-
import
|
|
287
|
+
import path4 from "path";
|
|
222
288
|
import { createServer } from "vite";
|
|
223
289
|
var buildServer = null;
|
|
224
290
|
async function createPageModuleLoader(args) {
|
|
@@ -245,7 +311,7 @@ async function createPageModuleLoader(args) {
|
|
|
245
311
|
buildServer = await createServer(config);
|
|
246
312
|
}
|
|
247
313
|
return async (entryPath) => {
|
|
248
|
-
const relativePath = "/" +
|
|
314
|
+
const relativePath = "/" + path4.relative(root, entryPath).replace(/\\/g, "/");
|
|
249
315
|
const mod = await buildServer.ssrLoadModule(relativePath);
|
|
250
316
|
return mod;
|
|
251
317
|
};
|
|
@@ -269,8 +335,8 @@ function tryRewriteRootAssetToSrc(server, url) {
|
|
|
269
335
|
if (!isStaticAssetRequest(url)) return null;
|
|
270
336
|
if (url.startsWith("/src/")) return null;
|
|
271
337
|
const root = server.config.root;
|
|
272
|
-
const candidate =
|
|
273
|
-
if (
|
|
338
|
+
const candidate = path5.join(root, "src", url.slice(1));
|
|
339
|
+
if (fs2.existsSync(candidate)) {
|
|
274
340
|
return `/src/${url.slice(1)}`;
|
|
275
341
|
}
|
|
276
342
|
return null;
|
|
@@ -368,149 +434,6 @@ async function buildPageIndex(args) {
|
|
|
368
434
|
return pages;
|
|
369
435
|
}
|
|
370
436
|
|
|
371
|
-
// src/assets.ts
|
|
372
|
-
import fs2 from "fs";
|
|
373
|
-
import path5 from "path";
|
|
374
|
-
var EXTERNAL_URL_RE = /^(?:[a-z]+:)?\/\//i;
|
|
375
|
-
function isLocalAssetUrl(url) {
|
|
376
|
-
return !!url && !url.startsWith("data:") && !url.startsWith("mailto:") && !url.startsWith("tel:") && !url.startsWith("#") && !EXTERNAL_URL_RE.test(url);
|
|
377
|
-
}
|
|
378
|
-
function stripQueryAndHash(url) {
|
|
379
|
-
return url.split("#")[0].split("?")[0];
|
|
380
|
-
}
|
|
381
|
-
function extractHtmlAssets(html) {
|
|
382
|
-
const assets = [];
|
|
383
|
-
for (const match of html.matchAll(
|
|
384
|
-
/<link\b[^>]*\brel=["']stylesheet["'][^>]*\bhref=["']([^"']+)["'][^>]*>/gi
|
|
385
|
-
)) {
|
|
386
|
-
assets.push({ kind: "css", url: match[1] });
|
|
387
|
-
}
|
|
388
|
-
for (const match of html.matchAll(
|
|
389
|
-
/<script\b[^>]*\bsrc=["']([^"']+)["'][^>]*>/gi
|
|
390
|
-
)) {
|
|
391
|
-
assets.push({ kind: "js", url: match[1] });
|
|
392
|
-
}
|
|
393
|
-
return dedupeExtractedAssets(assets);
|
|
394
|
-
}
|
|
395
|
-
function dedupeExtractedAssets(assets) {
|
|
396
|
-
const seen = /* @__PURE__ */ new Set();
|
|
397
|
-
const out = [];
|
|
398
|
-
for (const asset of assets) {
|
|
399
|
-
const key = `${asset.kind}:${asset.url}`;
|
|
400
|
-
if (seen.has(key)) continue;
|
|
401
|
-
seen.add(key);
|
|
402
|
-
out.push(asset);
|
|
403
|
-
}
|
|
404
|
-
return out;
|
|
405
|
-
}
|
|
406
|
-
function resolveLocalAssetPath(args) {
|
|
407
|
-
const { root, pagesDir, pageDir, url } = args;
|
|
408
|
-
if (!isLocalAssetUrl(url)) return null;
|
|
409
|
-
const cleanUrl = stripQueryAndHash(url);
|
|
410
|
-
let abs;
|
|
411
|
-
if (cleanUrl.startsWith("/")) {
|
|
412
|
-
abs = path5.join(root, pagesDir, cleanUrl.slice(1));
|
|
413
|
-
} else if (cleanUrl.startsWith(`${pagesDir}/`)) {
|
|
414
|
-
abs = path5.join(root, cleanUrl);
|
|
415
|
-
} else {
|
|
416
|
-
const baseDir = pageDir ?? path5.join(root, pagesDir);
|
|
417
|
-
abs = path5.resolve(baseDir, cleanUrl);
|
|
418
|
-
}
|
|
419
|
-
return fs2.existsSync(abs) ? abs : null;
|
|
420
|
-
}
|
|
421
|
-
function emitHtmlAsset(args) {
|
|
422
|
-
const { ctx, kind, absolutePath } = args;
|
|
423
|
-
if (kind === "css" || kind === "js") {
|
|
424
|
-
return ctx.emitFile({
|
|
425
|
-
type: "chunk",
|
|
426
|
-
id: absolutePath,
|
|
427
|
-
name: path5.basename(absolutePath, path5.extname(absolutePath))
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
throw new Error(`[vite-plugin-html-pages] Unsupported asset kind: ${kind}`);
|
|
431
|
-
}
|
|
432
|
-
function replaceAllLiteral(input, search, replacement) {
|
|
433
|
-
return input.split(search).join(replacement);
|
|
434
|
-
}
|
|
435
|
-
function rewriteHtmlAssetUrls(html, replacements) {
|
|
436
|
-
let out = html;
|
|
437
|
-
for (const [originalUrl, builtUrl] of replacements) {
|
|
438
|
-
out = replaceAllLiteral(
|
|
439
|
-
out,
|
|
440
|
-
`href="${originalUrl}"`,
|
|
441
|
-
`href="${builtUrl}"`
|
|
442
|
-
);
|
|
443
|
-
out = replaceAllLiteral(
|
|
444
|
-
out,
|
|
445
|
-
`href='${originalUrl}'`,
|
|
446
|
-
`href='${builtUrl}'`
|
|
447
|
-
);
|
|
448
|
-
out = replaceAllLiteral(
|
|
449
|
-
out,
|
|
450
|
-
`src="${originalUrl}"`,
|
|
451
|
-
`src="${builtUrl}"`
|
|
452
|
-
);
|
|
453
|
-
out = replaceAllLiteral(
|
|
454
|
-
out,
|
|
455
|
-
`src='${originalUrl}'`,
|
|
456
|
-
`src='${builtUrl}'`
|
|
457
|
-
);
|
|
458
|
-
}
|
|
459
|
-
return out;
|
|
460
|
-
}
|
|
461
|
-
async function collectHtmlAssetRefs(args) {
|
|
462
|
-
const { ctx, root, pagesDir, htmlByPageKey } = args;
|
|
463
|
-
const refs = /* @__PURE__ */ new Map();
|
|
464
|
-
for (const { html, pageDir } of htmlByPageKey.values()) {
|
|
465
|
-
const assets = extractHtmlAssets(html);
|
|
466
|
-
for (const asset of assets) {
|
|
467
|
-
const abs = resolveLocalAssetPath({
|
|
468
|
-
root,
|
|
469
|
-
pagesDir,
|
|
470
|
-
pageDir,
|
|
471
|
-
url: asset.url
|
|
472
|
-
});
|
|
473
|
-
if (!abs) continue;
|
|
474
|
-
const key = `${asset.kind}:${asset.url}`;
|
|
475
|
-
if (refs.has(key)) continue;
|
|
476
|
-
const refId = emitHtmlAsset({
|
|
477
|
-
ctx,
|
|
478
|
-
kind: asset.kind,
|
|
479
|
-
absolutePath: abs
|
|
480
|
-
});
|
|
481
|
-
refs.set(key, {
|
|
482
|
-
kind: asset.kind,
|
|
483
|
-
originalUrl: asset.url,
|
|
484
|
-
absolutePath: abs,
|
|
485
|
-
refId
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
return refs;
|
|
490
|
-
}
|
|
491
|
-
function buildHtmlAssetReplacementMap(args) {
|
|
492
|
-
const { ctx, refs, bundle } = args;
|
|
493
|
-
const replacements = /* @__PURE__ */ new Map();
|
|
494
|
-
for (const ref of refs.values()) {
|
|
495
|
-
if (ref.kind === "js") {
|
|
496
|
-
const fileName = ctx.getFileName(ref.refId);
|
|
497
|
-
replacements.set(ref.originalUrl, `/${fileName}`);
|
|
498
|
-
continue;
|
|
499
|
-
}
|
|
500
|
-
if (ref.kind === "css") {
|
|
501
|
-
const jsEntryFile = ctx.getFileName(ref.refId);
|
|
502
|
-
const jsChunk = bundle[jsEntryFile];
|
|
503
|
-
if (jsChunk && jsChunk.type === "chunk" && "viteMetadata" in jsChunk && jsChunk.viteMetadata?.importedCss && jsChunk.viteMetadata.importedCss.size > 0) {
|
|
504
|
-
const cssFile = [...jsChunk.viteMetadata.importedCss][0];
|
|
505
|
-
replacements.set(ref.originalUrl, `/${cssFile}`);
|
|
506
|
-
continue;
|
|
507
|
-
}
|
|
508
|
-
replacements.set(ref.originalUrl, `/${jsEntryFile}`);
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
return replacements;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
437
|
// src/plugin.ts
|
|
515
438
|
import fs3 from "fs";
|
|
516
439
|
import path6 from "path";
|
|
@@ -539,9 +462,9 @@ function htPages(options = {}) {
|
|
|
539
462
|
let root = process.cwd();
|
|
540
463
|
let server = null;
|
|
541
464
|
let devPages = [];
|
|
542
|
-
let htmlAssetRefs = /* @__PURE__ */ new Map();
|
|
543
465
|
const cleanUrls = options.cleanUrls ?? true;
|
|
544
466
|
const pagesDir = options.pagesDir ?? "src";
|
|
467
|
+
const pageExtensions = options.pageExtensions?.length ? options.pageExtensions : [".ht.js", ".html.js", ".ht.ts", ".html.ts"];
|
|
545
468
|
function logDebug(enabled, ...args) {
|
|
546
469
|
if (!enabled) return;
|
|
547
470
|
console.log(`[${PLUGIN_NAME}]`, ...args);
|
|
@@ -630,38 +553,21 @@ function htPages(options = {}) {
|
|
|
630
553
|
for (const entry of entries) {
|
|
631
554
|
this.addWatchFile(entry.entryPath);
|
|
632
555
|
}
|
|
633
|
-
|
|
634
|
-
return;
|
|
635
|
-
}
|
|
636
|
-
htmlAssetRefs.clear();
|
|
637
|
-
const { modulesByEntry, pages } = await buildPagesPipeline();
|
|
638
|
-
const htmlByPageKey = /* @__PURE__ */ new Map();
|
|
639
|
-
for (const page of pages) {
|
|
640
|
-
const mod = modulesByEntry.get(page.entryPath);
|
|
641
|
-
if (!mod) {
|
|
642
|
-
throw new Error(
|
|
643
|
-
`[${PLUGIN_NAME}] Missing module for page entry: ${page.entryPath}`
|
|
644
|
-
);
|
|
645
|
-
}
|
|
646
|
-
const html = await renderPage(page, mod, false);
|
|
647
|
-
htmlByPageKey.set(page.entryPath, {
|
|
648
|
-
html,
|
|
649
|
-
pageDir: path6.dirname(page.absolutePath)
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
htmlAssetRefs = await collectHtmlAssetRefs({
|
|
653
|
-
ctx: this,
|
|
556
|
+
const staticAssets = await collectStaticAssets({
|
|
654
557
|
root,
|
|
655
558
|
pagesDir,
|
|
656
|
-
|
|
559
|
+
pageExtensions
|
|
657
560
|
});
|
|
561
|
+
for (const asset of staticAssets) {
|
|
562
|
+
this.addWatchFile(asset.absolutePath);
|
|
563
|
+
}
|
|
658
564
|
logDebug(
|
|
659
565
|
options.debug,
|
|
660
|
-
"
|
|
661
|
-
|
|
662
|
-
kind:
|
|
663
|
-
|
|
664
|
-
|
|
566
|
+
"static assets",
|
|
567
|
+
staticAssets.map((asset) => ({
|
|
568
|
+
kind: asset.kind,
|
|
569
|
+
input: asset.relativePathFromSrc,
|
|
570
|
+
output: asset.outputFileName
|
|
665
571
|
}))
|
|
666
572
|
);
|
|
667
573
|
},
|
|
@@ -690,23 +596,31 @@ function htPages(options = {}) {
|
|
|
690
596
|
async generateBundle(_, bundle) {
|
|
691
597
|
try {
|
|
692
598
|
const { modulesByEntry, pages } = await buildPagesPipeline();
|
|
693
|
-
const
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
599
|
+
const staticAssets = await collectStaticAssets({
|
|
600
|
+
root,
|
|
601
|
+
pagesDir,
|
|
602
|
+
pageExtensions
|
|
697
603
|
});
|
|
698
604
|
logDebug(
|
|
699
605
|
options.debug,
|
|
700
|
-
"
|
|
701
|
-
|
|
606
|
+
"emitting pages",
|
|
607
|
+
pages.map((p) => p.fileName)
|
|
702
608
|
);
|
|
703
609
|
logDebug(
|
|
704
610
|
options.debug,
|
|
705
|
-
"emitting
|
|
706
|
-
|
|
611
|
+
"emitting static assets",
|
|
612
|
+
staticAssets.map((asset) => asset.outputFileName)
|
|
707
613
|
);
|
|
708
614
|
const limit = pLimit(options.renderConcurrency ?? 8);
|
|
709
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
|
+
}
|
|
710
624
|
for (const batch of chunkArray(pages, batchSize)) {
|
|
711
625
|
await Promise.all(
|
|
712
626
|
batch.map(
|
|
@@ -717,8 +631,7 @@ function htPages(options = {}) {
|
|
|
717
631
|
`[${PLUGIN_NAME}] Missing module for page entry: ${page.entryPath}`
|
|
718
632
|
);
|
|
719
633
|
}
|
|
720
|
-
|
|
721
|
-
html = rewriteHtmlAssetUrls(html, assetReplacements);
|
|
634
|
+
const html = await renderPage(page, mod, false);
|
|
722
635
|
this.emitFile({
|
|
723
636
|
type: "asset",
|
|
724
637
|
fileName: options.mapOutputPath?.(page) ?? page.fileName,
|
|
@@ -736,8 +649,7 @@ function htPages(options = {}) {
|
|
|
736
649
|
`[${PLUGIN_NAME}] Missing module for 404 page entry: ${notFoundPage.entryPath}`
|
|
737
650
|
);
|
|
738
651
|
}
|
|
739
|
-
|
|
740
|
-
html = rewriteHtmlAssetUrls(html, assetReplacements);
|
|
652
|
+
const html = await renderPage(notFoundPage, mod, false);
|
|
741
653
|
this.emitFile({
|
|
742
654
|
type: "asset",
|
|
743
655
|
fileName: "404.html",
|
|
@@ -746,49 +658,49 @@ function htPages(options = {}) {
|
|
|
746
658
|
logDebug(options.debug, "generated 404.html from user page");
|
|
747
659
|
} else {
|
|
748
660
|
const default404 = `<!doctype html>
|
|
749
|
-
<html lang="en">
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
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
|
-
</html>
|
|
791
|
-
`;
|
|
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
|
+
`;
|
|
792
704
|
this.emitFile({
|
|
793
705
|
type: "asset",
|
|
794
706
|
fileName: "404.html",
|