vite-plugin-html-pages 2.0.6 → 2.0.8
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 +506 -370
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -9,6 +9,74 @@ import pLimit from "p-limit";
|
|
|
9
9
|
import fs from "fs/promises";
|
|
10
10
|
import path2 from "path";
|
|
11
11
|
|
|
12
|
+
// src/page-helper-generator.ts
|
|
13
|
+
function paramsTypeFromDefinitions(paramDefinitions) {
|
|
14
|
+
if (paramDefinitions.length === 0) {
|
|
15
|
+
return "{}";
|
|
16
|
+
}
|
|
17
|
+
const fields = paramDefinitions.map((param) => {
|
|
18
|
+
if (param.type === "single") {
|
|
19
|
+
return `${JSON.stringify(param.name)}: string`;
|
|
20
|
+
}
|
|
21
|
+
if (param.type === "catch-all") {
|
|
22
|
+
return `${JSON.stringify(param.name)}: string[]`;
|
|
23
|
+
}
|
|
24
|
+
return `${JSON.stringify(param.name)}?: string[]`;
|
|
25
|
+
});
|
|
26
|
+
return `{ ${fields.join("; ")} }`;
|
|
27
|
+
}
|
|
28
|
+
function generateTypedPageHelper(page) {
|
|
29
|
+
const paramsType = page ? paramsTypeFromDefinitions(page.paramDefinitions ?? []) : "{}";
|
|
30
|
+
return `
|
|
31
|
+
export type PageParams = ${paramsType};
|
|
32
|
+
|
|
33
|
+
export type StaticParams = PageParams[];
|
|
34
|
+
|
|
35
|
+
export type DataContext = {
|
|
36
|
+
params: PageParams;
|
|
37
|
+
dev: boolean;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type RenderContext<TData = unknown> = {
|
|
41
|
+
params: PageParams;
|
|
42
|
+
data: TData;
|
|
43
|
+
dev: boolean;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type PageContext<TData = unknown> = {
|
|
47
|
+
params: PageParams;
|
|
48
|
+
data?: TData;
|
|
49
|
+
dev: boolean;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type PageModule<TData = unknown> = {
|
|
53
|
+
generateStaticParams?: () => StaticParams | Promise<StaticParams>;
|
|
54
|
+
data?: (ctx: DataContext) => TData | Promise<TData>;
|
|
55
|
+
render: (ctx: RenderContext<TData>) => any;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export function definePage<T extends (ctx: PageContext) => any>(fn: T): T {
|
|
59
|
+
return fn;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function defineData<T extends (ctx: DataContext) => any>(fn: T): T {
|
|
63
|
+
return fn;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function defineStaticParams<
|
|
67
|
+
T extends () => StaticParams | Promise<StaticParams>
|
|
68
|
+
>(fn: T): T {
|
|
69
|
+
return fn;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function definePageModule<TData>(
|
|
73
|
+
mod: PageModule<TData>,
|
|
74
|
+
): PageModule<TData> {
|
|
75
|
+
return mod;
|
|
76
|
+
}
|
|
77
|
+
`;
|
|
78
|
+
}
|
|
79
|
+
|
|
12
80
|
// src/path-utils.ts
|
|
13
81
|
import path from "path";
|
|
14
82
|
function toPosix(value) {
|
|
@@ -31,68 +99,53 @@ function stripPageSuffix(filePath, extensions) {
|
|
|
31
99
|
}
|
|
32
100
|
|
|
33
101
|
// src/typegen.ts
|
|
34
|
-
function paramsTypeFromDefinitions(paramDefinitions) {
|
|
35
|
-
if (paramDefinitions.length === 0) {
|
|
36
|
-
return "{}";
|
|
37
|
-
}
|
|
38
|
-
const fields = paramDefinitions.map((param) => {
|
|
39
|
-
if (param.type === "single") {
|
|
40
|
-
return `${param.name}: string`;
|
|
41
|
-
}
|
|
42
|
-
if (param.type === "catch-all") {
|
|
43
|
-
return `${param.name}: string[]`;
|
|
44
|
-
}
|
|
45
|
-
return `${param.name}?: string[]`;
|
|
46
|
-
});
|
|
47
|
-
return `{ ${fields.join("; ")} }`;
|
|
48
|
-
}
|
|
49
102
|
function pageHelperModuleSource(page) {
|
|
50
103
|
const paramsType = paramsTypeFromDefinitions(page.paramDefinitions ?? []);
|
|
51
104
|
return `export type PageParams = ${paramsType};
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
105
|
+
|
|
106
|
+
export type StaticParams = PageParams[];
|
|
107
|
+
|
|
108
|
+
export type DataContext = {
|
|
109
|
+
params: PageParams;
|
|
110
|
+
dev: boolean;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export type RenderContext<TData = unknown> = {
|
|
114
|
+
params: PageParams;
|
|
115
|
+
data: TData;
|
|
116
|
+
dev: boolean;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export type PageContext<TData = unknown> = {
|
|
120
|
+
params: PageParams;
|
|
121
|
+
data?: TData;
|
|
122
|
+
dev: boolean;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export type RenderResult = unknown;
|
|
126
|
+
|
|
127
|
+
export type PageModule<TData = unknown> = {
|
|
128
|
+
generateStaticParams?: () => StaticParams | Promise<StaticParams>;
|
|
129
|
+
data?: (ctx: DataContext) => TData | Promise<TData>;
|
|
130
|
+
render: (ctx: RenderContext<TData>) => RenderResult | Promise<RenderResult>;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export declare function definePage<
|
|
134
|
+
T extends (ctx: PageContext) => RenderResult | Promise<RenderResult>
|
|
135
|
+
>(fn: T): T;
|
|
136
|
+
|
|
137
|
+
export declare function defineData<
|
|
138
|
+
T extends (ctx: DataContext) => unknown | Promise<unknown>
|
|
139
|
+
>(fn: T): T;
|
|
140
|
+
|
|
141
|
+
export declare function defineStaticParams<
|
|
142
|
+
T extends () => StaticParams | Promise<StaticParams>
|
|
143
|
+
>(fn: T): T;
|
|
144
|
+
|
|
145
|
+
export declare function definePageModule<TData>(
|
|
146
|
+
mod: PageModule<TData>
|
|
147
|
+
): PageModule<TData>;
|
|
148
|
+
`;
|
|
96
149
|
}
|
|
97
150
|
function stripPageExtension(filePath) {
|
|
98
151
|
return filePath.replace(/\.(ht|html)\.(js|ts|jsx|tsx)$/i, "");
|
|
@@ -321,7 +374,7 @@ function formatDevPageError(args) {
|
|
|
321
374
|
const location = extractedLocation ? refineReferenceErrorLocation(error, extractedLocation) : null;
|
|
322
375
|
const sourceFrame = location ? createSourceFrame(location) : null;
|
|
323
376
|
const heading = phase === "load" ? "PAGE LOAD ERROR" : "PAGE RELOAD ERROR";
|
|
324
|
-
const fileLabel = location ? ` ${path3.relative(root, location.file)}` : "";
|
|
377
|
+
const fileLabel = location ? ` ${path3.relative(root, location.file)}:${location.line}:${location.column}` : "";
|
|
325
378
|
const separatorLength = Math.max(
|
|
326
379
|
12,
|
|
327
380
|
64 - heading.length - fileLabel.length
|
|
@@ -354,8 +407,17 @@ var PLUGIN_NAME = "vite-plugin-html-pages";
|
|
|
354
407
|
var VIRTUAL_BUILD_ENTRY_ID = `\0${PLUGIN_NAME}:build-entry`;
|
|
355
408
|
var VIRTUAL_PAGE_HELPER_ID = `${PLUGIN_NAME}/page`;
|
|
356
409
|
var RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX = `\0${PLUGIN_NAME}/page:`;
|
|
357
|
-
var VIRTUAL_MANIFEST_ID = `\0virtual:${PLUGIN_NAME}-manifest`;
|
|
358
410
|
var CACHE_DIR_NAME = `node_modules/.cache/${PLUGIN_NAME}`;
|
|
411
|
+
var DEFAULT_PAGE_EXTENSIONS = [
|
|
412
|
+
".ht.js",
|
|
413
|
+
".html.js",
|
|
414
|
+
".ht.ts",
|
|
415
|
+
".html.ts",
|
|
416
|
+
".ht.jsx",
|
|
417
|
+
".html.jsx",
|
|
418
|
+
".ht.tsx",
|
|
419
|
+
".html.tsx"
|
|
420
|
+
];
|
|
359
421
|
var VIRTUAL_JSX_RUNTIME_ID = `${PLUGIN_NAME}/jsx-runtime`;
|
|
360
422
|
var VIRTUAL_JSX_DEV_RUNTIME_ID = `${PLUGIN_NAME}/jsx-dev-runtime`;
|
|
361
423
|
var RESOLVED_VIRTUAL_JSX_RUNTIME_ID = `\0${VIRTUAL_JSX_RUNTIME_ID}`;
|
|
@@ -515,7 +577,7 @@ async function discoverEntryPages(root, options) {
|
|
|
515
577
|
const fgModule = await import("fast-glob");
|
|
516
578
|
const fg2 = fgModule.default ?? fgModule;
|
|
517
579
|
const pagesDir = options.pagesDir ?? "src";
|
|
518
|
-
const pageExtensions = options.pageExtensions?.length ? options.pageExtensions :
|
|
580
|
+
const pageExtensions = options.pageExtensions?.length ? options.pageExtensions : DEFAULT_PAGE_EXTENSIONS;
|
|
519
581
|
const include = Array.isArray(options.include) ? options.include : options.include ? [options.include] : buildDefaultIncludeGlobs(pagesDir, pageExtensions);
|
|
520
582
|
const exclude = Array.isArray(options.exclude) ? options.exclude : options.exclude ? [options.exclude] : [];
|
|
521
583
|
const pagesRoot = normalizeFsPath(path4.join(root, pagesDir));
|
|
@@ -700,6 +762,9 @@ async function renderPage(page, mod, dev = false) {
|
|
|
700
762
|
ctx.data = await mod.data(ctx);
|
|
701
763
|
}
|
|
702
764
|
const result = await resolveRenderResult(page, mod, ctx);
|
|
765
|
+
if (result == null) {
|
|
766
|
+
throw invalidHtmlReturn(page, result);
|
|
767
|
+
}
|
|
703
768
|
if (typeof result === "string") {
|
|
704
769
|
return ensureDoctype(result);
|
|
705
770
|
}
|
|
@@ -727,79 +792,9 @@ async function renderPage(page, mod, dev = false) {
|
|
|
727
792
|
import path5 from "path";
|
|
728
793
|
import {
|
|
729
794
|
createServer,
|
|
730
|
-
isRunnableDevEnvironment
|
|
795
|
+
isRunnableDevEnvironment,
|
|
796
|
+
loadConfigFromFile
|
|
731
797
|
} from "vite";
|
|
732
|
-
|
|
733
|
-
// src/page-helper-generator.ts
|
|
734
|
-
function paramsTypeFromDefinitions2(paramDefinitions) {
|
|
735
|
-
if (paramDefinitions.length === 0) {
|
|
736
|
-
return "{}";
|
|
737
|
-
}
|
|
738
|
-
const fields = paramDefinitions.map((param) => {
|
|
739
|
-
if (param.type === "single") {
|
|
740
|
-
return `${JSON.stringify(param.name)}: string`;
|
|
741
|
-
}
|
|
742
|
-
if (param.type === "catch-all") {
|
|
743
|
-
return `${JSON.stringify(param.name)}: string[]`;
|
|
744
|
-
}
|
|
745
|
-
return `${JSON.stringify(param.name)}?: string[]`;
|
|
746
|
-
});
|
|
747
|
-
return `{ ${fields.join("; ")} }`;
|
|
748
|
-
}
|
|
749
|
-
function generateTypedPageHelper(page) {
|
|
750
|
-
const paramsType = page ? paramsTypeFromDefinitions2(page.paramDefinitions ?? []) : "{}";
|
|
751
|
-
return `
|
|
752
|
-
export type PageParams = ${paramsType};
|
|
753
|
-
|
|
754
|
-
export type StaticParams = PageParams[];
|
|
755
|
-
|
|
756
|
-
export type DataContext = {
|
|
757
|
-
params: PageParams;
|
|
758
|
-
dev: boolean;
|
|
759
|
-
};
|
|
760
|
-
|
|
761
|
-
export type RenderContext<TData = unknown> = {
|
|
762
|
-
params: PageParams;
|
|
763
|
-
data: TData;
|
|
764
|
-
dev: boolean;
|
|
765
|
-
};
|
|
766
|
-
|
|
767
|
-
export type PageContext<TData = unknown> = {
|
|
768
|
-
params: PageParams;
|
|
769
|
-
data?: TData;
|
|
770
|
-
dev: boolean;
|
|
771
|
-
};
|
|
772
|
-
|
|
773
|
-
export type PageModule<TData = unknown> = {
|
|
774
|
-
generateStaticParams?: () => StaticParams | Promise<StaticParams>;
|
|
775
|
-
data?: (ctx: DataContext) => TData | Promise<TData>;
|
|
776
|
-
render: (ctx: RenderContext<TData>) => any;
|
|
777
|
-
};
|
|
778
|
-
|
|
779
|
-
export function definePage<T extends (ctx: PageContext) => any>(fn: T): T {
|
|
780
|
-
return fn;
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
export function defineData<T extends (ctx: DataContext) => any>(fn: T): T {
|
|
784
|
-
return fn;
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
export function defineStaticParams<
|
|
788
|
-
T extends () => StaticParams | Promise<StaticParams>
|
|
789
|
-
>(fn: T): T {
|
|
790
|
-
return fn;
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
export function definePageModule<TData>(
|
|
794
|
-
mod: PageModule<TData>,
|
|
795
|
-
): PageModule<TData> {
|
|
796
|
-
return mod;
|
|
797
|
-
}
|
|
798
|
-
`;
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
// src/module-loader.ts
|
|
802
|
-
var buildServer = null;
|
|
803
798
|
async function importPageModule(server, url) {
|
|
804
799
|
const environment = server.environments.ssr;
|
|
805
800
|
if (!isRunnableDevEnvironment(environment)) {
|
|
@@ -810,15 +805,12 @@ async function importPageModule(server, url) {
|
|
|
810
805
|
const mod = await environment.runner.import(url);
|
|
811
806
|
return normalizeLoadedPageModule(mod);
|
|
812
807
|
}
|
|
813
|
-
function isStructuredPageModule2(value) {
|
|
814
|
-
return !!value && typeof value === "object" && "render" in value && typeof value.render === "function";
|
|
815
|
-
}
|
|
816
808
|
function isLocalPageTypesImport(id) {
|
|
817
809
|
return /^\.\/\$types(?:\.[A-Za-z0-9_.-]+)?$/.test(id);
|
|
818
810
|
}
|
|
819
811
|
function normalizeLoadedPageModule(mod) {
|
|
820
812
|
const pageModule = mod ?? {};
|
|
821
|
-
if (
|
|
813
|
+
if (isStructuredPageModule(pageModule.default)) {
|
|
822
814
|
const structured = pageModule.default;
|
|
823
815
|
return {
|
|
824
816
|
default: structured.render,
|
|
@@ -830,83 +822,125 @@ function normalizeLoadedPageModule(mod) {
|
|
|
830
822
|
}
|
|
831
823
|
return pageModule;
|
|
832
824
|
}
|
|
825
|
+
async function flattenPluginOptions(option) {
|
|
826
|
+
const resolved = await option;
|
|
827
|
+
if (!resolved) return [];
|
|
828
|
+
if (Array.isArray(resolved)) {
|
|
829
|
+
const nested = await Promise.all(
|
|
830
|
+
resolved.map((entry) => flattenPluginOptions(entry))
|
|
831
|
+
);
|
|
832
|
+
return nested.flat();
|
|
833
|
+
}
|
|
834
|
+
return [resolved];
|
|
835
|
+
}
|
|
836
|
+
async function loadUserConfig(args) {
|
|
837
|
+
if (!args.configFile) {
|
|
838
|
+
return { userConfig: {}, userPlugins: [] };
|
|
839
|
+
}
|
|
840
|
+
const loaded = await loadConfigFromFile(
|
|
841
|
+
{ command: "serve", mode: args.mode },
|
|
842
|
+
args.configFile,
|
|
843
|
+
args.root
|
|
844
|
+
);
|
|
845
|
+
if (!loaded) {
|
|
846
|
+
return { userConfig: {}, userPlugins: [] };
|
|
847
|
+
}
|
|
848
|
+
const plugins = await flattenPluginOptions(loaded.config.plugins);
|
|
849
|
+
return {
|
|
850
|
+
userConfig: loaded.config,
|
|
851
|
+
// Filter ourselves out so evaluating page modules does not
|
|
852
|
+
// recursively spawn more build loaders.
|
|
853
|
+
userPlugins: plugins.filter((plugin) => plugin.name !== PLUGIN_NAME)
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
function createPageHelperPlugin(getPages) {
|
|
857
|
+
return {
|
|
858
|
+
name: `${PLUGIN_NAME}:page-helper`,
|
|
859
|
+
resolveId(id, importer) {
|
|
860
|
+
if (id === VIRTUAL_PAGE_HELPER_ID && importer) {
|
|
861
|
+
return `${RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX}${importer}`;
|
|
862
|
+
}
|
|
863
|
+
if (importer && isLocalPageTypesImport(id)) {
|
|
864
|
+
return `${VIRTUAL_LOCAL_TYPES_PREFIX}${importer}::${id}`;
|
|
865
|
+
}
|
|
866
|
+
return null;
|
|
867
|
+
},
|
|
868
|
+
async load(id) {
|
|
869
|
+
if (id.startsWith(VIRTUAL_LOCAL_TYPES_PREFIX)) {
|
|
870
|
+
return `
|
|
871
|
+
export {
|
|
872
|
+
definePage,
|
|
873
|
+
defineData,
|
|
874
|
+
defineStaticParams,
|
|
875
|
+
definePageModule
|
|
876
|
+
} from 'vite-plugin-html-pages/page';
|
|
877
|
+
`;
|
|
878
|
+
}
|
|
879
|
+
if (!id.startsWith(RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX)) {
|
|
880
|
+
return null;
|
|
881
|
+
}
|
|
882
|
+
const importer = id.slice(RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX.length);
|
|
883
|
+
const pages = await getPages();
|
|
884
|
+
const normalizedImporter = path5.resolve(importer);
|
|
885
|
+
const page = pages.find(
|
|
886
|
+
(candidate) => path5.resolve(candidate.absolutePath) === normalizedImporter
|
|
887
|
+
);
|
|
888
|
+
return generateTypedPageHelper(page);
|
|
889
|
+
}
|
|
890
|
+
};
|
|
891
|
+
}
|
|
833
892
|
async function createPageModuleLoader(args) {
|
|
834
893
|
const { mode, root, server, getPages } = args;
|
|
835
894
|
if (mode === "dev") {
|
|
836
895
|
if (!server) {
|
|
837
896
|
throw new Error("[vite-plugin-html-pages] dev server not available");
|
|
838
897
|
}
|
|
839
|
-
return
|
|
898
|
+
return {
|
|
899
|
+
loadModule: async (_entryPath, relativePath) => importPageModule(server, `/${relativePath}`),
|
|
900
|
+
close: async () => {
|
|
901
|
+
}
|
|
902
|
+
};
|
|
840
903
|
}
|
|
841
904
|
if (!getPages) {
|
|
842
905
|
throw new Error(
|
|
843
906
|
"[vite-plugin-html-pages] getPages is required in build mode"
|
|
844
907
|
);
|
|
845
908
|
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
}
|
|
880
|
-
`;
|
|
881
|
-
}
|
|
882
|
-
if (!id.startsWith(RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX)) {
|
|
883
|
-
return null;
|
|
884
|
-
}
|
|
885
|
-
const importer = id.slice(
|
|
886
|
-
RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX.length
|
|
887
|
-
);
|
|
888
|
-
const pages = await getPages();
|
|
889
|
-
const normalizedImporter = path5.resolve(importer);
|
|
890
|
-
const page = pages.find(
|
|
891
|
-
(candidate) => path5.resolve(candidate.absolutePath) === normalizedImporter
|
|
892
|
-
);
|
|
893
|
-
return generateTypedPageHelper(page);
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
]
|
|
897
|
-
};
|
|
898
|
-
buildServer = await createServer(config);
|
|
899
|
-
}
|
|
900
|
-
return async (entryPath) => {
|
|
901
|
-
const relativePath = "/" + path5.relative(root, entryPath).replace(/\\/g, "/");
|
|
902
|
-
return importPageModule(buildServer, relativePath);
|
|
909
|
+
const configMode = args.configMode ?? "production";
|
|
910
|
+
const { userConfig, userPlugins } = await loadUserConfig({
|
|
911
|
+
root,
|
|
912
|
+
configFile: args.configFile,
|
|
913
|
+
mode: configMode
|
|
914
|
+
});
|
|
915
|
+
const config = {
|
|
916
|
+
...userConfig,
|
|
917
|
+
root,
|
|
918
|
+
mode: configMode,
|
|
919
|
+
configFile: false,
|
|
920
|
+
logLevel: "error",
|
|
921
|
+
appType: "custom",
|
|
922
|
+
esbuild: userConfig.esbuild === false ? false : {
|
|
923
|
+
...userConfig.esbuild,
|
|
924
|
+
jsx: "automatic",
|
|
925
|
+
jsxImportSource: PLUGIN_NAME
|
|
926
|
+
},
|
|
927
|
+
server: {
|
|
928
|
+
...userConfig.server,
|
|
929
|
+
middlewareMode: true,
|
|
930
|
+
watch: null
|
|
931
|
+
},
|
|
932
|
+
plugins: [...userPlugins, createPageHelperPlugin(getPages)]
|
|
933
|
+
};
|
|
934
|
+
const buildServer = await createServer(config);
|
|
935
|
+
return {
|
|
936
|
+
loadModule: async (entryPath) => {
|
|
937
|
+
const relativePath = "/" + path5.relative(root, entryPath).replace(/\\/g, "/");
|
|
938
|
+
return importPageModule(buildServer, relativePath);
|
|
939
|
+
},
|
|
940
|
+
close: async () => {
|
|
941
|
+
await buildServer.close();
|
|
942
|
+
}
|
|
903
943
|
};
|
|
904
|
-
}
|
|
905
|
-
async function closePageModuleLoader() {
|
|
906
|
-
if (buildServer) {
|
|
907
|
-
await buildServer.close();
|
|
908
|
-
buildServer = null;
|
|
909
|
-
}
|
|
910
944
|
}
|
|
911
945
|
|
|
912
946
|
// src/dev-server.ts
|
|
@@ -925,8 +959,12 @@ function normalizeRoutePath2(input) {
|
|
|
925
959
|
function shouldSkipHtmlRouting(url, pagesDir) {
|
|
926
960
|
return url.startsWith("/@vite") || url.startsWith("/@fs/") || url.startsWith("/node_modules/") || url.startsWith(`/${pagesDir}/`) || url === "/favicon.ico" || isStaticAssetRequest(url);
|
|
927
961
|
}
|
|
962
|
+
function hasDotDotSegment(url) {
|
|
963
|
+
return url.split("/").includes("..");
|
|
964
|
+
}
|
|
928
965
|
function tryRewriteRootAssetToSrc(root, pagesDir, url) {
|
|
929
966
|
if (!url.startsWith("/")) return null;
|
|
967
|
+
if (hasDotDotSegment(url)) return null;
|
|
930
968
|
if (!isStaticAssetRequest(url)) return null;
|
|
931
969
|
if (url.startsWith(`/${pagesDir}/`)) return null;
|
|
932
970
|
const candidate = path6.join(root, pagesDir, url.slice(1));
|
|
@@ -939,6 +977,7 @@ function rewriteRootAssetUrlsInDevHtml(html, root, pagesDir) {
|
|
|
939
977
|
return html.replace(
|
|
940
978
|
/\b(href|src)=["'](\/[^"']+)["']/g,
|
|
941
979
|
(full, attr, url) => {
|
|
980
|
+
if (hasDotDotSegment(url)) return full;
|
|
942
981
|
if (!isStaticAssetRequest(url)) return full;
|
|
943
982
|
if (url.startsWith(`/${pagesDir}/`)) return full;
|
|
944
983
|
const candidate = path6.join(root, pagesDir, url.slice(1));
|
|
@@ -949,7 +988,7 @@ function rewriteRootAssetUrlsInDevHtml(html, root, pagesDir) {
|
|
|
949
988
|
}
|
|
950
989
|
function installDevServer(args) {
|
|
951
990
|
const { server, root, pagesDir, getPages } = args;
|
|
952
|
-
const
|
|
991
|
+
const loaderPromise = createPageModuleLoader({
|
|
953
992
|
mode: "dev",
|
|
954
993
|
root,
|
|
955
994
|
server
|
|
@@ -960,7 +999,9 @@ function installDevServer(args) {
|
|
|
960
999
|
const url = normalizeRoutePath2(originalUrl);
|
|
961
1000
|
const rewrittenAssetUrl = tryRewriteRootAssetToSrc(root, pagesDir, url);
|
|
962
1001
|
if (rewrittenAssetUrl) {
|
|
963
|
-
|
|
1002
|
+
const suffixIndex = originalUrl.search(/[?#]/);
|
|
1003
|
+
const suffix = suffixIndex === -1 ? "" : originalUrl.slice(suffixIndex);
|
|
1004
|
+
req.url = rewrittenAssetUrl + suffix;
|
|
964
1005
|
return next();
|
|
965
1006
|
}
|
|
966
1007
|
if (shouldSkipHtmlRouting(url, pagesDir)) {
|
|
@@ -973,7 +1014,7 @@ function installDevServer(args) {
|
|
|
973
1014
|
if (!page) {
|
|
974
1015
|
return next();
|
|
975
1016
|
}
|
|
976
|
-
const loadModule = await
|
|
1017
|
+
const { loadModule } = await loaderPromise;
|
|
977
1018
|
const mod = await loadModule(page.entryPath, page.relativePath);
|
|
978
1019
|
if (!mod) {
|
|
979
1020
|
return next();
|
|
@@ -1029,10 +1070,11 @@ function collectScriptSrcs(html) {
|
|
|
1029
1070
|
}
|
|
1030
1071
|
function collectStylesheetHrefs(html) {
|
|
1031
1072
|
const out = [];
|
|
1032
|
-
for (const match of html.matchAll(
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1073
|
+
for (const match of html.matchAll(/<link\b[^>]*>/gi)) {
|
|
1074
|
+
const tag = match[0];
|
|
1075
|
+
if (!/\brel=["']?stylesheet["']?/i.test(tag)) continue;
|
|
1076
|
+
const href = tag.match(/\bhref=["']([^"']+)["']/i);
|
|
1077
|
+
if (href) out.push(href[1]);
|
|
1036
1078
|
}
|
|
1037
1079
|
return out;
|
|
1038
1080
|
}
|
|
@@ -1048,6 +1090,20 @@ function collectLiteralDynamicImports(html) {
|
|
|
1048
1090
|
function unique(values) {
|
|
1049
1091
|
return [...new Set(values)];
|
|
1050
1092
|
}
|
|
1093
|
+
function collectHrefSrcUrls(html) {
|
|
1094
|
+
const out = [];
|
|
1095
|
+
for (const match of html.matchAll(/\b(?:href|src)=["']([^"']+)["']/gi)) {
|
|
1096
|
+
out.push(match[1]);
|
|
1097
|
+
}
|
|
1098
|
+
return out;
|
|
1099
|
+
}
|
|
1100
|
+
function collectLocalAssetUrls(html) {
|
|
1101
|
+
const candidates = [
|
|
1102
|
+
...collectHrefSrcUrls(html),
|
|
1103
|
+
...collectLiteralDynamicImports(html)
|
|
1104
|
+
];
|
|
1105
|
+
return unique(candidates.filter(isLocalRootUrl).map(stripQueryAndHash));
|
|
1106
|
+
}
|
|
1051
1107
|
function formatPageLabel(pageLabel) {
|
|
1052
1108
|
return pageLabel ? ` (${pageLabel})` : "";
|
|
1053
1109
|
}
|
|
@@ -1132,6 +1188,12 @@ async function buildPageIndex(args) {
|
|
|
1132
1188
|
const mod = modulesByEntry.get(entry.entryPath) ?? {};
|
|
1133
1189
|
if (entry.dynamic) {
|
|
1134
1190
|
const rows = (mod.generateStaticParams ? await mod.generateStaticParams() : []) ?? [];
|
|
1191
|
+
const paramRows = Array.isArray(rows) ? rows : [];
|
|
1192
|
+
if (paramRows.length === 0) {
|
|
1193
|
+
console.warn(
|
|
1194
|
+
`[${PLUGIN_NAME}] \u26A0\uFE0F Dynamic page "${entry.relativePath}" generated no routes. Export generateStaticParams() returning at least one params object to emit pages for it.`
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1135
1197
|
pages.push(
|
|
1136
1198
|
...expandStaticPaths(
|
|
1137
1199
|
{
|
|
@@ -1144,7 +1206,7 @@ async function buildPageIndex(args) {
|
|
|
1144
1206
|
paramNames: entry.paramNames,
|
|
1145
1207
|
paramDefinitions: entry.paramDefinitions
|
|
1146
1208
|
},
|
|
1147
|
-
|
|
1209
|
+
paramRows,
|
|
1148
1210
|
cleanUrls
|
|
1149
1211
|
)
|
|
1150
1212
|
);
|
|
@@ -1328,8 +1390,8 @@ function warnIfNotESM(root) {
|
|
|
1328
1390
|
} catch {
|
|
1329
1391
|
}
|
|
1330
1392
|
}
|
|
1331
|
-
function
|
|
1332
|
-
return
|
|
1393
|
+
function escapeXml(value) {
|
|
1394
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1333
1395
|
}
|
|
1334
1396
|
function chunkArray(items, size) {
|
|
1335
1397
|
const out = [];
|
|
@@ -1338,6 +1400,50 @@ function chunkArray(items, size) {
|
|
|
1338
1400
|
}
|
|
1339
1401
|
return out;
|
|
1340
1402
|
}
|
|
1403
|
+
var DEFAULT_404_HTML = `<!doctype html>
|
|
1404
|
+
<html lang="en">
|
|
1405
|
+
<head>
|
|
1406
|
+
<meta charset="UTF-8" />
|
|
1407
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1408
|
+
<title>404 - Page Not Found</title>
|
|
1409
|
+
<style>
|
|
1410
|
+
:root {
|
|
1411
|
+
color-scheme: light dark;
|
|
1412
|
+
}
|
|
1413
|
+
body {
|
|
1414
|
+
margin: 0;
|
|
1415
|
+
font-family: system-ui, sans-serif;
|
|
1416
|
+
min-height: 100vh;
|
|
1417
|
+
display: grid;
|
|
1418
|
+
place-items: center;
|
|
1419
|
+
padding: 2rem;
|
|
1420
|
+
}
|
|
1421
|
+
main {
|
|
1422
|
+
max-width: 40rem;
|
|
1423
|
+
text-align: center;
|
|
1424
|
+
}
|
|
1425
|
+
h1 {
|
|
1426
|
+
font-size: 3rem;
|
|
1427
|
+
margin: 0 0 1rem;
|
|
1428
|
+
}
|
|
1429
|
+
p {
|
|
1430
|
+
margin: 0.5rem 0;
|
|
1431
|
+
line-height: 1.5;
|
|
1432
|
+
}
|
|
1433
|
+
a {
|
|
1434
|
+
color: inherit;
|
|
1435
|
+
}
|
|
1436
|
+
</style>
|
|
1437
|
+
</head>
|
|
1438
|
+
<body>
|
|
1439
|
+
<main>
|
|
1440
|
+
<h1>404</h1>
|
|
1441
|
+
<p>Page not found.</p>
|
|
1442
|
+
<p><a href="/">Go back home</a></p>
|
|
1443
|
+
</main>
|
|
1444
|
+
</body>
|
|
1445
|
+
</html>
|
|
1446
|
+
`;
|
|
1341
1447
|
function isHtJsxFile(id) {
|
|
1342
1448
|
return id.endsWith(".ht.jsx") || id.endsWith(".html.jsx") || id.endsWith(".ht.tsx") || id.endsWith(".html.tsx");
|
|
1343
1449
|
}
|
|
@@ -1354,18 +1460,12 @@ function htPages(options = {}) {
|
|
|
1354
1460
|
let server = null;
|
|
1355
1461
|
let devPages = [];
|
|
1356
1462
|
let watcherAttached = false;
|
|
1463
|
+
let userConfigFile;
|
|
1464
|
+
let resolvedMode = "production";
|
|
1465
|
+
let buildPipelinePromise = null;
|
|
1357
1466
|
const cleanUrls = options.cleanUrls ?? true;
|
|
1358
1467
|
const pagesDir = options.pagesDir ?? "src";
|
|
1359
|
-
const pageExtensions = options.pageExtensions?.length ? options.pageExtensions :
|
|
1360
|
-
".ht.js",
|
|
1361
|
-
".html.js",
|
|
1362
|
-
".ht.ts",
|
|
1363
|
-
".html.ts",
|
|
1364
|
-
".ht.jsx",
|
|
1365
|
-
".html.jsx",
|
|
1366
|
-
".ht.tsx",
|
|
1367
|
-
".html.tsx"
|
|
1368
|
-
];
|
|
1468
|
+
const pageExtensions = options.pageExtensions?.length ? options.pageExtensions : DEFAULT_PAGE_EXTENSIONS;
|
|
1369
1469
|
function logDebug(enabled, ...args) {
|
|
1370
1470
|
if (!enabled) return;
|
|
1371
1471
|
console.log(`[${PLUGIN_NAME}]`, ...args);
|
|
@@ -1384,13 +1484,13 @@ function htPages(options = {}) {
|
|
|
1384
1484
|
entries.map((e) => e.relativePath)
|
|
1385
1485
|
);
|
|
1386
1486
|
if (!server) return [];
|
|
1387
|
-
const
|
|
1487
|
+
const loader = await createPageModuleLoader({
|
|
1388
1488
|
mode: "dev",
|
|
1389
1489
|
root,
|
|
1390
1490
|
server
|
|
1391
1491
|
});
|
|
1392
1492
|
for (const entry of entries) {
|
|
1393
|
-
const mod = await loadModule(entry.entryPath, entry.relativePath);
|
|
1493
|
+
const mod = await loader.loadModule(entry.entryPath, entry.relativePath);
|
|
1394
1494
|
modulesByEntry.set(entry.entryPath, mod);
|
|
1395
1495
|
}
|
|
1396
1496
|
devPages = await buildPageIndex({
|
|
@@ -1405,29 +1505,55 @@ function htPages(options = {}) {
|
|
|
1405
1505
|
);
|
|
1406
1506
|
return devPages;
|
|
1407
1507
|
}
|
|
1408
|
-
async function
|
|
1508
|
+
async function createBuildPipeline() {
|
|
1409
1509
|
const entries = await discoverEntryPages(root, options);
|
|
1410
1510
|
await writePageTypeDeclarations({
|
|
1411
1511
|
root,
|
|
1412
1512
|
pagesDir,
|
|
1413
1513
|
entries
|
|
1414
1514
|
});
|
|
1415
|
-
const
|
|
1416
|
-
const loadModule = await createPageModuleLoader({
|
|
1515
|
+
const loader = await createPageModuleLoader({
|
|
1417
1516
|
mode: "build",
|
|
1418
1517
|
root,
|
|
1419
|
-
getPages: async () => entries
|
|
1518
|
+
getPages: async () => entries,
|
|
1519
|
+
configFile: userConfigFile,
|
|
1520
|
+
configMode: resolvedMode
|
|
1420
1521
|
});
|
|
1421
|
-
|
|
1422
|
-
const
|
|
1423
|
-
|
|
1522
|
+
try {
|
|
1523
|
+
const modulesByEntry = /* @__PURE__ */ new Map();
|
|
1524
|
+
for (const entry of entries) {
|
|
1525
|
+
const mod = await loader.loadModule(
|
|
1526
|
+
entry.entryPath,
|
|
1527
|
+
entry.relativePath
|
|
1528
|
+
);
|
|
1529
|
+
modulesByEntry.set(entry.entryPath, mod);
|
|
1530
|
+
}
|
|
1531
|
+
const pages = await buildPageIndex({
|
|
1532
|
+
entries,
|
|
1533
|
+
modulesByEntry,
|
|
1534
|
+
cleanUrls
|
|
1535
|
+
});
|
|
1536
|
+
return { entries, modulesByEntry, pages, closeLoader: loader.close };
|
|
1537
|
+
} catch (error) {
|
|
1538
|
+
await loader.close();
|
|
1539
|
+
throw error;
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
function getBuildPipeline() {
|
|
1543
|
+
if (!buildPipelinePromise) {
|
|
1544
|
+
buildPipelinePromise = createBuildPipeline();
|
|
1545
|
+
}
|
|
1546
|
+
return buildPipelinePromise;
|
|
1547
|
+
}
|
|
1548
|
+
async function closeBuildPipeline() {
|
|
1549
|
+
const pending = buildPipelinePromise;
|
|
1550
|
+
buildPipelinePromise = null;
|
|
1551
|
+
if (!pending) return;
|
|
1552
|
+
try {
|
|
1553
|
+
const pipeline = await pending;
|
|
1554
|
+
await pipeline.closeLoader();
|
|
1555
|
+
} catch {
|
|
1424
1556
|
}
|
|
1425
|
-
const pages = await buildPageIndex({
|
|
1426
|
-
entries,
|
|
1427
|
-
modulesByEntry,
|
|
1428
|
-
cleanUrls
|
|
1429
|
-
});
|
|
1430
|
-
return { entries, modulesByEntry, pages };
|
|
1431
1557
|
}
|
|
1432
1558
|
return {
|
|
1433
1559
|
name: PLUGIN_NAME,
|
|
@@ -1464,7 +1590,7 @@ function htPages(options = {}) {
|
|
|
1464
1590
|
return RESOLVED_VIRTUAL_JSX_DEV_RUNTIME_ID;
|
|
1465
1591
|
}
|
|
1466
1592
|
}
|
|
1467
|
-
if (importer &&
|
|
1593
|
+
if (importer && isLocalPageTypesImport(id)) {
|
|
1468
1594
|
return `${VIRTUAL_LOCAL_TYPES_PREFIX}${importer}::${id}`;
|
|
1469
1595
|
}
|
|
1470
1596
|
return null;
|
|
@@ -1489,9 +1615,9 @@ export { Fragment, jsx, jsxs, jsxDEV } from ${JSON.stringify(
|
|
|
1489
1615
|
}
|
|
1490
1616
|
if (id.startsWith(RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX)) {
|
|
1491
1617
|
const importer = id.slice(RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX.length);
|
|
1492
|
-
const
|
|
1618
|
+
const entries = await discoverEntryPages(root, options);
|
|
1493
1619
|
const normalizedImporter = path9.resolve(importer);
|
|
1494
|
-
const page =
|
|
1620
|
+
const page = entries.find(
|
|
1495
1621
|
(candidate) => path9.resolve(candidate.absolutePath) === normalizedImporter
|
|
1496
1622
|
);
|
|
1497
1623
|
return generateTypedPageHelper(page);
|
|
@@ -1529,12 +1655,15 @@ export {
|
|
|
1529
1655
|
},
|
|
1530
1656
|
configResolved(resolved) {
|
|
1531
1657
|
root = options.root ? path9.resolve(resolved.root, options.root) : resolved.root;
|
|
1658
|
+
userConfigFile = resolved.configFile ?? void 0;
|
|
1659
|
+
resolvedMode = resolved.mode;
|
|
1532
1660
|
if (!hasWarnedESM) {
|
|
1533
1661
|
warnIfNotESM(root);
|
|
1534
1662
|
hasWarnedESM = true;
|
|
1535
1663
|
}
|
|
1536
1664
|
},
|
|
1537
1665
|
async buildStart() {
|
|
1666
|
+
await closeBuildPipeline();
|
|
1538
1667
|
const entries = await discoverEntryPages(root, options);
|
|
1539
1668
|
for (const entry of entries) {
|
|
1540
1669
|
this.addWatchFile(entry.entryPath);
|
|
@@ -1590,8 +1719,10 @@ export {
|
|
|
1590
1719
|
);
|
|
1591
1720
|
}
|
|
1592
1721
|
};
|
|
1722
|
+
const pagesRoot = path9.join(root, pagesDir);
|
|
1593
1723
|
const reload = (file) => {
|
|
1594
|
-
|
|
1724
|
+
const relative = path9.relative(pagesRoot, file);
|
|
1725
|
+
if (relative === "" || relative.startsWith("..") || path9.isAbsolute(relative) || relative.split(path9.sep).includes("node_modules")) {
|
|
1595
1726
|
return;
|
|
1596
1727
|
}
|
|
1597
1728
|
void reloadAfterChange(file);
|
|
@@ -1613,32 +1744,92 @@ export {
|
|
|
1613
1744
|
},
|
|
1614
1745
|
async generateBundle(_, bundle) {
|
|
1615
1746
|
try {
|
|
1616
|
-
const { modulesByEntry, pages } = await
|
|
1617
|
-
const staticAssets = await collectStaticAssets({
|
|
1618
|
-
root,
|
|
1619
|
-
pagesDir,
|
|
1620
|
-
pageExtensions
|
|
1621
|
-
});
|
|
1747
|
+
const { modulesByEntry, pages } = await getBuildPipeline();
|
|
1622
1748
|
logDebug(
|
|
1623
1749
|
options.debug,
|
|
1624
1750
|
"emitting pages",
|
|
1625
1751
|
pages.map((p) => p.fileName)
|
|
1626
1752
|
);
|
|
1753
|
+
const limit = pLimit(options.renderConcurrency ?? 8);
|
|
1754
|
+
const batchSize = options.renderBatchSize ?? Math.max(options.renderConcurrency ?? 8, 32);
|
|
1755
|
+
const renderedPages = [];
|
|
1756
|
+
for (const batch of chunkArray(pages, batchSize)) {
|
|
1757
|
+
await Promise.all(
|
|
1758
|
+
batch.map(
|
|
1759
|
+
(page) => limit(async () => {
|
|
1760
|
+
const mod = modulesByEntry.get(page.entryPath);
|
|
1761
|
+
if (!mod) {
|
|
1762
|
+
throw new Error(
|
|
1763
|
+
`[${PLUGIN_NAME}] Missing module for page entry: ${page.entryPath}`
|
|
1764
|
+
);
|
|
1765
|
+
}
|
|
1766
|
+
const html = await renderPage(page, mod, false);
|
|
1767
|
+
validateHtmlAssetReferences({
|
|
1768
|
+
root,
|
|
1769
|
+
pagesDir,
|
|
1770
|
+
html,
|
|
1771
|
+
pluginName: PLUGIN_NAME,
|
|
1772
|
+
pageLabel: page.relativePath,
|
|
1773
|
+
missingAssets: options.missingAssets ?? "error"
|
|
1774
|
+
});
|
|
1775
|
+
renderedPages.push({ page, html });
|
|
1776
|
+
})
|
|
1777
|
+
)
|
|
1778
|
+
);
|
|
1779
|
+
}
|
|
1780
|
+
const rendered404 = renderedPages.find(
|
|
1781
|
+
(rendered) => rendered.page.routePath === "/404"
|
|
1782
|
+
);
|
|
1783
|
+
const notFoundHtml = rendered404?.html ?? DEFAULT_404_HTML;
|
|
1784
|
+
logDebug(
|
|
1785
|
+
options.debug,
|
|
1786
|
+
rendered404 ? "generated 404.html from user page" : "generated default 404.html"
|
|
1787
|
+
);
|
|
1788
|
+
const referencedUrls = /* @__PURE__ */ new Set();
|
|
1789
|
+
for (const { html } of renderedPages) {
|
|
1790
|
+
for (const url of collectLocalAssetUrls(html)) {
|
|
1791
|
+
referencedUrls.add(url);
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
for (const url of collectLocalAssetUrls(notFoundHtml)) {
|
|
1795
|
+
referencedUrls.add(url);
|
|
1796
|
+
}
|
|
1797
|
+
const staticAssets = await collectStaticAssets({
|
|
1798
|
+
root,
|
|
1799
|
+
pagesDir,
|
|
1800
|
+
pageExtensions
|
|
1801
|
+
});
|
|
1802
|
+
const isReferenced = (asset) => referencedUrls.has(`/${asset.outputFileName}`) || referencedUrls.has(`/${asset.relativePathFromSrc}`);
|
|
1803
|
+
const processableAssets = [];
|
|
1804
|
+
const skippedAssets = [];
|
|
1805
|
+
for (const asset of staticAssets) {
|
|
1806
|
+
if (asset.kind !== "process") continue;
|
|
1807
|
+
if (isReferenced(asset)) {
|
|
1808
|
+
processableAssets.push(asset);
|
|
1809
|
+
} else {
|
|
1810
|
+
skippedAssets.push(asset);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
if (skippedAssets.length > 0) {
|
|
1814
|
+
logDebug(
|
|
1815
|
+
options.debug,
|
|
1816
|
+
"skipping unreferenced code assets",
|
|
1817
|
+
skippedAssets.map((asset) => asset.relativePathFromSrc)
|
|
1818
|
+
);
|
|
1819
|
+
}
|
|
1627
1820
|
logDebug(
|
|
1628
1821
|
options.debug,
|
|
1629
1822
|
"emitting static assets",
|
|
1630
|
-
staticAssets.map((asset) => ({
|
|
1823
|
+
staticAssets.filter((asset) => asset.kind === "copy" || isReferenced(asset)).map((asset) => ({
|
|
1631
1824
|
kind: asset.kind,
|
|
1632
1825
|
input: asset.relativePathFromSrc,
|
|
1633
1826
|
output: asset.outputFileName
|
|
1634
1827
|
}))
|
|
1635
1828
|
);
|
|
1636
|
-
const limit = pLimit(options.renderConcurrency ?? 8);
|
|
1637
|
-
const batchSize = options.renderBatchSize ?? Math.max(options.renderConcurrency ?? 8, 32);
|
|
1638
1829
|
const processedOutputs = await buildProcessedStaticAssets({
|
|
1639
1830
|
root,
|
|
1640
1831
|
pagesDir,
|
|
1641
|
-
assets:
|
|
1832
|
+
assets: processableAssets,
|
|
1642
1833
|
minify: true,
|
|
1643
1834
|
sourcemap: false
|
|
1644
1835
|
});
|
|
@@ -1658,117 +1849,28 @@ export {
|
|
|
1658
1849
|
source
|
|
1659
1850
|
});
|
|
1660
1851
|
}
|
|
1661
|
-
for (const
|
|
1662
|
-
await Promise.all(
|
|
1663
|
-
batch.map(
|
|
1664
|
-
(page) => limit(async () => {
|
|
1665
|
-
const mod = modulesByEntry.get(page.entryPath);
|
|
1666
|
-
if (!mod) {
|
|
1667
|
-
throw new Error(
|
|
1668
|
-
`[${PLUGIN_NAME}] Missing module for page entry: ${page.entryPath}`
|
|
1669
|
-
);
|
|
1670
|
-
}
|
|
1671
|
-
const html = await renderPage(page, mod, false);
|
|
1672
|
-
validateHtmlAssetReferences({
|
|
1673
|
-
root,
|
|
1674
|
-
pagesDir,
|
|
1675
|
-
html,
|
|
1676
|
-
pluginName: PLUGIN_NAME,
|
|
1677
|
-
pageLabel: page.relativePath,
|
|
1678
|
-
missingAssets: options.missingAssets ?? "error"
|
|
1679
|
-
});
|
|
1680
|
-
this.emitFile({
|
|
1681
|
-
type: "asset",
|
|
1682
|
-
fileName: options.mapOutputPath?.(page) ?? page.fileName,
|
|
1683
|
-
source: html
|
|
1684
|
-
});
|
|
1685
|
-
})
|
|
1686
|
-
)
|
|
1687
|
-
);
|
|
1688
|
-
}
|
|
1689
|
-
const notFoundPage = pages.find((p) => p.routePath === "/404");
|
|
1690
|
-
if (notFoundPage) {
|
|
1691
|
-
const mod = modulesByEntry.get(notFoundPage.entryPath);
|
|
1692
|
-
if (!mod) {
|
|
1693
|
-
throw new Error(
|
|
1694
|
-
`[${PLUGIN_NAME}] Missing module for 404 page entry: ${notFoundPage.entryPath}`
|
|
1695
|
-
);
|
|
1696
|
-
}
|
|
1697
|
-
const html = await renderPage(notFoundPage, mod, false);
|
|
1698
|
-
validateHtmlAssetReferences({
|
|
1699
|
-
root,
|
|
1700
|
-
pagesDir,
|
|
1701
|
-
html,
|
|
1702
|
-
pluginName: PLUGIN_NAME,
|
|
1703
|
-
pageLabel: notFoundPage.relativePath,
|
|
1704
|
-
missingAssets: options.missingAssets ?? "error"
|
|
1705
|
-
});
|
|
1852
|
+
for (const { page, html } of renderedPages) {
|
|
1706
1853
|
this.emitFile({
|
|
1707
1854
|
type: "asset",
|
|
1708
|
-
fileName:
|
|
1855
|
+
fileName: options.mapOutputPath?.(page) ?? page.fileName,
|
|
1709
1856
|
source: html
|
|
1710
1857
|
});
|
|
1711
|
-
logDebug(options.debug, "generated 404.html from user page");
|
|
1712
|
-
} else {
|
|
1713
|
-
const default404 = `<!doctype html>
|
|
1714
|
-
<html lang="en">
|
|
1715
|
-
<head>
|
|
1716
|
-
<meta charset="UTF-8" />
|
|
1717
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1718
|
-
<title>404 - Page Not Found</title>
|
|
1719
|
-
<style>
|
|
1720
|
-
:root {
|
|
1721
|
-
color-scheme: light dark;
|
|
1722
|
-
}
|
|
1723
|
-
body {
|
|
1724
|
-
margin: 0;
|
|
1725
|
-
font-family: system-ui, sans-serif;
|
|
1726
|
-
min-height: 100vh;
|
|
1727
|
-
display: grid;
|
|
1728
|
-
place-items: center;
|
|
1729
|
-
padding: 2rem;
|
|
1730
|
-
}
|
|
1731
|
-
main {
|
|
1732
|
-
max-width: 40rem;
|
|
1733
|
-
text-align: center;
|
|
1734
|
-
}
|
|
1735
|
-
h1 {
|
|
1736
|
-
font-size: 3rem;
|
|
1737
|
-
margin: 0 0 1rem;
|
|
1738
|
-
}
|
|
1739
|
-
p {
|
|
1740
|
-
margin: 0.5rem 0;
|
|
1741
|
-
line-height: 1.5;
|
|
1742
|
-
}
|
|
1743
|
-
a {
|
|
1744
|
-
color: inherit;
|
|
1745
|
-
}
|
|
1746
|
-
</style>
|
|
1747
|
-
</head>
|
|
1748
|
-
<body>
|
|
1749
|
-
<main>
|
|
1750
|
-
<h1>404</h1>
|
|
1751
|
-
<p>Page not found.</p>
|
|
1752
|
-
<p><a href="/">Go back home</a></p>
|
|
1753
|
-
</main>
|
|
1754
|
-
</body>
|
|
1755
|
-
</html>
|
|
1756
|
-
`;
|
|
1757
|
-
this.emitFile({
|
|
1758
|
-
type: "asset",
|
|
1759
|
-
fileName: "404.html",
|
|
1760
|
-
source: default404
|
|
1761
|
-
});
|
|
1762
|
-
logDebug(options.debug, "generated default 404.html");
|
|
1763
1858
|
}
|
|
1764
|
-
|
|
1859
|
+
this.emitFile({
|
|
1860
|
+
type: "asset",
|
|
1861
|
+
fileName: "404.html",
|
|
1862
|
+
source: notFoundHtml
|
|
1863
|
+
});
|
|
1864
|
+
const sitemapBase = (options.site ?? "").replace(/\/+$/, "");
|
|
1765
1865
|
const sitemapRoutes = [...new Set(pages.map((p) => p.routePath))].filter(
|
|
1766
1866
|
(route) => !route.includes(":") && !route.includes("*")
|
|
1767
1867
|
);
|
|
1768
1868
|
if (sitemapBase && sitemapRoutes.length > 0) {
|
|
1769
1869
|
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1770
1870
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
1771
|
-
${sitemapRoutes.map(
|
|
1871
|
+
${sitemapRoutes.map(
|
|
1872
|
+
(route) => ` <url><loc>${escapeXml(`${sitemapBase}${route}`)}</loc></url>`
|
|
1873
|
+
).join("\n")}
|
|
1772
1874
|
</urlset>
|
|
1773
1875
|
`;
|
|
1774
1876
|
this.emitFile({
|
|
@@ -1781,10 +1883,11 @@ ${sitemapRoutes.map((route) => ` <url><loc>${sitemapBase}${route}</loc></url>`)
|
|
|
1781
1883
|
const rss = options.rss;
|
|
1782
1884
|
if (rss?.site) {
|
|
1783
1885
|
const routePrefix = rss.routePrefix ?? "/blog";
|
|
1886
|
+
const rssSite = rss.site.replace(/\/+$/, "");
|
|
1784
1887
|
const rssItems = pages.filter((page) => page.routePath.startsWith(routePrefix)).map((page) => {
|
|
1785
|
-
const url = `${
|
|
1888
|
+
const url = escapeXml(`${rssSite}${page.routePath}`);
|
|
1786
1889
|
return ` <item>
|
|
1787
|
-
<title>${page.routePath}</title>
|
|
1890
|
+
<title>${escapeXml(page.routePath)}</title>
|
|
1788
1891
|
<link>${url}</link>
|
|
1789
1892
|
<guid>${url}</guid>
|
|
1790
1893
|
</item>`;
|
|
@@ -1792,9 +1895,9 @@ ${sitemapRoutes.map((route) => ` <url><loc>${sitemapBase}${route}</loc></url>`)
|
|
|
1792
1895
|
const rssXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1793
1896
|
<rss version="2.0">
|
|
1794
1897
|
<channel>
|
|
1795
|
-
<title>${rss.title ?? PLUGIN_NAME}</title>
|
|
1796
|
-
<link>${
|
|
1797
|
-
<description>${rss.description ?? "RSS feed"}</description>
|
|
1898
|
+
<title>${escapeXml(rss.title ?? PLUGIN_NAME)}</title>
|
|
1899
|
+
<link>${escapeXml(rssSite)}</link>
|
|
1900
|
+
<description>${escapeXml(rss.description ?? "RSS feed")}</description>
|
|
1798
1901
|
${rssItems}
|
|
1799
1902
|
</channel>
|
|
1800
1903
|
</rss>
|
|
@@ -1812,8 +1915,16 @@ ${rssItems}
|
|
|
1812
1915
|
}
|
|
1813
1916
|
}
|
|
1814
1917
|
} finally {
|
|
1815
|
-
await
|
|
1918
|
+
await closeBuildPipeline();
|
|
1919
|
+
}
|
|
1920
|
+
},
|
|
1921
|
+
async buildEnd(error) {
|
|
1922
|
+
if (error) {
|
|
1923
|
+
await closeBuildPipeline();
|
|
1816
1924
|
}
|
|
1925
|
+
},
|
|
1926
|
+
async closeBundle() {
|
|
1927
|
+
await closeBuildPipeline();
|
|
1817
1928
|
}
|
|
1818
1929
|
};
|
|
1819
1930
|
}
|
|
@@ -1832,8 +1943,20 @@ function createDefaultCacheKey(input, init) {
|
|
|
1832
1943
|
});
|
|
1833
1944
|
return createHash("sha256").update(raw).digest("hex");
|
|
1834
1945
|
}
|
|
1946
|
+
var SAFE_FILE_KEY_RE = /^[A-Za-z0-9._-]{1,200}$/;
|
|
1947
|
+
function toSafeFileKey(cacheKey) {
|
|
1948
|
+
if (SAFE_FILE_KEY_RE.test(cacheKey)) {
|
|
1949
|
+
return cacheKey;
|
|
1950
|
+
}
|
|
1951
|
+
return createHash("sha256").update(cacheKey).digest("hex");
|
|
1952
|
+
}
|
|
1835
1953
|
function getCacheFilePath(cacheKey) {
|
|
1836
|
-
return path10.join(
|
|
1954
|
+
return path10.join(
|
|
1955
|
+
process.cwd(),
|
|
1956
|
+
CACHE_DIR_NAME,
|
|
1957
|
+
"fetch",
|
|
1958
|
+
`${toSafeFileKey(cacheKey)}.json`
|
|
1959
|
+
);
|
|
1837
1960
|
}
|
|
1838
1961
|
function getEffectiveCacheMode(mode) {
|
|
1839
1962
|
if (mode === "memory" || mode === "fs" || mode === "none") {
|
|
@@ -1841,6 +1964,16 @@ function getEffectiveCacheMode(mode) {
|
|
|
1841
1964
|
}
|
|
1842
1965
|
return process.env.NODE_ENV === "production" ? "fs" : "memory";
|
|
1843
1966
|
}
|
|
1967
|
+
var BODY_ENCODING_HEADERS = /* @__PURE__ */ new Set([
|
|
1968
|
+
"content-encoding",
|
|
1969
|
+
"content-length",
|
|
1970
|
+
"transfer-encoding"
|
|
1971
|
+
]);
|
|
1972
|
+
function sanitizeHeaders(headers) {
|
|
1973
|
+
return [...headers.entries()].filter(
|
|
1974
|
+
([name]) => !BODY_ENCODING_HEADERS.has(name.toLowerCase())
|
|
1975
|
+
);
|
|
1976
|
+
}
|
|
1844
1977
|
function toResponse(cached) {
|
|
1845
1978
|
return new Response(cached.body, {
|
|
1846
1979
|
status: cached.status,
|
|
@@ -1885,22 +2018,25 @@ async function fetchWithCache(input, init, options = {}) {
|
|
|
1885
2018
|
}
|
|
1886
2019
|
const res = await fetch(input, init);
|
|
1887
2020
|
const body = await res.text();
|
|
1888
|
-
const
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
2021
|
+
const headers = sanitizeHeaders(res.headers);
|
|
2022
|
+
if (res.ok) {
|
|
2023
|
+
const record = {
|
|
2024
|
+
timestamp: Date.now(),
|
|
2025
|
+
status: res.status,
|
|
2026
|
+
statusText: res.statusText,
|
|
2027
|
+
headers,
|
|
2028
|
+
body
|
|
2029
|
+
};
|
|
2030
|
+
if (cacheMode === "memory") {
|
|
2031
|
+
memoryCache.set(cacheKey, record);
|
|
2032
|
+
} else if (cacheMode === "fs") {
|
|
2033
|
+
await fs7.writeFile(filePath, JSON.stringify(record), "utf8");
|
|
2034
|
+
}
|
|
1899
2035
|
}
|
|
1900
2036
|
return new Response(body, {
|
|
1901
2037
|
status: res.status,
|
|
1902
2038
|
statusText: res.statusText,
|
|
1903
|
-
headers
|
|
2039
|
+
headers
|
|
1904
2040
|
});
|
|
1905
2041
|
}
|
|
1906
2042
|
export {
|