vite-plugin-htjs-pages 0.2.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/README.md +629 -0
- package/dist/chunk-XFMAP2PF.js +23177 -0
- package/dist/chunk-XFMAP2PF.js.map +1 -0
- package/dist/index.cjs +23499 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +44 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.js +447 -0
- package/dist/index.js.map +1 -0
- package/dist/watch-KN535AIO.js +6933 -0
- package/dist/watch-KN535AIO.js.map +1 -0
- package/package.json +35 -0
- package/src/dev-server.ts +34 -0
- package/src/discover.ts +38 -0
- package/src/errors.ts +19 -0
- package/src/index.ts +8 -0
- package/src/manifest.ts +25 -0
- package/src/page-index.ts +44 -0
- package/src/path-utils.ts +20 -0
- package/src/plugin.ts +154 -0
- package/src/render-bundle.ts +53 -0
- package/src/render-runtime.ts +27 -0
- package/src/route-utils.ts +114 -0
- package/src/types.ts +41 -0
- package/tsconfig.json +14 -0
- package/tsup.config.ts +9 -0
package/README.md
ADDED
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
# vite-plugin-ht-pages
|
|
2
|
+
|
|
3
|
+
A lightweight Vite plugin for generating static HTML from `*.ht.js` page modules.
|
|
4
|
+
|
|
5
|
+
Pages are written as JavaScript that returns HTML. The plugin turns them into static HTML files during build.
|
|
6
|
+
|
|
7
|
+
This makes it ideal for **blogs, documentation sites, and marketing pages** where you want full control over HTML but still want a modern build pipeline.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Features
|
|
12
|
+
|
|
13
|
+
- File‑based routing
|
|
14
|
+
- Dynamic routes (`[slug].ht.js`)
|
|
15
|
+
- Catch‑all routes (`[...slug].ht.js`)
|
|
16
|
+
- Static params generation (`generateStaticParams()`)
|
|
17
|
+
- Dev server SSR rendering
|
|
18
|
+
- Clean URL support
|
|
19
|
+
- Parallel + batched page rendering
|
|
20
|
+
- Compatible with HT.js style HTML generation
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install vite-plugin-ht-pages --save-dev
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
# Quick Start
|
|
33
|
+
|
|
34
|
+
### 1. Configure Vite
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { defineConfig } from 'vite'
|
|
38
|
+
import { htPages } from 'vite-plugin-ht-pages'
|
|
39
|
+
|
|
40
|
+
export default defineConfig({
|
|
41
|
+
plugins: [htPages()]
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
### 2. Create a page
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
src/index.ht.js
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
import { fragment, html, head, title, body, h1 } from 'javascript-to-html'
|
|
55
|
+
|
|
56
|
+
export default () => fragment(
|
|
57
|
+
'<!doctype html>',
|
|
58
|
+
html(
|
|
59
|
+
head(
|
|
60
|
+
title('hello world')
|
|
61
|
+
),
|
|
62
|
+
body(
|
|
63
|
+
h1('Hello world')
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
### 3. Run dev server
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
vite
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Open:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
http://localhost:5173/
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
### 4. Build static HTML
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
vite build
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Output:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
dist/index.html
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
# Project Structure Example
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
src/
|
|
103
|
+
|
|
104
|
+
index.ht.js
|
|
105
|
+
|
|
106
|
+
blog/
|
|
107
|
+
[slug].ht.js
|
|
108
|
+
|
|
109
|
+
docs/
|
|
110
|
+
[...slug].ht.js
|
|
111
|
+
|
|
112
|
+
templates/
|
|
113
|
+
layout.js
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Build output:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
dist/
|
|
120
|
+
|
|
121
|
+
index.html
|
|
122
|
+
|
|
123
|
+
blog/
|
|
124
|
+
hello-world/index.html
|
|
125
|
+
|
|
126
|
+
docs/
|
|
127
|
+
getting-started/index.html
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
# Page Modules
|
|
133
|
+
|
|
134
|
+
Pages export a function returning HTML using `javascript-to-html` helpers.
|
|
135
|
+
|
|
136
|
+
```js
|
|
137
|
+
import { fragment, html, body, h1 } from 'javascript-to-html'
|
|
138
|
+
|
|
139
|
+
export default () => fragment(
|
|
140
|
+
'<!doctype html>',
|
|
141
|
+
html(
|
|
142
|
+
body(
|
|
143
|
+
h1('Hello')
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Async rendering works too:
|
|
150
|
+
|
|
151
|
+
```js
|
|
152
|
+
import { fragment, html, body, h1 } from 'javascript-to-html'
|
|
153
|
+
|
|
154
|
+
export default async () => {
|
|
155
|
+
const post = await loadPost()
|
|
156
|
+
|
|
157
|
+
return fragment(
|
|
158
|
+
'<!doctype html>',
|
|
159
|
+
html(
|
|
160
|
+
body(
|
|
161
|
+
h1(post.title)
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
# Data Loading
|
|
171
|
+
|
|
172
|
+
Pages can export a `data()` function.
|
|
173
|
+
|
|
174
|
+
```js
|
|
175
|
+
import { fragment, html, body, h1 } from 'javascript-to-html'
|
|
176
|
+
|
|
177
|
+
export async function data({ params }) {
|
|
178
|
+
return {
|
|
179
|
+
title: params.slug
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export default ({ data }) => fragment(
|
|
184
|
+
'<!doctype html>',
|
|
185
|
+
html(
|
|
186
|
+
body(
|
|
187
|
+
h1(data.title)
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
# Dynamic Routes
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
src/blog/[slug].ht.js
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```js
|
|
202
|
+
import { fragment, html, body, h1 } from 'javascript-to-html'
|
|
203
|
+
|
|
204
|
+
export function generateStaticParams() {
|
|
205
|
+
return [
|
|
206
|
+
{ slug: 'hello-world' },
|
|
207
|
+
{ slug: 'deep-dive' }
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export default ({ params }) => fragment(
|
|
212
|
+
'<!doctype html>',
|
|
213
|
+
html(
|
|
214
|
+
body(
|
|
215
|
+
h1(params.slug)
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Output:
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
/blog/hello-world/index.html
|
|
225
|
+
/blog/deep-dive/index.html
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
# Catch‑All Routes
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
src/docs/[...slug].ht.js
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
import { fragment, html, body, h1 } from 'javascript-to-html'
|
|
238
|
+
|
|
239
|
+
export function generateStaticParams() {
|
|
240
|
+
return [
|
|
241
|
+
{ slug: 'getting-started' },
|
|
242
|
+
{ slug: 'api/auth/login' }
|
|
243
|
+
]
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export default ({ params }) => fragment(
|
|
247
|
+
'<!doctype html>',
|
|
248
|
+
html(
|
|
249
|
+
body(
|
|
250
|
+
h1(`Docs: ${params.slug}`)
|
|
251
|
+
)
|
|
252
|
+
)
|
|
253
|
+
)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Output:
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
/docs/getting-started/index.html
|
|
260
|
+
/docs/api/auth/login/index.html
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
# Routing Rules
|
|
266
|
+
|
|
267
|
+
Routes are automatically sorted so specific routes win.
|
|
268
|
+
|
|
269
|
+
| Type | Example | Priority |
|
|
270
|
+
|-----|------|------|
|
|
271
|
+
| Static | `/blog/new` | Highest |
|
|
272
|
+
| Dynamic | `/blog/:slug` | Medium |
|
|
273
|
+
| Catch‑all | `/docs/*:slug` | Lowest |
|
|
274
|
+
|
|
275
|
+
This prevents dynamic routes from accidentally overriding static pages.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
# Plugin Options
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
htPages({
|
|
283
|
+
cleanUrls: true,
|
|
284
|
+
renderConcurrency: 8,
|
|
285
|
+
renderBatchSize: 64
|
|
286
|
+
})
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
| Option | Description |
|
|
290
|
+
|------|------|
|
|
291
|
+
| `cleanUrls` | `/page/index.html` instead of `/page.html` |
|
|
292
|
+
| `renderConcurrency` | concurrent page renders |
|
|
293
|
+
| `renderBatchSize` | render batch size |
|
|
294
|
+
| `include` | page file glob |
|
|
295
|
+
| `exclude` | excluded globs |
|
|
296
|
+
| `pagesDir` | route root directory |
|
|
297
|
+
| `mapOutputPath` | customize output path |
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
# HT.js Example
|
|
302
|
+
|
|
303
|
+
```js
|
|
304
|
+
import { fragment, html, body, h1, p } from 'javascript-to-html'
|
|
305
|
+
|
|
306
|
+
export default () => fragment(
|
|
307
|
+
'<!doctype html>',
|
|
308
|
+
html(
|
|
309
|
+
body(
|
|
310
|
+
h1('Hello'),
|
|
311
|
+
p('Welcome to my site')
|
|
312
|
+
)
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
# Example Blog Page
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
src/blog/[slug].ht.js
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
```js
|
|
326
|
+
import { fragment, html, body, article, h1, p } from 'javascript-to-html'
|
|
327
|
+
|
|
328
|
+
export function generateStaticParams() {
|
|
329
|
+
return [
|
|
330
|
+
{ slug: 'my-first-post' },
|
|
331
|
+
{ slug: 'another-post' }
|
|
332
|
+
]
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export async function data({ params }) {
|
|
336
|
+
const post = await loadPost(params.slug)
|
|
337
|
+
return { post }
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export default ({ data }) => fragment(
|
|
341
|
+
'<!doctype html>',
|
|
342
|
+
html(
|
|
343
|
+
body(
|
|
344
|
+
article(
|
|
345
|
+
h1(data.post.title),
|
|
346
|
+
p(data.post.content)
|
|
347
|
+
)
|
|
348
|
+
)
|
|
349
|
+
)
|
|
350
|
+
)
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
# Best Practices
|
|
356
|
+
|
|
357
|
+
### Keep layouts reusable
|
|
358
|
+
|
|
359
|
+
```
|
|
360
|
+
src/templates/layout.js
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Pages import layouts instead of duplicating HTML.
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
### Keep data loading separate
|
|
368
|
+
|
|
369
|
+
Prefer this:
|
|
370
|
+
|
|
371
|
+
```js
|
|
372
|
+
export async function data() {}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Instead of heavy logic in the render function.
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
### Prefer deterministic builds
|
|
380
|
+
|
|
381
|
+
Dynamic pages should use `generateStaticParams()` so builds are predictable.
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
# Layouts
|
|
386
|
+
|
|
387
|
+
A common HT.js pattern is creating reusable layout templates.
|
|
388
|
+
|
|
389
|
+
```
|
|
390
|
+
src/templates/layout.js
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
Example layout:
|
|
394
|
+
|
|
395
|
+
```js
|
|
396
|
+
import { fragment, html, head, body } from 'javascript-to-html'
|
|
397
|
+
|
|
398
|
+
export default (...content) => fragment(
|
|
399
|
+
'<!doctype html>',
|
|
400
|
+
html(
|
|
401
|
+
head(),
|
|
402
|
+
body(
|
|
403
|
+
...content
|
|
404
|
+
)
|
|
405
|
+
)
|
|
406
|
+
)
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Then use the layout in a page:
|
|
410
|
+
|
|
411
|
+
```js
|
|
412
|
+
import { h1 } from 'javascript-to-html'
|
|
413
|
+
import layout from '../templates/layout.js'
|
|
414
|
+
|
|
415
|
+
export default () => layout(
|
|
416
|
+
h1('Home page')
|
|
417
|
+
)
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
This keeps page files small while sharing global structure.
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
# Reusable Components
|
|
425
|
+
|
|
426
|
+
HT.js works well with small reusable components.
|
|
427
|
+
|
|
428
|
+
Example component:
|
|
429
|
+
|
|
430
|
+
```
|
|
431
|
+
src/components/nav.js
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
```js
|
|
435
|
+
import { nav, a } from 'javascript-to-html'
|
|
436
|
+
|
|
437
|
+
export default () => nav(
|
|
438
|
+
a({ href: '/' }, 'Home'),
|
|
439
|
+
a({ href: '/blog' }, 'Blog')
|
|
440
|
+
)
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
Use inside pages or layouts:
|
|
444
|
+
|
|
445
|
+
```js
|
|
446
|
+
import { fragment, html, body, main, h1 } from 'javascript-to-html'
|
|
447
|
+
import nav from '../components/nav.js'
|
|
448
|
+
|
|
449
|
+
export default () => fragment(
|
|
450
|
+
'<!doctype html>',
|
|
451
|
+
html(
|
|
452
|
+
body(
|
|
453
|
+
nav(),
|
|
454
|
+
main(
|
|
455
|
+
h1('Welcome')
|
|
456
|
+
)
|
|
457
|
+
)
|
|
458
|
+
)
|
|
459
|
+
)
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Breaking UI into small components keeps templates maintainable and mirrors the approach used by component frameworks while remaining pure HTML generation.
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
# Performance Tips
|
|
467
|
+
|
|
468
|
+
Large sites (500+ pages) should increase batching:
|
|
469
|
+
|
|
470
|
+
```ts
|
|
471
|
+
htPages({
|
|
472
|
+
renderConcurrency: 16,
|
|
473
|
+
renderBatchSize: 128
|
|
474
|
+
})
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
This keeps memory usage stable during builds.
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
# Comparison
|
|
482
|
+
|
|
483
|
+
| Tool | Focus |
|
|
484
|
+
|----|----|
|
|
485
|
+
| Astro | full framework |
|
|
486
|
+
| Next.js | SSR framework |
|
|
487
|
+
| vite-plugin-ht-pages | minimal static HTML generation |
|
|
488
|
+
|
|
489
|
+
This plugin intentionally stays **small and unopinionated**.
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
# FAQ
|
|
494
|
+
|
|
495
|
+
### Can I use React/Vue inside pages?
|
|
496
|
+
|
|
497
|
+
Technically yes, but this plugin is intended for **HTML generation**, not SPA rendering.
|
|
498
|
+
|
|
499
|
+
### Can I add layouts?
|
|
500
|
+
|
|
501
|
+
Yes — just import shared functions.
|
|
502
|
+
|
|
503
|
+
```
|
|
504
|
+
import layout from '../templates/layout.js'
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### Does it support thousands of pages?
|
|
508
|
+
|
|
509
|
+
Yes. Batched rendering keeps builds stable even for very large sites.
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
# License
|
|
514
|
+
|
|
515
|
+
```
|
|
516
|
+
MIT
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## Example dynamic page
|
|
522
|
+
|
|
523
|
+
```js
|
|
524
|
+
// src/blog/[slug].ht.js
|
|
525
|
+
import { fragment, html, head, title, body, main, h1 } from 'javascript-to-html'
|
|
526
|
+
|
|
527
|
+
export function generateStaticParams() {
|
|
528
|
+
return [
|
|
529
|
+
{ slug: 'hello-world' },
|
|
530
|
+
{ slug: 'deep-dive' },
|
|
531
|
+
];
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
export async function data({ params }) {
|
|
535
|
+
return {
|
|
536
|
+
title: params.slug,
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
export default ({ data }) => fragment(
|
|
541
|
+
'<!doctype html>',
|
|
542
|
+
html(
|
|
543
|
+
head(
|
|
544
|
+
title(data.title)
|
|
545
|
+
),
|
|
546
|
+
body(
|
|
547
|
+
main(
|
|
548
|
+
h1(data.title)
|
|
549
|
+
)
|
|
550
|
+
)
|
|
551
|
+
)
|
|
552
|
+
);
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## Example catch-all page
|
|
558
|
+
|
|
559
|
+
```js
|
|
560
|
+
// src/docs/[...slug].ht.js
|
|
561
|
+
import { fragment, html, head, title, body, main, h1 } from 'javascript-to-html'
|
|
562
|
+
|
|
563
|
+
export function generateStaticParams() {
|
|
564
|
+
return [
|
|
565
|
+
{ slug: 'getting-started' },
|
|
566
|
+
{ slug: 'api/auth/login' },
|
|
567
|
+
{ slug: 'guides/rendering/static' },
|
|
568
|
+
];
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
export default ({ params }) => fragment(
|
|
572
|
+
'<!doctype html>',
|
|
573
|
+
html(
|
|
574
|
+
head(
|
|
575
|
+
title(params.slug)
|
|
576
|
+
),
|
|
577
|
+
body(
|
|
578
|
+
main(
|
|
579
|
+
h1(params.slug)
|
|
580
|
+
)
|
|
581
|
+
)
|
|
582
|
+
)
|
|
583
|
+
);
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
## Notes
|
|
589
|
+
|
|
590
|
+
### Route precedence
|
|
591
|
+
|
|
592
|
+
Given both:
|
|
593
|
+
|
|
594
|
+
- `src/blog/new.ht.js`
|
|
595
|
+
- `src/blog/[slug].ht.js`
|
|
596
|
+
|
|
597
|
+
`/blog/new` will match the static page first.
|
|
598
|
+
|
|
599
|
+
### Catch-all routes
|
|
600
|
+
|
|
601
|
+
`src/docs/[...slug].ht.js` matches nested paths and expects `generateStaticParams()` to provide values like:
|
|
602
|
+
|
|
603
|
+
- `{ slug: 'getting-started' }`
|
|
604
|
+
- `{ slug: 'api/auth/login' }`
|
|
605
|
+
|
|
606
|
+
### Batched rendering
|
|
607
|
+
|
|
608
|
+
Large builds are processed in chunks for lower peak memory and more stable execution.
|
|
609
|
+
|
|
610
|
+
---
|
|
611
|
+
|
|
612
|
+
## What this version does well
|
|
613
|
+
|
|
614
|
+
- very small mental model
|
|
615
|
+
- static routes beat dynamic routes
|
|
616
|
+
- catch-all routes are supported
|
|
617
|
+
- no Node per-page dynamic-import loop in build mode
|
|
618
|
+
- static params are first-class
|
|
619
|
+
- dev mode stays simple by letting Vite SSR-load the page module directly
|
|
620
|
+
- build mode is calmer for large page counts
|
|
621
|
+
|
|
622
|
+
## What it intentionally does not do
|
|
623
|
+
|
|
624
|
+
- no custom DOM patch HMR
|
|
625
|
+
- no incremental dependency invalidation
|
|
626
|
+
- no smart route groups or layout system
|
|
627
|
+
- no optional catch-all routes yet
|
|
628
|
+
|
|
629
|
+
That tradeoff is deliberate: this is a strong small-core version to prototype or publish from.
|