vite-plugin-html-pages 1.3.6 → 1.4.0

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.
@@ -0,0 +1,44 @@
1
+ // src/jsx-runtime.ts
2
+ import * as tags from "javascript-to-html";
3
+ function flatten(value) {
4
+ if (Array.isArray(value)) {
5
+ return value.flatMap((item) => flatten(item));
6
+ }
7
+ return [value];
8
+ }
9
+ function normalizeChildren(children) {
10
+ return flatten(children).filter((value) => value !== true);
11
+ }
12
+ function renderIntrinsicTag(tagName, props) {
13
+ const tag = tags[tagName];
14
+ if (typeof tag !== "function") {
15
+ throw new Error(
16
+ `[vite-plugin-html-pages] Unknown JSX tag: <${tagName}>`
17
+ );
18
+ }
19
+ const { children, ...rest } = props;
20
+ const normalizedChildren = normalizeChildren(children);
21
+ if (Object.keys(rest).length > 0) {
22
+ return tag(rest, ...normalizedChildren);
23
+ }
24
+ return tag(...normalizedChildren);
25
+ }
26
+ function Fragment(props) {
27
+ return tags.fragment(...normalizeChildren(props.children));
28
+ }
29
+ function jsx(type, props) {
30
+ const finalProps = props ?? {};
31
+ if (typeof type === "function") {
32
+ return type(finalProps);
33
+ }
34
+ return renderIntrinsicTag(type, finalProps);
35
+ }
36
+ var jsxs = jsx;
37
+ var jsxDEV = jsx;
38
+ export {
39
+ Fragment,
40
+ jsx,
41
+ jsxDEV,
42
+ jsxs
43
+ };
44
+ //# sourceMappingURL=jsx-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/jsx-runtime.ts"],"sourcesContent":["import * as tags from 'javascript-to-html';\n\ntype PrimitiveChild = string | number | boolean | null | undefined;\ntype Child = PrimitiveChild | Child[];\n\nfunction flatten(value: Child): PrimitiveChild[] {\n if (Array.isArray(value)) {\n return value.flatMap((item) => flatten(item));\n }\n\n return [value];\n}\n\nfunction normalizeChildren(children: unknown): PrimitiveChild[] {\n return flatten(children as Child).filter((value) => value !== true);\n}\n\ntype Props = Record<string, unknown> & {\n children?: unknown;\n};\n\ntype Component = (props: Props) => unknown;\n\nfunction renderIntrinsicTag(tagName: string, props: Props): string {\n const tag = (tags as Record<string, (...args: unknown[]) => string>)[tagName];\n\n if (typeof tag !== 'function') {\n throw new Error(\n `[vite-plugin-html-pages] Unknown JSX tag: <${tagName}>`,\n );\n }\n\n const { children, ...rest } = props;\n const normalizedChildren = normalizeChildren(children);\n\n if (Object.keys(rest).length > 0) {\n return tag(rest, ...normalizedChildren);\n }\n\n return tag(...normalizedChildren);\n}\n\nexport function Fragment(props: Props): string {\n return tags.fragment(...normalizeChildren(props.children));\n}\n\nexport function jsx(\n type: string | Component,\n props: Props | null,\n): unknown {\n const finalProps = props ?? {};\n\n if (typeof type === 'function') {\n return type(finalProps);\n }\n\n return renderIntrinsicTag(type, finalProps);\n}\n\nexport const jsxs = jsx;\nexport const jsxDEV = jsx;"],"mappings":";AAAA,YAAY,UAAU;AAKtB,SAAS,QAAQ,OAAgC;AAC/C,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,QAAQ,CAAC,SAAS,QAAQ,IAAI,CAAC;AAAA,EAC9C;AAEA,SAAO,CAAC,KAAK;AACf;AAEA,SAAS,kBAAkB,UAAqC;AAC9D,SAAO,QAAQ,QAAiB,EAAE,OAAO,CAAC,UAAU,UAAU,IAAI;AACpE;AAQA,SAAS,mBAAmB,SAAiB,OAAsB;AACjE,QAAM,MAAO,KAAwD,OAAO;AAE5E,MAAI,OAAO,QAAQ,YAAY;AAC7B,UAAM,IAAI;AAAA,MACR,8CAA8C,OAAO;AAAA,IACvD;AAAA,EACF;AAEA,QAAM,EAAE,UAAU,GAAG,KAAK,IAAI;AAC9B,QAAM,qBAAqB,kBAAkB,QAAQ;AAErD,MAAI,OAAO,KAAK,IAAI,EAAE,SAAS,GAAG;AAChC,WAAO,IAAI,MAAM,GAAG,kBAAkB;AAAA,EACxC;AAEA,SAAO,IAAI,GAAG,kBAAkB;AAClC;AAEO,SAAS,SAAS,OAAsB;AAC7C,SAAY,cAAS,GAAG,kBAAkB,MAAM,QAAQ,CAAC;AAC3D;AAEO,SAAS,IACd,MACA,OACS;AACT,QAAM,aAAa,SAAS,CAAC;AAE7B,MAAI,OAAO,SAAS,YAAY;AAC9B,WAAO,KAAK,UAAU;AAAA,EACxB;AAEA,SAAO,mBAAmB,MAAM,UAAU;AAC5C;AAEO,IAAM,OAAO;AACb,IAAM,SAAS;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-html-pages",
3
- "version": "1.3.6",
3
+ "version": "1.4.0",
4
4
  "author": "Paul Browne",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -16,7 +16,10 @@
16
16
  "javascript-to-html",
17
17
  "static-html",
18
18
  "blog-generator"
19
- ],
19
+ ],
20
+ "files": [
21
+ "dist/**/*"
22
+ ],
20
23
  "description": "Minimal static site generation (SSG) for Vite using JavaScript functions that return HTML",
21
24
  "homepage": "https://github.com/paul-browne/vite-plugin-html-pages",
22
25
  "repository": {
@@ -29,10 +32,15 @@
29
32
  "main": "./dist/index.js",
30
33
  "module": "./dist/index.js",
31
34
  "types": "./dist/index.d.ts",
35
+ "sideEffects": false,
32
36
  "exports": {
33
37
  ".": {
34
38
  "types": "./dist/index.d.ts",
35
39
  "import": "./dist/index.js"
40
+ },
41
+ "./jsx-runtime": {
42
+ "types": "./dist/jsx-runtime.d.ts",
43
+ "import": "./dist/jsx-runtime.js"
36
44
  }
37
45
  },
38
46
  "scripts": {
@@ -46,9 +54,10 @@
46
54
  "vite": ">=5"
47
55
  },
48
56
  "dependencies": {
57
+ "esbuild": "^0.24.0",
49
58
  "fast-glob": "^3.3.3",
50
- "p-limit": "^4.0.0",
51
- "esbuild": "^0.24.0"
59
+ "javascript-to-html": "^1.1.3",
60
+ "p-limit": "^4.0.0"
52
61
  },
53
62
  "engines": {
54
63
  "node": ">=18"
package/TODO DELETED
@@ -1 +0,0 @@
1
- - Update README
package/src/constants.ts DELETED
@@ -1,6 +0,0 @@
1
- export const PLUGIN_NAME = 'vite-plugin-html-pages';
2
- export const VIRTUAL_BUILD_ENTRY_ID = `\0${PLUGIN_NAME}:build-entry`;
3
- export const VIRTUAL_PAGE_HELPER_ID = `${PLUGIN_NAME}/page`;
4
- export const RESOLVED_VIRTUAL_PAGE_HELPER_PREFIX =`\0${PLUGIN_NAME}/page:`;
5
- export const VIRTUAL_MANIFEST_ID = `\0virtual:${PLUGIN_NAME}-manifest`;
6
- export const CACHE_DIR_NAME = `node_modules/.cache/${PLUGIN_NAME}`;
package/src/dev-server.ts DELETED
@@ -1,126 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import type { ViteDevServer } from 'vite';
4
-
5
- import { renderPage } from './render-runtime';
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;
45
-
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 {
57
- return mod.dynamic === true || mod.prerender === false;
58
- }
59
-
60
- export function installDevServer(args: {
61
- server: ViteDevServer;
62
- getPages: () => Promise<HtPageInfo[]>;
63
- getEntries?: () => Promise<HtPageInfo[]>;
64
- }) {
65
- const { server, getPages } = args;
66
-
67
- server.middlewares.use(async (req, res, next) => {
68
- try {
69
- const originalUrl = req.url ?? '/';
70
- const url = originalUrl.split('?')[0];
71
-
72
- const rewrittenAssetUrl = tryRewriteRootAssetToSrc(server, url);
73
- if (rewrittenAssetUrl) {
74
- req.url = rewrittenAssetUrl + originalUrl.slice(url.length);
75
- return next();
76
- }
77
-
78
- if (shouldSkipHtmlRouting(url)) {
79
- return next();
80
- }
81
-
82
- const pages = await getPages();
83
-
84
- const page = pages.find((p) => p.routePath === url);
85
-
86
- if (!page) {
87
- return next();
88
- }
89
-
90
- const loadModule = await createPageModuleLoader({
91
- mode: 'dev',
92
- root: server.config.root,
93
- server,
94
- });
95
-
96
- const mod = await loadModule(page.entryPath, page.relativePath);
97
-
98
- if (!mod) {
99
- return next();
100
- }
101
-
102
- if (!shouldUseDynamicRendering(mod) && page.dynamic) {
103
- return next();
104
- }
105
-
106
- const html = await renderPage(page, mod, true);
107
- const transformedHtml = await server.transformIndexHtml(
108
- url,
109
- html,
110
- req.originalUrl,
111
- );
112
-
113
- res.statusCode = 200;
114
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
115
- res.end(transformedHtml);
116
- } catch (error) {
117
- server.config.logger.error(
118
- `[${PLUGIN_NAME}] dev server render failed: ${
119
- error instanceof Error ? error.stack ?? error.message : String(error)
120
- }`,
121
- );
122
-
123
- next(error as Error);
124
- }
125
- });
126
- }
package/src/discover.ts DELETED
@@ -1,84 +0,0 @@
1
- import path from 'node:path';
2
- import { normalizeFsPath, toPosix } from './path-utils';
3
- import { isDynamicPage, toRoutePattern } from './route-utils';
4
- import { extractRouteParamDefinitions } from './route-params';
5
- import type { HtPageInfo, HtPagesPluginOptions } from './types';
6
- import { PLUGIN_NAME } from './constants';
7
-
8
- function buildDefaultIncludeGlobs(
9
- pagesDir: string,
10
- pageExtensions: string[],
11
- ): string[] {
12
- return pageExtensions.map((ext) => {
13
- const cleanExt = ext.startsWith('.') ? ext.slice(1) : ext;
14
- return `${pagesDir}/**/*.${cleanExt}`;
15
- });
16
- }
17
-
18
- export async function discoverEntryPages(
19
- root: string,
20
- options: HtPagesPluginOptions,
21
- ): Promise<HtPageInfo[]> {
22
- const fgModule = await import('fast-glob');
23
- const fg = (fgModule.default ?? fgModule) as typeof import('fast-glob');
24
-
25
- const pagesDir = options.pagesDir ?? 'src';
26
- const pageExtensions = options.pageExtensions?.length
27
- ? options.pageExtensions
28
- : ['.ht.js', '.html.js', '.ht.ts', '.html.ts'];
29
-
30
- const include = Array.isArray(options.include)
31
- ? options.include
32
- : options.include
33
- ? [options.include]
34
- : buildDefaultIncludeGlobs(pagesDir, pageExtensions);
35
-
36
- const exclude = Array.isArray(options.exclude)
37
- ? options.exclude
38
- : options.exclude
39
- ? [options.exclude]
40
- : [];
41
-
42
- const pagesRoot = normalizeFsPath(path.join(root, pagesDir));
43
-
44
- const files = await fg.glob(include, {
45
- cwd: root,
46
- ignore: exclude,
47
- absolute: true,
48
- });
49
-
50
- return files
51
- .sort()
52
- .map((absolutePath) => {
53
- const entryPath = normalizeFsPath(absolutePath);
54
- const relativePath = toPosix(path.relative(root, entryPath));
55
- const relativeFromPagesDir = toPosix(path.relative(pagesRoot, entryPath));
56
-
57
- if (
58
- relativeFromPagesDir.startsWith('../') ||
59
- relativeFromPagesDir === '..'
60
- ) {
61
- throw new Error(
62
- `[${PLUGIN_NAME}] Page is outside pagesDir: ${entryPath} (pagesDir: ${pagesDir})`,
63
- );
64
- }
65
-
66
- const dynamic = isDynamicPage(relativeFromPagesDir);
67
- const routePattern = toRoutePattern(relativeFromPagesDir, pageExtensions);
68
- const paramDefinitions = extractRouteParamDefinitions(routePattern);
69
-
70
- return {
71
- id: entryPath,
72
- entryPath,
73
- absolutePath: entryPath,
74
- relativePath,
75
- routePattern,
76
- routePath: routePattern,
77
- fileName: '',
78
- dynamic,
79
- paramNames: paramDefinitions.map((p) => p.name),
80
- paramDefinitions,
81
- params: {},
82
- } satisfies HtPageInfo;
83
- });
84
- }
package/src/env.d.ts DELETED
@@ -1,11 +0,0 @@
1
- declare module 'vite-plugin-html-pages/page' {
2
- export type PageParams = Record<string, string | string[] | undefined>;
3
-
4
- export type PageContext = {
5
- params: PageParams;
6
- data?: unknown;
7
- dev: boolean;
8
- };
9
-
10
- export function definePage<T extends (ctx: PageContext) => any>(fn: T): T;
11
- }
package/src/errors.ts DELETED
@@ -1,32 +0,0 @@
1
- import type { HtPageInfo } from './types';
2
- import { PLUGIN_NAME } from './constants';
3
- export function invalidHtmlReturn(
4
- page: HtPageInfo,
5
- value: unknown,
6
- ): Error {
7
- return new Error(
8
- `[${PLUGIN_NAME}] Page "${page.relativePath}" must resolve to an HTML string, got ${typeof value}`,
9
- );
10
- }
11
-
12
- export function missingDefaultExport(page: HtPageInfo): Error {
13
- return new Error(
14
- `[${PLUGIN_NAME}] Page "${page.relativePath}" does not export a default renderer`,
15
- );
16
- }
17
-
18
- export function pageError(page: HtPageInfo, cause: unknown): Error {
19
- const message = `[${PLUGIN_NAME}] Failed to render "${page.relativePath}" at route "${page.routePath}"`;
20
-
21
- if (cause instanceof Error) {
22
- const err = new Error(`${message}: ${cause.message}`);
23
-
24
- if (cause.stack) {
25
- err.stack = `${err.stack}\nCaused by:\n${cause.stack}`;
26
- }
27
-
28
- return err;
29
- }
30
-
31
- return new Error(`${message}: ${String(cause)}`);
32
- }
@@ -1,141 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { createHash } from 'node:crypto';
4
- import { CACHE_DIR_NAME } from './constants';
5
-
6
- export type FetchCacheMode = 'auto' | 'memory' | 'fs' | 'none';
7
- export interface FetchWithCacheOptions {
8
- maxAge?: number;
9
- cacheKey?: string;
10
- forceRefresh?: boolean;
11
- cache?: FetchCacheMode;
12
- }
13
-
14
- type CachedResponseRecord = {
15
- timestamp: number;
16
- status: number;
17
- statusText: string;
18
- headers: [string, string][];
19
- body: string;
20
- };
21
-
22
- const memoryCache = new Map<string, CachedResponseRecord>();
23
-
24
- function createDefaultCacheKey(
25
- input: RequestInfo | URL,
26
- init?: RequestInit,
27
- ): string {
28
- const raw = JSON.stringify({
29
- url: String(input),
30
- method: init?.method ?? 'GET',
31
- headers: init?.headers ?? {},
32
- body: init?.body ?? null,
33
- });
34
-
35
- return createHash('sha256').update(raw).digest('hex');
36
- }
37
-
38
- function getCacheFilePath(cacheKey: string): string {
39
- return path.join(process.cwd(), CACHE_DIR_NAME, 'fetch', `${cacheKey}.json`);
40
- }
41
-
42
- function getEffectiveCacheMode(
43
- mode: FetchCacheMode | undefined,
44
- ): Exclude<FetchCacheMode, 'auto'> {
45
- if (mode === 'memory' || mode === 'fs' || mode === 'none') {
46
- return mode;
47
- }
48
-
49
- return process.env.NODE_ENV === 'production' ? 'fs' : 'memory';
50
- }
51
-
52
- function toResponse(cached: CachedResponseRecord): Response {
53
- return new Response(cached.body, {
54
- status: cached.status,
55
- statusText: cached.statusText,
56
- headers: cached.headers,
57
- });
58
- }
59
-
60
- function isFresh(cached: CachedResponseRecord, maxAgeSeconds: number): boolean {
61
- const ageSeconds = (Date.now() - cached.timestamp) / 1000;
62
- return ageSeconds <= maxAgeSeconds;
63
- }
64
-
65
- export function clearMemoryFetchCache(): void {
66
- memoryCache.clear();
67
- }
68
-
69
- export function deleteMemoryFetchCache(cacheKey: string): void {
70
- memoryCache.delete(cacheKey);
71
- }
72
-
73
- export async function fetchWithCache(
74
- input: RequestInfo | URL,
75
- init?: RequestInit,
76
- options: FetchWithCacheOptions = {},
77
- ): Promise<Response> {
78
- const maxAge = options.maxAge ?? 60 * 60;
79
- const method = (init?.method ?? 'GET').toUpperCase();
80
-
81
- if (method !== 'GET' && !options.cacheKey) {
82
- return fetch(input, init);
83
- }
84
-
85
- const cacheMode = getEffectiveCacheMode(options.cache);
86
- const cacheKey = options.cacheKey ?? createDefaultCacheKey(input, init);
87
-
88
- if (cacheMode === 'none') {
89
- return fetch(input, init);
90
- }
91
-
92
- if (cacheMode === 'memory' && !options.forceRefresh) {
93
- const cached = memoryCache.get(cacheKey);
94
-
95
- if (cached && isFresh(cached, maxAge)) {
96
- return toResponse(cached);
97
- }
98
- }
99
-
100
- const filePath = getCacheFilePath(cacheKey);
101
-
102
- if (cacheMode === 'fs') {
103
- await fs.mkdir(path.dirname(filePath), { recursive: true });
104
-
105
- if (!options.forceRefresh) {
106
- try {
107
- const raw = await fs.readFile(filePath, 'utf8');
108
- const cached = JSON.parse(raw) as CachedResponseRecord;
109
-
110
- if (isFresh(cached, maxAge)) {
111
- return toResponse(cached);
112
- }
113
- } catch {
114
- // cache miss or invalid cache; fetch fresh
115
- }
116
- }
117
- }
118
-
119
- const res = await fetch(input, init);
120
- const body = await res.text();
121
-
122
- const record: CachedResponseRecord = {
123
- timestamp: Date.now(),
124
- status: res.status,
125
- statusText: res.statusText,
126
- headers: [...res.headers.entries()],
127
- body,
128
- };
129
-
130
- if (cacheMode === 'memory') {
131
- memoryCache.set(cacheKey, record);
132
- } else if (cacheMode === 'fs') {
133
- await fs.writeFile(filePath, JSON.stringify(record), 'utf8');
134
- }
135
-
136
- return new Response(body, {
137
- status: res.status,
138
- statusText: res.statusText,
139
- headers: res.headers,
140
- });
141
- }
@@ -1,176 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
-
4
- export interface HtmlAssetValidationOptions {
5
- root: string;
6
- pagesDir: string;
7
- html: string;
8
- pluginName: string;
9
- pageLabel?: string;
10
- missingAssets?: 'error' | 'warn';
11
- }
12
-
13
- function stripQueryAndHash(url: string): string {
14
- return url.split('#')[0].split('?')[0];
15
- }
16
-
17
- function isLocalRootUrl(url: string): boolean {
18
- return !!url && url.startsWith('/') && !url.startsWith('//');
19
- }
20
-
21
- function fileExistsForPublicUrl(root: string, pagesDir: string, url: string): boolean {
22
- const clean = stripQueryAndHash(url).slice(1);
23
-
24
- const fromSrc = path.join(root, pagesDir, clean);
25
- if (fs.existsSync(fromSrc)) return true;
26
-
27
- const fromPublic = path.join(root, 'public', clean);
28
- if (fs.existsSync(fromPublic)) return true;
29
-
30
- return false;
31
- }
32
-
33
- function collectScriptSrcs(html: string): string[] {
34
- const out: string[] = [];
35
-
36
- for (const match of html.matchAll(
37
- /<script\b[^>]*\bsrc=["']([^"']+)["'][^>]*>/gi,
38
- )) {
39
- out.push(match[1]);
40
- }
41
-
42
- return out;
43
- }
44
-
45
- function collectStylesheetHrefs(html: string): string[] {
46
- const out: string[] = [];
47
-
48
- for (const match of html.matchAll(
49
- /<link\b[^>]*\brel=["']stylesheet["'][^>]*\bhref=["']([^"']+)["'][^>]*>/gi,
50
- )) {
51
- out.push(match[1]);
52
- }
53
-
54
- return out;
55
- }
56
-
57
- function collectLiteralDynamicImports(html: string): string[] {
58
- const out: string[] = [];
59
-
60
- for (const match of html.matchAll(
61
- /import\s*\(\s*["']([^"'`]+)["']\s*\)/gi,
62
- )) {
63
- out.push(match[1]);
64
- }
65
-
66
- return out;
67
- }
68
-
69
- function unique(values: string[]): string[] {
70
- return [...new Set(values)];
71
- }
72
-
73
- function formatPageLabel(pageLabel?: string): string {
74
- return pageLabel ? ` (${pageLabel})` : '';
75
- }
76
-
77
- function missingAssetMessage(args: {
78
- pluginName: string;
79
- kind: string;
80
- url: string;
81
- root: string;
82
- pagesDir: string;
83
- pageLabel?: string;
84
- }): string {
85
- const { pluginName, kind, url, root, pagesDir, pageLabel } = args;
86
- const clean = stripQueryAndHash(url).slice(1);
87
- const pageSuffix = formatPageLabel(pageLabel);
88
-
89
- return (
90
- `[${pluginName}] Missing ${kind}${pageSuffix}: ${url}\n` +
91
- `Expected one of:\n` +
92
- `- ${path.join(root, pagesDir, clean)}\n` +
93
- `- ${path.join(root, 'public', clean)}`
94
- );
95
- }
96
-
97
- function reportMissing(args: {
98
- mode: 'error' | 'warn';
99
- pluginName: string;
100
- kind: string;
101
- url: string;
102
- root: string;
103
- pagesDir: string;
104
- pageLabel?: string;
105
- }) {
106
- const message = missingAssetMessage(args);
107
-
108
- if (args.mode === 'warn') {
109
- console.warn(`⚠️ ${message}`);
110
- return;
111
- }
112
-
113
- throw new Error(message);
114
- }
115
-
116
- export function validateHtmlAssetReferences(
117
- options: HtmlAssetValidationOptions,
118
- ): void {
119
- const {
120
- root,
121
- pagesDir,
122
- html,
123
- pluginName,
124
- pageLabel,
125
- missingAssets = 'error',
126
- } = options;
127
-
128
- const scriptSrcs = unique(collectScriptSrcs(html)).filter(isLocalRootUrl);
129
- const stylesheetHrefs = unique(collectStylesheetHrefs(html)).filter(isLocalRootUrl);
130
- const literalDynamicImports = unique(collectLiteralDynamicImports(html)).filter(
131
- isLocalRootUrl,
132
- );
133
-
134
- for (const url of scriptSrcs) {
135
- if (!fileExistsForPublicUrl(root, pagesDir, url)) {
136
- reportMissing({
137
- mode: missingAssets,
138
- pluginName,
139
- kind: 'JavaScript asset',
140
- url,
141
- root,
142
- pagesDir,
143
- pageLabel,
144
- });
145
- }
146
- }
147
-
148
- for (const url of stylesheetHrefs) {
149
- if (!fileExistsForPublicUrl(root, pagesDir, url)) {
150
- reportMissing({
151
- mode: missingAssets,
152
- pluginName,
153
- kind: 'stylesheet asset',
154
- url,
155
- root,
156
- pagesDir,
157
- pageLabel,
158
- });
159
- }
160
- }
161
-
162
- for (const url of literalDynamicImports) {
163
- if (!fileExistsForPublicUrl(root, pagesDir, url)) {
164
- console.warn(
165
- `⚠️ ${missingAssetMessage({
166
- pluginName,
167
- kind: 'literal dynamic import',
168
- url,
169
- root,
170
- pagesDir,
171
- pageLabel,
172
- })}`,
173
- );
174
- }
175
- }
176
- }
package/src/index.ts DELETED
@@ -1,12 +0,0 @@
1
- export { htPages as default } from './plugin';
2
- export { fetchWithCache } from './fetch-cache';
3
-
4
- export type {
5
- HtPageInfo,
6
- HtPageModule,
7
- HtPagesPluginOptions,
8
- HtPageRenderContext,
9
- StaticParamRecord,
10
- } from './types';
11
-
12
- export type { FetchWithCacheOptions } from './fetch-cache';