vite-plugin-html-pages 1.0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Paul Browne
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,532 @@
1
+ # vite-plugin-html-pages
2
+
3
+ [![npm version](https://img.shields.io/npm/v/vite-plugin-html-pages.svg)](https://www.npmjs.com/package/vite-plugin-html-pages)
4
+ [![npm downloads](https://img.shields.io/npm/dm/vite-plugin-html-pages.svg)](https://www.npmjs.com/package/vite-plugin-html-pages)
5
+ [![license](https://img.shields.io/npm/l/vite-plugin-html-pages.svg)](LICENSE)
6
+ [![vite](https://img.shields.io/badge/vite-plugin-646CFF?logo=vite&logoColor=white)](https://vitejs.dev)
7
+
8
+ Minimal **static site generation for Vite** using JavaScript files that return HTML.
9
+
10
+ Generate static HTML pages from `*.ht.js` modules using Vite and
11
+ [`javascript-to-html`](https://www.npmjs.com/package/javascript-to-html).
12
+
13
+ ⭐ If this project helps you, please consider starring it.
14
+
15
+ # Built for the Vite ecosystem
16
+
17
+ Works seamlessly with:
18
+
19
+ - ⚡ **Vite**
20
+ - 📦 **Rollup / Rolldown**
21
+ - 🧩 **javascript-to-html**
22
+
23
+ A minimal **static site generator for Vite** that keeps pages as simple
24
+ JavaScript functions returning HTML.
25
+
26
+ ---
27
+
28
+ # TL;DR
29
+
30
+ Write:
31
+
32
+ ```js
33
+ // src/index.ht.js
34
+
35
+ import { fragment, html, body, head, title, h1 } from 'javascript-to-html'
36
+
37
+ export default () => fragment(
38
+ '<!doctype html>',
39
+ html({lang: "en"},
40
+ head(
41
+ title("My website")
42
+ ),
43
+ body(
44
+ h1('Hello world')
45
+ )
46
+ )
47
+ )
48
+ ```
49
+
50
+ Run:
51
+
52
+ ``` bash
53
+ vite build
54
+ ```
55
+
56
+ Get:
57
+
58
+ ```html
59
+ <!-- dist/index.html -->
60
+
61
+ <!doctype html>
62
+ <html lang="en">
63
+ <head>
64
+ <title>My website</title>
65
+ </head>
66
+ <body>
67
+ <h1>Hello world</h1>
68
+ </body>
69
+ </html>
70
+ ```
71
+
72
+ ---
73
+
74
+ # Features
75
+
76
+ - File-based routing
77
+ - Dynamic routes `[slug]`
78
+ - Multiple parameters `[year]/[slug]`
79
+ - Catch-all routes `[...slug]`
80
+ - Optional catch-all routes `[...slug]?`
81
+ - Route groups `(admin)/users.ht.js`
82
+ - Index routes `blog/[slug]/index.ht.js`
83
+ - Static params generation
84
+ - Fetch caching for data loading
85
+ - Dev server SSR rendering
86
+ - Parallel static generation
87
+ - Automatic `404.html`
88
+ - Automatic `sitemap.xml`
89
+ - Optional RSS feed generation
90
+ - Debug logging
91
+
92
+ ---
93
+
94
+ # Installation
95
+
96
+ ```bash
97
+ npm install vite-plugin-html-pages javascript-to-html
98
+ ```
99
+
100
+ ---
101
+
102
+ # Why this exists
103
+
104
+ Modern static site tools are powerful, but they often introduce:
105
+
106
+ - frameworks
107
+ - component systems
108
+ - complex build pipelines
109
+ - opinionated conventions
110
+
111
+ Sometimes you just want to:
112
+
113
+ - write HTML
114
+ - organize pages in folders
115
+ - generate static files
116
+
117
+ `vite-plugin-html-pages` exists for that use case.
118
+
119
+ It gives you:
120
+
121
+ - file‑based routing
122
+ - dynamic pages
123
+ - static generation
124
+ - dev server rendering
125
+
126
+ while keeping pages as **simple JavaScript functions that return HTML**.
127
+
128
+ ---
129
+
130
+ # Quick Start
131
+
132
+ ### vite.config.js
133
+
134
+ ```js
135
+ import { defineConfig } from "vite"
136
+ import { htPages } from "vite-plugin-html-pages"
137
+
138
+ export default defineConfig({
139
+ plugins: [htPages()]
140
+ })
141
+ ```
142
+
143
+ ---
144
+
145
+ # Example Project Structure
146
+
147
+ ```
148
+ src/
149
+
150
+ index.ht.js
151
+ about.ht.js
152
+
153
+ blog/
154
+ index.ht.js
155
+ [slug].ht.js
156
+ [year]/[slug].ht.js
157
+
158
+ docs/
159
+ [...slug]?.ht.js
160
+
161
+ (admin)/
162
+ users.ht.js
163
+ ```
164
+
165
+ ---
166
+
167
+ # Routing
168
+
169
+ Routes are generated directly from the filesystem.
170
+
171
+
172
+ | Feature | File | URL |
173
+ |-----|-----|-----|
174
+ | static routes | `index.ht.js` | `/` |
175
+ | dynamic routes | `blog/[slug].ht.js` | `/blog/my-post` |
176
+ | multiple params | `blog/[year]/[slug].ht.js` | `/blog/2026/my-post` |
177
+ | catch-all | `docs/[...slug].ht.js` | `/docs/api/auth/login` |
178
+ | optional catch-all | `docs/[...slug]?.ht.js` | `/docs` or `/docs/getting-started` |
179
+ | index routes | `products/[product]/index.ht.js` | `/products/iphone-18` |
180
+ | route groups | `(admin)/users.ht.js` | `/users` |
181
+
182
+ ---
183
+
184
+ # Dynamic Routes
185
+
186
+ ```
187
+ src/blog/[slug].ht.js
188
+ ```
189
+
190
+ Matches:
191
+
192
+ ```
193
+ /blog/hello-world
194
+ /blog/my-first-post
195
+ ```
196
+
197
+ Example:
198
+
199
+ ``` js
200
+ import { fragment, html, body, h1 } from 'javascript-to-html'
201
+
202
+ export function generateStaticParams() {
203
+ return [
204
+ { slug: 'hello-world' },
205
+ { slug: 'my-first-post' }
206
+ ]
207
+ }
208
+
209
+ export default ({ params }) => fragment(
210
+ '<!doctype html>',
211
+ html(
212
+ body(
213
+ h1(params.slug)
214
+ )
215
+ )
216
+ )
217
+ ```
218
+
219
+ ---
220
+
221
+ # Multiple Parameters
222
+
223
+ ```
224
+ src/blog/[year]/[slug].ht.js
225
+ ```
226
+
227
+ Matches:
228
+
229
+ ```
230
+ /blog/2026/vite-routing
231
+ /blog/2025/my-first-post
232
+ ```
233
+
234
+ Example:
235
+
236
+ ``` js
237
+ import { fragment, html, body, h1 } from 'javascript-to-html'
238
+
239
+ export function generateStaticParams() {
240
+ return [
241
+ {
242
+ year: 2026,
243
+ slug: "vite-routing"
244
+ },
245
+ {
246
+ year: 2025,
247
+ slug: 'my-first-post'
248
+ }
249
+ ]
250
+ }
251
+
252
+ export default ({ params }) => fragment(
253
+ '<!doctype html>',
254
+ html(
255
+ body(
256
+ h1(params.slug)
257
+ )
258
+ )
259
+ )
260
+ ```
261
+
262
+ ---
263
+
264
+ # Catch-All Routes
265
+
266
+ ```
267
+ src/docs/[...slug].ht.js
268
+ ```
269
+
270
+ Matches:
271
+
272
+ ```
273
+ /docs/api/auth/login
274
+ /docs/guides/rendering/static
275
+ ```
276
+
277
+ Params:
278
+
279
+ ```
280
+ params.slug === "api/auth/login"
281
+ ```
282
+
283
+ ---
284
+
285
+ # Optional Catch-All Routes
286
+
287
+ ```
288
+ src/docs/[...slug]?.ht.js
289
+ ```
290
+
291
+ Matches both:
292
+
293
+ ```
294
+ /docs
295
+ /docs/getting-started
296
+ /docs/api/auth/login
297
+ ```
298
+
299
+ Params:
300
+
301
+ | URL | params.slug |
302
+ |-----|-------------|
303
+ | `/docs` | "" |
304
+ | `/docs/api` | "api" |
305
+ | `/docs/api/auth` | "api/auth" |
306
+
307
+ ---
308
+
309
+ # Route Groups
310
+
311
+ Folders wrapped in parentheses are ignored in URLs.
312
+
313
+ ```
314
+ src/(admin)/users.ht.js
315
+ ```
316
+
317
+ URL:
318
+
319
+ ```
320
+ /users
321
+ ```
322
+
323
+ ---
324
+
325
+ # Index Routes
326
+
327
+ Files named `index.ht.js` map to the parent route.
328
+
329
+ ```
330
+ src/blog/index.ht.js -> /blog
331
+ src/blog/[slug]/index.ht.js -> /blog/my-post
332
+ ```
333
+
334
+ ---
335
+
336
+ # Static Params
337
+
338
+ Dynamic routes can export `generateStaticParams`.
339
+
340
+ ```js
341
+ export function generateStaticParams() {
342
+ return [
343
+ { slug: "hello-world" },
344
+ { slug: "vite-routing" }
345
+ ]
346
+ }
347
+ ```
348
+
349
+ ---
350
+
351
+ # Data Loading
352
+
353
+ Pages can export a `data()` function.
354
+
355
+ ```js
356
+ export async function data({ params }) {
357
+ return { title: params.slug }
358
+ }
359
+ ```
360
+
361
+ ---
362
+
363
+ # Caching
364
+
365
+ Use `fetchAndCache` for HTTP requests during static generation. Responses are
366
+ cached to avoid repeated network calls across page builds.
367
+
368
+ ```js
369
+ import { fetchAndCache } from 'vite-plugin-html-pages'
370
+
371
+ export async function data({ params }) {
372
+ const res = await fetchAndCache(
373
+ `https://api.example.com/posts/${params.slug}`,
374
+ {
375
+ // fetch API options
376
+ },
377
+ { maxAge: 3600 }
378
+ )
379
+ const post = await res.json()
380
+ return { post }
381
+ }
382
+ ```
383
+
384
+ ## Options
385
+
386
+ | Option | Description |
387
+ |--------|-------------|
388
+ | `maxAge` | Cache TTL in seconds (default: 3600) |
389
+ | `cacheKey` | Custom cache key (default: hash of URL + method + headers) |
390
+ | `forceRefresh` | Bypass cache and fetch fresh |
391
+ | `cache` | `'auto'` \| `'memory'` \| `'fs'` \| `'none'` |
392
+
393
+ ## Cache modes
394
+
395
+ - **`auto`** (default): memory in dev, filesystem in production
396
+ - **`memory`**: in-process cache, cleared when the build process exits
397
+ - **`fs`**: persisted under `node_modules/.cache/vite-plugin-html-pages/fetch/`
398
+ - **`none`**: no caching, always fetches
399
+
400
+ Only `GET` requests are cached by default. For other methods, provide a
401
+ `cacheKey` to enable caching.
402
+
403
+ ---
404
+
405
+ # Layouts
406
+
407
+ Reusable layout functions work naturally with HT.js.
408
+
409
+ ``` js
410
+ import { fragment, html, head, body } from 'javascript-to-html'
411
+
412
+ export default (...content) => fragment(
413
+ '<!doctype html>',
414
+ html(
415
+ head(),
416
+ body(
417
+ ...content
418
+ )
419
+ )
420
+ )
421
+ ```
422
+
423
+ ---
424
+
425
+ # Plugin Options
426
+
427
+ | Option | Description |
428
+ |------|------|
429
+ | `pagesDir` | root directory for pages |
430
+ | `include` | page glob |
431
+ | `exclude` | excluded files |
432
+ | `cleanUrls` | `/page/index.html` instead of `/page.html` |
433
+ | `renderConcurrency` | parallel rendering |
434
+ | `renderBatchSize` | batch size |
435
+ | `debug` | enable debug logging |
436
+ | `site` | base URL for sitemap |
437
+ | `rss` | RSS configuration |
438
+
439
+ ---
440
+
441
+ # Debug Mode
442
+
443
+ Enable debug logging when troubleshooting.
444
+
445
+ ```js
446
+ htPages({
447
+ debug: true
448
+ })
449
+ ```
450
+
451
+ Example output:
452
+
453
+ [vite-plugin-html-pages] discovered entries [...]
454
+ [vite-plugin-html-pages] dev pages [...]
455
+ [vite-plugin-html-pages] render bundle ...
456
+
457
+ This helps diagnose routing or build issues without modifying plugin
458
+ code.
459
+
460
+ ---
461
+
462
+ # Automatic Sitemap
463
+
464
+ A `sitemap.xml` is generated automatically.
465
+
466
+ ```
467
+ dist/sitemap.xml
468
+ ```
469
+
470
+ ---
471
+
472
+ # Optional RSS Feed
473
+
474
+ ```js
475
+ htPages({
476
+ rss: {
477
+ site: "https://example.com",
478
+ title: "My Blog",
479
+ description: "Latest posts",
480
+ routePrefix: "/blog"
481
+ }
482
+ })
483
+ ```
484
+
485
+ Produces:
486
+
487
+ ```
488
+ dist/rss.xml
489
+ ```
490
+
491
+ ---
492
+
493
+ # Performance
494
+
495
+ Large sites can increase concurrency:
496
+
497
+ ```js
498
+ htPages({
499
+ renderConcurrency: 16,
500
+ renderBatchSize: 128
501
+ })
502
+ ```
503
+
504
+ ---
505
+
506
+ # Comparison
507
+
508
+ | Tool | Focus |
509
+ |-----|-----|
510
+ | Astro | component‑based SSG |
511
+ | Next.js | React SSR framework |
512
+ | vite-plugin-html-pages | minimal HTML SSG for Vite |
513
+
514
+ ---
515
+
516
+ # Use Cases
517
+
518
+ `vite-plugin-html-pages` works well for:
519
+
520
+ - **Vite static site generation**
521
+ - **File-based routing with Vite**
522
+ - **Generating static HTML with Vite**
523
+ - **Vite blog generators**
524
+ - **Documentation sites**
525
+ - **Minimal static site generators**
526
+ - **HTML‑first Vite projects**
527
+
528
+ ---
529
+
530
+ # License
531
+
532
+ MIT
package/TODO ADDED
File without changes
@@ -0,0 +1,63 @@
1
+ import { Plugin as Plugin$1 } from 'vite';
2
+ import { Plugin } from 'rollup';
3
+
4
+ interface StaticParamRecord {
5
+ [key: string]: string | number | boolean;
6
+ }
7
+ interface HtPageInfo {
8
+ id: string;
9
+ entryPath: string;
10
+ absolutePath: string;
11
+ relativePath: string;
12
+ routePattern: string;
13
+ routePath: string;
14
+ fileName: string;
15
+ dynamic: boolean;
16
+ paramNames: string[];
17
+ params: Record<string, string>;
18
+ }
19
+ interface HtPageRenderContext {
20
+ page: HtPageInfo;
21
+ params: Record<string, string>;
22
+ data?: unknown;
23
+ dev: boolean;
24
+ }
25
+ interface HtPageModule {
26
+ default?: string | ((ctx: HtPageRenderContext) => string | Promise<string>);
27
+ data?: (ctx: HtPageRenderContext) => unknown | Promise<unknown>;
28
+ generateStaticParams?: () => StaticParamRecord[] | Promise<StaticParamRecord[]>;
29
+ dynamic?: boolean;
30
+ prerender?: boolean;
31
+ }
32
+ interface HtPagesPluginOptions {
33
+ root?: string;
34
+ include?: string | string[];
35
+ exclude?: string | string[];
36
+ pagesDir?: string;
37
+ renderConcurrency?: number;
38
+ renderBatchSize?: number;
39
+ cleanUrls?: boolean;
40
+ ssrPlugins?: Plugin[];
41
+ mapOutputPath?: (page: HtPageInfo) => string;
42
+ debug?: boolean;
43
+ site?: string;
44
+ rss?: {
45
+ site: string;
46
+ title?: string;
47
+ description?: string;
48
+ routePrefix?: string;
49
+ };
50
+ }
51
+
52
+ declare function htPages(options?: HtPagesPluginOptions): Plugin$1;
53
+
54
+ type FetchCacheMode = 'auto' | 'memory' | 'fs' | 'none';
55
+ interface FetchAndCacheOptions {
56
+ maxAge?: number;
57
+ cacheKey?: string;
58
+ forceRefresh?: boolean;
59
+ cache?: FetchCacheMode;
60
+ }
61
+ declare function fetchAndCache(input: RequestInfo | URL, init?: RequestInit, options?: FetchAndCacheOptions): Promise<Response>;
62
+
63
+ export { type FetchAndCacheOptions, type HtPageInfo, type HtPageModule, type HtPageRenderContext, type HtPagesPluginOptions, type StaticParamRecord, fetchAndCache, htPages };