vite-react-ssg 0.6.2 → 0.6.3
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 +21 -21
- package/README.md +555 -555
- package/bin/vite-react-ssg.js +3 -3
- package/dist/chunks/jsdomGlobal.cjs +80 -79
- package/dist/chunks/jsdomGlobal.mjs +80 -79
- package/dist/client/single-page.cjs +1 -1
- package/dist/client/single-page.mjs +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/node/cli.cjs +1 -1
- package/dist/node/cli.mjs +1 -1
- package/dist/node.cjs +5 -3
- package/dist/node.mjs +6 -4
- package/dist/shared/{vite-react-ssg.a009fbf1.mjs → vite-react-ssg.579feabb.mjs} +1 -1
- package/dist/shared/{vite-react-ssg.e6991406.cjs → vite-react-ssg.bee8a5a9.cjs} +1 -1
- package/package.json +8 -13
package/README.md
CHANGED
|
@@ -1,555 +1,555 @@
|
|
|
1
|
-
# Vite React SSG
|
|
2
|
-
|
|
3
|
-
Static-site generation for React on Vite.
|
|
4
|
-
|
|
5
|
-
See demo(also document): [docs](https://vite-react-ssg.netlify.app/)
|
|
6
|
-
|
|
7
|
-
**🎈 Support for [`@tanstack/router`](https://tanstack.com/router/latest/docs/framework/react/overview) and [`wouter`](https://github.com/molefrog/wouter) is in progress!**
|
|
8
|
-
|
|
9
|
-
[](https://www.npmjs.com/package/vite-react-ssg)
|
|
10
|
-
|
|
11
|
-
# Table of contents
|
|
12
|
-
|
|
13
|
-
- [Usage](#usage)
|
|
14
|
-
- [Use CSR during development](#use-csr-during-development)
|
|
15
|
-
- [Extra route options](#extra-route-options)
|
|
16
|
-
- [`entry`](#entry)
|
|
17
|
-
- [`getStaticPaths`](#getstaticpaths)
|
|
18
|
-
- [Data fetch](#data-fetch)
|
|
19
|
-
- [lazy](#lazy)
|
|
20
|
-
- [`<ClientOnly/>`](#clientonly)
|
|
21
|
-
- [Document head](#document-head)
|
|
22
|
-
- [Reactive head](#reactive-head)
|
|
23
|
-
- [Public Base Path](#public-base-path)
|
|
24
|
-
- [CSS in JS](#css-in-js)
|
|
25
|
-
- [Critical CSS](#critical-css)
|
|
26
|
-
- [Configuration](#configuration)
|
|
27
|
-
- [Custom Routes to Render](#custom-routes-to-render)
|
|
28
|
-
- [Https](#https)
|
|
29
|
-
- [Roadmap](#roadmap)
|
|
30
|
-
- [Credits](#credits)
|
|
31
|
-
|
|
32
|
-
## Usage
|
|
33
|
-
|
|
34
|
-
<pre>
|
|
35
|
-
<b>npm i -D vite-react-ssg</b> <em>react-router-dom</em>
|
|
36
|
-
</pre>
|
|
37
|
-
|
|
38
|
-
```diff
|
|
39
|
-
// package.json
|
|
40
|
-
{
|
|
41
|
-
"scripts": {
|
|
42
|
-
- "build": "vite build"
|
|
43
|
-
+ "build": "vite-react-ssg build"
|
|
44
|
-
// If you need ssr when dev
|
|
45
|
-
- "dev": "vite",
|
|
46
|
-
+ "dev": "vite-react-ssg dev",
|
|
47
|
-
|
|
48
|
-
// OR if you want to use another vite config file
|
|
49
|
-
+ "build": "vite-react-ssg build -c another-vite.config.ts"
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
```ts
|
|
55
|
-
// src/main.ts
|
|
56
|
-
import { ViteReactSSG } from 'vite-react-ssg'
|
|
57
|
-
import routes from './App.tsx'
|
|
58
|
-
|
|
59
|
-
export const createRoot = ViteReactSSG(
|
|
60
|
-
// react-router-dom data routes
|
|
61
|
-
{ routes },
|
|
62
|
-
// function to have custom setups
|
|
63
|
-
({ router, routes, isClient, initialState }) => {
|
|
64
|
-
// do something.
|
|
65
|
-
},
|
|
66
|
-
)
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
```tsx
|
|
70
|
-
// src/App.tsx
|
|
71
|
-
import React from 'react'
|
|
72
|
-
import type { RouteRecord } from 'vite-react-ssg'
|
|
73
|
-
import './App.css'
|
|
74
|
-
|
|
75
|
-
const Layout = React.lazy(() => import('./Layout'))
|
|
76
|
-
|
|
77
|
-
export const routes: RouteRecord[] = [
|
|
78
|
-
{
|
|
79
|
-
path: '/',
|
|
80
|
-
element: <Layout />,
|
|
81
|
-
entry: 'src/Layout.tsx',
|
|
82
|
-
children: [
|
|
83
|
-
{
|
|
84
|
-
path: 'a',
|
|
85
|
-
Component: React.lazy(() => import('./pages/a')),
|
|
86
|
-
entry: 'src/pages/a.tsx',
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
index: true,
|
|
90
|
-
Component: React.lazy(() => import('./pages/index')),
|
|
91
|
-
// Used to obtain static resources through manifest
|
|
92
|
-
entry: 'src/pages/index.tsx',
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
path: 'nest/:b',
|
|
96
|
-
Component: React.lazy(() => import('./pages/nest/[b]')),
|
|
97
|
-
entry: 'src/pages/nest/[b].tsx',
|
|
98
|
-
// To determine which paths will be pre-rendered
|
|
99
|
-
getStaticPaths: () => ['nest/b1', 'nest/b2'],
|
|
100
|
-
},
|
|
101
|
-
],
|
|
102
|
-
},
|
|
103
|
-
]
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### Use CSR during development
|
|
107
|
-
|
|
108
|
-
Vite React SSG provide SSR (Server-Side Rendering) during development to ensure consistency between development and production as much as possible.
|
|
109
|
-
|
|
110
|
-
But if you want to use CSR during development, just:
|
|
111
|
-
|
|
112
|
-
```diff
|
|
113
|
-
// package.json
|
|
114
|
-
{
|
|
115
|
-
"scripts": {
|
|
116
|
-
- "dev": "vite-react-ssg dev",
|
|
117
|
-
+ "dev": "vite",
|
|
118
|
-
"build": "vite-react-ssg build"
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### Single Page SSG
|
|
124
|
-
|
|
125
|
-
For SSG of an index page only (i.e. without `react-router-dom`); import `vite-react-ssg/single-page` instead.
|
|
126
|
-
|
|
127
|
-
```tsx
|
|
128
|
-
// src/main.tsx
|
|
129
|
-
import { ViteReactSSG } from 'vite-react-ssg/single-page'
|
|
130
|
-
import App from './App.tsx'
|
|
131
|
-
|
|
132
|
-
export const createRoot = ViteReactSSG(<App />)
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
## Extra route options
|
|
136
|
-
|
|
137
|
-
The RouteObject of vite-react-ssg is based on react-router, and vite-react-ssg receives some additional properties.
|
|
138
|
-
|
|
139
|
-
#### `entry`
|
|
140
|
-
|
|
141
|
-
Used to obtain static resources.If you introduce static resources (such as css files) in that route and use lazy loading (such as React.lazy or route.lazy), you should set the entry field.
|
|
142
|
-
It should be the path from root to the target file.
|
|
143
|
-
|
|
144
|
-
eg: `src/pages/page1.tsx`
|
|
145
|
-
|
|
146
|
-
#### `getStaticPaths`
|
|
147
|
-
|
|
148
|
-
The `getStaticPaths()` function should return an array of path
|
|
149
|
-
to determine which paths will be pre-rendered by vite-react-ssg.
|
|
150
|
-
|
|
151
|
-
This function is only valid for dynamic route.
|
|
152
|
-
|
|
153
|
-
```tsx
|
|
154
|
-
const route = {
|
|
155
|
-
path: 'nest/:b',
|
|
156
|
-
Component: React.lazy(() => import('./pages/nest/[b]')),
|
|
157
|
-
entry: 'src/pages/nest/[b].tsx',
|
|
158
|
-
// To determine which paths will be pre-rendered
|
|
159
|
-
getStaticPaths: () => ['nest/b1', 'nest/b2'],
|
|
160
|
-
},
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
## lazy
|
|
164
|
-
|
|
165
|
-
These options work well with the `lazy` field.
|
|
166
|
-
|
|
167
|
-
```tsx
|
|
168
|
-
// src/pages/[page].tsx
|
|
169
|
-
export function Component() {
|
|
170
|
-
return (
|
|
171
|
-
<div>{/* your component */}</div>
|
|
172
|
-
)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export function getStaticPaths() {
|
|
176
|
-
return ['page1', 'page2']
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export const entry = 'src/pages/[page].tsx'
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
```ts
|
|
183
|
-
// src/routes.ts
|
|
184
|
-
const routes = [
|
|
185
|
-
{
|
|
186
|
-
path: '/:page',
|
|
187
|
-
lazy: () => import('./pages/[page]')
|
|
188
|
-
}
|
|
189
|
-
]
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
See [example](./examples/lazy-pages/src/App.tsx).
|
|
193
|
-
|
|
194
|
-
## Data fetch
|
|
195
|
-
|
|
196
|
-
You can use react-router-dom's `loader` to fetch data at build time and use `useLoaderData` to get the data in the component.
|
|
197
|
-
|
|
198
|
-
See [example | with-loader](./examples/with-loader/src/pages/[docs].tsx).
|
|
199
|
-
|
|
200
|
-
## `<ClientOnly/>`
|
|
201
|
-
|
|
202
|
-
If you need to render some component in browser only, you can wrap your component with `<ClientOnly>`.
|
|
203
|
-
|
|
204
|
-
```tsx
|
|
205
|
-
import { ClientOnly } from 'vite-react-ssg'
|
|
206
|
-
|
|
207
|
-
function MyComponent() {
|
|
208
|
-
return (
|
|
209
|
-
<ClientOnly>
|
|
210
|
-
{() => {
|
|
211
|
-
return <div>{window.location.href}</div>
|
|
212
|
-
}}
|
|
213
|
-
</ClientOnly>
|
|
214
|
-
)
|
|
215
|
-
}
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
> It's important that the children of `<ClientOnly>` is not a JSX element, but a function that returns an element.
|
|
219
|
-
> Because React will try to render children, and may use the client's API on the server.
|
|
220
|
-
|
|
221
|
-
## Document head
|
|
222
|
-
|
|
223
|
-
You can use `<Head/>` to manage all of your changes to the document head. It takes plain HTML tags and outputs plain HTML tags. It is a wrapper around [React Helmet](https://github.com/nfl/react-helmet).
|
|
224
|
-
|
|
225
|
-
```tsx
|
|
226
|
-
import { Head } from 'vite-react-ssg'
|
|
227
|
-
|
|
228
|
-
function MyHead() {
|
|
229
|
-
return (
|
|
230
|
-
<Head>
|
|
231
|
-
<meta property="og:description" content="My custom description" />
|
|
232
|
-
<meta charSet="utf-8" />
|
|
233
|
-
<title>My Title</title>
|
|
234
|
-
<link rel="canonical" href="http://mysite.com/example" />
|
|
235
|
-
</Head>
|
|
236
|
-
)
|
|
237
|
-
}
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
Nested or latter components will override duplicate usages:
|
|
241
|
-
|
|
242
|
-
```tsx
|
|
243
|
-
import { Head } from 'vite-react-ssg'
|
|
244
|
-
|
|
245
|
-
function MyHead() {
|
|
246
|
-
return (
|
|
247
|
-
<parent>
|
|
248
|
-
<Head>
|
|
249
|
-
<title>My Title</title>
|
|
250
|
-
<meta name="description" content="Helmet application" />
|
|
251
|
-
</Head>
|
|
252
|
-
<child>
|
|
253
|
-
<Head>
|
|
254
|
-
<title>Nested Title</title>
|
|
255
|
-
<meta name="description" content="Nested component" />
|
|
256
|
-
</Head>
|
|
257
|
-
</child>
|
|
258
|
-
</parent>
|
|
259
|
-
)
|
|
260
|
-
}
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
Outputs:
|
|
264
|
-
|
|
265
|
-
```html
|
|
266
|
-
<head>
|
|
267
|
-
<title>Nested Title</title>
|
|
268
|
-
<meta name="description" content="Nested component" />
|
|
269
|
-
</head>
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
### Reactive head
|
|
273
|
-
|
|
274
|
-
```tsx
|
|
275
|
-
import { useState } from 'react'
|
|
276
|
-
import { Head } from 'vite-react-ssg'
|
|
277
|
-
|
|
278
|
-
export default function MyHead() {
|
|
279
|
-
const [state, setState] = useState(false)
|
|
280
|
-
|
|
281
|
-
return (
|
|
282
|
-
<Head>
|
|
283
|
-
<meta charSet="UTF-8" />
|
|
284
|
-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
285
|
-
<title>head test {state ? 'A' : 'B'}</title>
|
|
286
|
-
{/* You can also set the 'body' attributes here */}
|
|
287
|
-
<body className={`body-class-in-head-${state ? 'a' : 'b'}`} />
|
|
288
|
-
</Head>
|
|
289
|
-
)
|
|
290
|
-
}
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
## Public Base Path
|
|
294
|
-
|
|
295
|
-
Just set `base` in vite.config.ts like:
|
|
296
|
-
|
|
297
|
-
```ts
|
|
298
|
-
// vite.config.ts
|
|
299
|
-
import { defineConfig } from 'vite'
|
|
300
|
-
import react from '@vitejs/plugin-react-swc'
|
|
301
|
-
|
|
302
|
-
// https://vitejs.dev/config/
|
|
303
|
-
export default defineConfig({
|
|
304
|
-
plugins: [react()],
|
|
305
|
-
base: '/base-path',
|
|
306
|
-
})
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
```ts
|
|
310
|
-
// main.ts
|
|
311
|
-
import { ViteReactSSG } from 'vite-react-ssg'
|
|
312
|
-
import { routes } from './App'
|
|
313
|
-
import './index.css'
|
|
314
|
-
|
|
315
|
-
export const createRoot = ViteReactSSG(
|
|
316
|
-
{
|
|
317
|
-
routes,
|
|
318
|
-
// pass your BASE_URL
|
|
319
|
-
basename: import.meta.env.BASE_URL,
|
|
320
|
-
},
|
|
321
|
-
)
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
Vite React SSG will give it to the react-router's `basename`.
|
|
325
|
-
|
|
326
|
-
See: [react-router's create-browser-router](https://reactrouter.com/en/main/routers/create-browser-router#basename)
|
|
327
|
-
|
|
328
|
-
[Example](./examples/lazy-pages/vite.config.ts)
|
|
329
|
-
|
|
330
|
-
## CSS in JS
|
|
331
|
-
|
|
332
|
-
Use the `getStyleCollector` option to specify an SSR/SSG style collector. Currently only supports `styled-components`.
|
|
333
|
-
|
|
334
|
-
```tsx
|
|
335
|
-
import { ViteReactSSG } from 'vite-react-ssg'
|
|
336
|
-
import getStyledComponentsCollector from 'vite-react-ssg/style-collectors/styled-components'
|
|
337
|
-
import { routes } from './App.js'
|
|
338
|
-
import './index.css'
|
|
339
|
-
|
|
340
|
-
export const createRoot = ViteReactSSG(
|
|
341
|
-
{ routes },
|
|
342
|
-
() => { },
|
|
343
|
-
{ getStyleCollector: getStyledComponentsCollector }
|
|
344
|
-
)
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
You can provide your own by looking at the [implementation](./src/style-collectors/) of any of the existing collectors.
|
|
348
|
-
|
|
349
|
-
## Critical CSS
|
|
350
|
-
|
|
351
|
-
Vite React SSG has built-in support for generating [Critical CSS](https://web.dev/extract-critical-css/) inlined in the HTML via the [`critters`](https://github.com/GoogleChromeLabs/critters) package.
|
|
352
|
-
Install it with:
|
|
353
|
-
|
|
354
|
-
```bash
|
|
355
|
-
npm i -D critters
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
Critical CSS generation will automatically be enabled for you.
|
|
359
|
-
|
|
360
|
-
To configure `critters`, pass [its options](https://github.com/GoogleChromeLabs/critters#usage) into `ssgOptions.crittersOptions` in `vite.config.ts`:
|
|
361
|
-
|
|
362
|
-
```ts
|
|
363
|
-
// vite.config.ts
|
|
364
|
-
export default defineConfig({
|
|
365
|
-
ssgOptions: {
|
|
366
|
-
crittersOptions: {
|
|
367
|
-
// E.g., change the preload strategy
|
|
368
|
-
preload: 'media',
|
|
369
|
-
// Other options: https://github.com/GoogleChromeLabs/critters#usage
|
|
370
|
-
},
|
|
371
|
-
},
|
|
372
|
-
})
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
## Configuration
|
|
376
|
-
|
|
377
|
-
You can pass options to Vite SSG in the `ssgOptions` field of your `vite.config.js`
|
|
378
|
-
|
|
379
|
-
```js
|
|
380
|
-
// vite.config.js
|
|
381
|
-
|
|
382
|
-
export default {
|
|
383
|
-
plugins: [],
|
|
384
|
-
ssgOptions: {
|
|
385
|
-
script: 'async',
|
|
386
|
-
},
|
|
387
|
-
}
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
```ts
|
|
391
|
-
interface ViteReactSSGOptions {
|
|
392
|
-
/**
|
|
393
|
-
* Set the scripts' loading mode. Only works for `type="module"`.
|
|
394
|
-
*
|
|
395
|
-
* @default 'sync'
|
|
396
|
-
*/
|
|
397
|
-
script?: 'sync' | 'async' | 'defer' | 'async defer'
|
|
398
|
-
/**
|
|
399
|
-
* Build format.
|
|
400
|
-
*
|
|
401
|
-
* @default 'esm'
|
|
402
|
-
*/
|
|
403
|
-
format?: 'esm' | 'cjs'
|
|
404
|
-
/**
|
|
405
|
-
* The path of the main entry file (relative to the project root).
|
|
406
|
-
*
|
|
407
|
-
* @default 'src/main.ts'
|
|
408
|
-
*/
|
|
409
|
-
entry?: string
|
|
410
|
-
/**
|
|
411
|
-
* Mock browser global variables (window, document, etc...) from SSG.
|
|
412
|
-
*
|
|
413
|
-
* @default false
|
|
414
|
-
*/
|
|
415
|
-
mock?: boolean
|
|
416
|
-
/**
|
|
417
|
-
* Apply formatter to the generated index file.
|
|
418
|
-
*
|
|
419
|
-
* **It will cause Hydration Failed.**
|
|
420
|
-
*
|
|
421
|
-
* @default 'none'
|
|
422
|
-
*/
|
|
423
|
-
formatting?: 'minify' | 'prettify' | 'none'
|
|
424
|
-
/**
|
|
425
|
-
* Vite environmeng mode.
|
|
426
|
-
*/
|
|
427
|
-
mode?: string
|
|
428
|
-
/**
|
|
429
|
-
* Directory style of the output directory.
|
|
430
|
-
*
|
|
431
|
-
* flat: `/foo` -> `/foo.html`
|
|
432
|
-
* nested: `/foo` -> `/foo/index.html`
|
|
433
|
-
*
|
|
434
|
-
* @default 'flat'
|
|
435
|
-
*/
|
|
436
|
-
dirStyle?: 'flat' | 'nested'
|
|
437
|
-
/**
|
|
438
|
-
* Generate for all routes, including dynamic routes.
|
|
439
|
-
* If enabled, you will need to configGure your serve
|
|
440
|
-
* manually to handle dynamic routes properly.
|
|
441
|
-
*
|
|
442
|
-
* @default false
|
|
443
|
-
*/
|
|
444
|
-
includeAllRoutes?: boolean
|
|
445
|
-
/**
|
|
446
|
-
* Options for the critters packages.
|
|
447
|
-
*
|
|
448
|
-
* @see https://github.com/GoogleChromeLabs/critters
|
|
449
|
-
*/
|
|
450
|
-
crittersOptions?: CrittersOptions | false
|
|
451
|
-
/**
|
|
452
|
-
* Custom function to modify the routes to do the SSG.
|
|
453
|
-
*
|
|
454
|
-
* Works only when `includeAllRoutes` is set to false.
|
|
455
|
-
*
|
|
456
|
-
* Defaults to a handler that filters out all the dynamic routes.
|
|
457
|
-
* When passing your custom handler, you should also take care of the dynamic routes yourself.
|
|
458
|
-
*/
|
|
459
|
-
includedRoutes?: (paths: string[], routes: Readonly<RouteRecord[]>) => Promise<string[]> | string[]
|
|
460
|
-
/**
|
|
461
|
-
* Callback to be called before every page render.
|
|
462
|
-
*
|
|
463
|
-
* It can be used to transform the project's `index.html` file before passing it to the renderer.
|
|
464
|
-
*
|
|
465
|
-
* To do so, you can change the 'index.html' file contents (passed in through the `indexHTML` parameter), and return it.
|
|
466
|
-
* The returned value will then be passed to renderer.
|
|
467
|
-
*/
|
|
468
|
-
onBeforePageRender?: (route: string, indexHTML: string, appCtx: ViteReactSSGContext<true>) => Promise<string | null | undefined> | string | null | undefined
|
|
469
|
-
/**
|
|
470
|
-
* Callback to be called on every rendered page.
|
|
471
|
-
*
|
|
472
|
-
* It can be used to transform the current route's rendered HTML.
|
|
473
|
-
*
|
|
474
|
-
* To do so, you can transform the route's rendered HTML (passed in through the `renderedHTML` parameter), and return it.
|
|
475
|
-
* The returned value will be used as the HTML of the route.
|
|
476
|
-
*/
|
|
477
|
-
onPageRendered?: (route: string, renderedHTML: string, appCtx: ViteReactSSGContext<true>) => Promise<string | null | undefined> | string | null | undefined
|
|
478
|
-
|
|
479
|
-
onFinished?: () => Promise<void> | void
|
|
480
|
-
/**
|
|
481
|
-
* The application's root container `id`.
|
|
482
|
-
*
|
|
483
|
-
* @default `root`
|
|
484
|
-
*/
|
|
485
|
-
rootContainerId?: string
|
|
486
|
-
/**
|
|
487
|
-
* The size of the SSG processing queue.
|
|
488
|
-
*
|
|
489
|
-
* @default 20
|
|
490
|
-
*/
|
|
491
|
-
concurrency?: number
|
|
492
|
-
}
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
See [src/types.ts](./src/types.ts). for more options available.
|
|
496
|
-
|
|
497
|
-
### Custom Routes to Render
|
|
498
|
-
|
|
499
|
-
You can use the `includedRoutes` hook to include or exclude route paths to render, or even provide some completely custom ones.
|
|
500
|
-
|
|
501
|
-
```js
|
|
502
|
-
// vite.config.js
|
|
503
|
-
|
|
504
|
-
export default {
|
|
505
|
-
plugins: [],
|
|
506
|
-
ssgOptions: {
|
|
507
|
-
includedRoutes(paths, routes) {
|
|
508
|
-
// exclude all the route paths that contains 'foo'
|
|
509
|
-
return paths.filter(i => !i.includes('foo'))
|
|
510
|
-
},
|
|
511
|
-
},
|
|
512
|
-
}
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
```js
|
|
516
|
-
// vite.config.js
|
|
517
|
-
|
|
518
|
-
export default {
|
|
519
|
-
plugins: [],
|
|
520
|
-
ssgOptions: {
|
|
521
|
-
includedRoutes(paths, routes) {
|
|
522
|
-
// use original route records
|
|
523
|
-
return routes.flatMap(route => {
|
|
524
|
-
return route.name === 'Blog'
|
|
525
|
-
? myBlogSlugs.map(slug => `/blog/${slug}`)
|
|
526
|
-
: route.path
|
|
527
|
-
})
|
|
528
|
-
},
|
|
529
|
-
},
|
|
530
|
-
}
|
|
531
|
-
```
|
|
532
|
-
|
|
533
|
-
```ts
|
|
534
|
-
export default defineConfig({
|
|
535
|
-
server: {
|
|
536
|
-
https: true,
|
|
537
|
-
},
|
|
538
|
-
})
|
|
539
|
-
```
|
|
540
|
-
|
|
541
|
-
## Roadmap
|
|
542
|
-
|
|
543
|
-
- [x] Preload assets
|
|
544
|
-
- [x] Document head
|
|
545
|
-
- [x] SSR in dev environment
|
|
546
|
-
- [x] More Client components, such as `<ClientOnly />`
|
|
547
|
-
- [x] `getStaticPaths` for dynamic routes
|
|
548
|
-
|
|
549
|
-
## Credits
|
|
550
|
-
|
|
551
|
-
This project inspired by [vite-ssg](https://github.com/antfu/vite-ssg), thanks to [@antfu](https://github.com/antfu) for his awesome work.
|
|
552
|
-
|
|
553
|
-
## License
|
|
554
|
-
|
|
555
|
-
[MIT](./LICENSE) License © 2023 [Riri](https://github.com/Daydreamer-riri)
|
|
1
|
+
# Vite React SSG
|
|
2
|
+
|
|
3
|
+
Static-site generation for React on Vite.
|
|
4
|
+
|
|
5
|
+
See demo(also document): [docs](https://vite-react-ssg.netlify.app/)
|
|
6
|
+
|
|
7
|
+
**🎈 Support for [`@tanstack/router`](https://tanstack.com/router/latest/docs/framework/react/overview) and [`wouter`](https://github.com/molefrog/wouter) is in progress!**
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/vite-react-ssg)
|
|
10
|
+
|
|
11
|
+
# Table of contents
|
|
12
|
+
|
|
13
|
+
- [Usage](#usage)
|
|
14
|
+
- [Use CSR during development](#use-csr-during-development)
|
|
15
|
+
- [Extra route options](#extra-route-options)
|
|
16
|
+
- [`entry`](#entry)
|
|
17
|
+
- [`getStaticPaths`](#getstaticpaths)
|
|
18
|
+
- [Data fetch](#data-fetch)
|
|
19
|
+
- [lazy](#lazy)
|
|
20
|
+
- [`<ClientOnly/>`](#clientonly)
|
|
21
|
+
- [Document head](#document-head)
|
|
22
|
+
- [Reactive head](#reactive-head)
|
|
23
|
+
- [Public Base Path](#public-base-path)
|
|
24
|
+
- [CSS in JS](#css-in-js)
|
|
25
|
+
- [Critical CSS](#critical-css)
|
|
26
|
+
- [Configuration](#configuration)
|
|
27
|
+
- [Custom Routes to Render](#custom-routes-to-render)
|
|
28
|
+
- [Https](#https)
|
|
29
|
+
- [Roadmap](#roadmap)
|
|
30
|
+
- [Credits](#credits)
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
<pre>
|
|
35
|
+
<b>npm i -D vite-react-ssg</b> <em>react-router-dom</em>
|
|
36
|
+
</pre>
|
|
37
|
+
|
|
38
|
+
```diff
|
|
39
|
+
// package.json
|
|
40
|
+
{
|
|
41
|
+
"scripts": {
|
|
42
|
+
- "build": "vite build"
|
|
43
|
+
+ "build": "vite-react-ssg build"
|
|
44
|
+
// If you need ssr when dev
|
|
45
|
+
- "dev": "vite",
|
|
46
|
+
+ "dev": "vite-react-ssg dev",
|
|
47
|
+
|
|
48
|
+
// OR if you want to use another vite config file
|
|
49
|
+
+ "build": "vite-react-ssg build -c another-vite.config.ts"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// src/main.ts
|
|
56
|
+
import { ViteReactSSG } from 'vite-react-ssg'
|
|
57
|
+
import routes from './App.tsx'
|
|
58
|
+
|
|
59
|
+
export const createRoot = ViteReactSSG(
|
|
60
|
+
// react-router-dom data routes
|
|
61
|
+
{ routes },
|
|
62
|
+
// function to have custom setups
|
|
63
|
+
({ router, routes, isClient, initialState }) => {
|
|
64
|
+
// do something.
|
|
65
|
+
},
|
|
66
|
+
)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
// src/App.tsx
|
|
71
|
+
import React from 'react'
|
|
72
|
+
import type { RouteRecord } from 'vite-react-ssg'
|
|
73
|
+
import './App.css'
|
|
74
|
+
|
|
75
|
+
const Layout = React.lazy(() => import('./Layout'))
|
|
76
|
+
|
|
77
|
+
export const routes: RouteRecord[] = [
|
|
78
|
+
{
|
|
79
|
+
path: '/',
|
|
80
|
+
element: <Layout />,
|
|
81
|
+
entry: 'src/Layout.tsx',
|
|
82
|
+
children: [
|
|
83
|
+
{
|
|
84
|
+
path: 'a',
|
|
85
|
+
Component: React.lazy(() => import('./pages/a')),
|
|
86
|
+
entry: 'src/pages/a.tsx',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
index: true,
|
|
90
|
+
Component: React.lazy(() => import('./pages/index')),
|
|
91
|
+
// Used to obtain static resources through manifest
|
|
92
|
+
entry: 'src/pages/index.tsx',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
path: 'nest/:b',
|
|
96
|
+
Component: React.lazy(() => import('./pages/nest/[b]')),
|
|
97
|
+
entry: 'src/pages/nest/[b].tsx',
|
|
98
|
+
// To determine which paths will be pre-rendered
|
|
99
|
+
getStaticPaths: () => ['nest/b1', 'nest/b2'],
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Use CSR during development
|
|
107
|
+
|
|
108
|
+
Vite React SSG provide SSR (Server-Side Rendering) during development to ensure consistency between development and production as much as possible.
|
|
109
|
+
|
|
110
|
+
But if you want to use CSR during development, just:
|
|
111
|
+
|
|
112
|
+
```diff
|
|
113
|
+
// package.json
|
|
114
|
+
{
|
|
115
|
+
"scripts": {
|
|
116
|
+
- "dev": "vite-react-ssg dev",
|
|
117
|
+
+ "dev": "vite",
|
|
118
|
+
"build": "vite-react-ssg build"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Single Page SSG
|
|
124
|
+
|
|
125
|
+
For SSG of an index page only (i.e. without `react-router-dom`); import `vite-react-ssg/single-page` instead.
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
// src/main.tsx
|
|
129
|
+
import { ViteReactSSG } from 'vite-react-ssg/single-page'
|
|
130
|
+
import App from './App.tsx'
|
|
131
|
+
|
|
132
|
+
export const createRoot = ViteReactSSG(<App />)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Extra route options
|
|
136
|
+
|
|
137
|
+
The RouteObject of vite-react-ssg is based on react-router, and vite-react-ssg receives some additional properties.
|
|
138
|
+
|
|
139
|
+
#### `entry`
|
|
140
|
+
|
|
141
|
+
Used to obtain static resources.If you introduce static resources (such as css files) in that route and use lazy loading (such as React.lazy or route.lazy), you should set the entry field.
|
|
142
|
+
It should be the path from root to the target file.
|
|
143
|
+
|
|
144
|
+
eg: `src/pages/page1.tsx`
|
|
145
|
+
|
|
146
|
+
#### `getStaticPaths`
|
|
147
|
+
|
|
148
|
+
The `getStaticPaths()` function should return an array of path
|
|
149
|
+
to determine which paths will be pre-rendered by vite-react-ssg.
|
|
150
|
+
|
|
151
|
+
This function is only valid for dynamic route.
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
const route = {
|
|
155
|
+
path: 'nest/:b',
|
|
156
|
+
Component: React.lazy(() => import('./pages/nest/[b]')),
|
|
157
|
+
entry: 'src/pages/nest/[b].tsx',
|
|
158
|
+
// To determine which paths will be pre-rendered
|
|
159
|
+
getStaticPaths: () => ['nest/b1', 'nest/b2'],
|
|
160
|
+
},
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## lazy
|
|
164
|
+
|
|
165
|
+
These options work well with the `lazy` field.
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
// src/pages/[page].tsx
|
|
169
|
+
export function Component() {
|
|
170
|
+
return (
|
|
171
|
+
<div>{/* your component */}</div>
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function getStaticPaths() {
|
|
176
|
+
return ['page1', 'page2']
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export const entry = 'src/pages/[page].tsx'
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
// src/routes.ts
|
|
184
|
+
const routes = [
|
|
185
|
+
{
|
|
186
|
+
path: '/:page',
|
|
187
|
+
lazy: () => import('./pages/[page]')
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
See [example](./examples/lazy-pages/src/App.tsx).
|
|
193
|
+
|
|
194
|
+
## Data fetch
|
|
195
|
+
|
|
196
|
+
You can use react-router-dom's `loader` to fetch data at build time and use `useLoaderData` to get the data in the component.
|
|
197
|
+
|
|
198
|
+
See [example | with-loader](./examples/with-loader/src/pages/[docs].tsx).
|
|
199
|
+
|
|
200
|
+
## `<ClientOnly/>`
|
|
201
|
+
|
|
202
|
+
If you need to render some component in browser only, you can wrap your component with `<ClientOnly>`.
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
import { ClientOnly } from 'vite-react-ssg'
|
|
206
|
+
|
|
207
|
+
function MyComponent() {
|
|
208
|
+
return (
|
|
209
|
+
<ClientOnly>
|
|
210
|
+
{() => {
|
|
211
|
+
return <div>{window.location.href}</div>
|
|
212
|
+
}}
|
|
213
|
+
</ClientOnly>
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
> It's important that the children of `<ClientOnly>` is not a JSX element, but a function that returns an element.
|
|
219
|
+
> Because React will try to render children, and may use the client's API on the server.
|
|
220
|
+
|
|
221
|
+
## Document head
|
|
222
|
+
|
|
223
|
+
You can use `<Head/>` to manage all of your changes to the document head. It takes plain HTML tags and outputs plain HTML tags. It is a wrapper around [React Helmet](https://github.com/nfl/react-helmet).
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
import { Head } from 'vite-react-ssg'
|
|
227
|
+
|
|
228
|
+
function MyHead() {
|
|
229
|
+
return (
|
|
230
|
+
<Head>
|
|
231
|
+
<meta property="og:description" content="My custom description" />
|
|
232
|
+
<meta charSet="utf-8" />
|
|
233
|
+
<title>My Title</title>
|
|
234
|
+
<link rel="canonical" href="http://mysite.com/example" />
|
|
235
|
+
</Head>
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Nested or latter components will override duplicate usages:
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
import { Head } from 'vite-react-ssg'
|
|
244
|
+
|
|
245
|
+
function MyHead() {
|
|
246
|
+
return (
|
|
247
|
+
<parent>
|
|
248
|
+
<Head>
|
|
249
|
+
<title>My Title</title>
|
|
250
|
+
<meta name="description" content="Helmet application" />
|
|
251
|
+
</Head>
|
|
252
|
+
<child>
|
|
253
|
+
<Head>
|
|
254
|
+
<title>Nested Title</title>
|
|
255
|
+
<meta name="description" content="Nested component" />
|
|
256
|
+
</Head>
|
|
257
|
+
</child>
|
|
258
|
+
</parent>
|
|
259
|
+
)
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Outputs:
|
|
264
|
+
|
|
265
|
+
```html
|
|
266
|
+
<head>
|
|
267
|
+
<title>Nested Title</title>
|
|
268
|
+
<meta name="description" content="Nested component" />
|
|
269
|
+
</head>
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Reactive head
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
import { useState } from 'react'
|
|
276
|
+
import { Head } from 'vite-react-ssg'
|
|
277
|
+
|
|
278
|
+
export default function MyHead() {
|
|
279
|
+
const [state, setState] = useState(false)
|
|
280
|
+
|
|
281
|
+
return (
|
|
282
|
+
<Head>
|
|
283
|
+
<meta charSet="UTF-8" />
|
|
284
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
285
|
+
<title>head test {state ? 'A' : 'B'}</title>
|
|
286
|
+
{/* You can also set the 'body' attributes here */}
|
|
287
|
+
<body className={`body-class-in-head-${state ? 'a' : 'b'}`} />
|
|
288
|
+
</Head>
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Public Base Path
|
|
294
|
+
|
|
295
|
+
Just set `base` in vite.config.ts like:
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
// vite.config.ts
|
|
299
|
+
import { defineConfig } from 'vite'
|
|
300
|
+
import react from '@vitejs/plugin-react-swc'
|
|
301
|
+
|
|
302
|
+
// https://vitejs.dev/config/
|
|
303
|
+
export default defineConfig({
|
|
304
|
+
plugins: [react()],
|
|
305
|
+
base: '/base-path',
|
|
306
|
+
})
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
// main.ts
|
|
311
|
+
import { ViteReactSSG } from 'vite-react-ssg'
|
|
312
|
+
import { routes } from './App'
|
|
313
|
+
import './index.css'
|
|
314
|
+
|
|
315
|
+
export const createRoot = ViteReactSSG(
|
|
316
|
+
{
|
|
317
|
+
routes,
|
|
318
|
+
// pass your BASE_URL
|
|
319
|
+
basename: import.meta.env.BASE_URL,
|
|
320
|
+
},
|
|
321
|
+
)
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Vite React SSG will give it to the react-router's `basename`.
|
|
325
|
+
|
|
326
|
+
See: [react-router's create-browser-router](https://reactrouter.com/en/main/routers/create-browser-router#basename)
|
|
327
|
+
|
|
328
|
+
[Example](./examples/lazy-pages/vite.config.ts)
|
|
329
|
+
|
|
330
|
+
## CSS in JS
|
|
331
|
+
|
|
332
|
+
Use the `getStyleCollector` option to specify an SSR/SSG style collector. Currently only supports `styled-components`.
|
|
333
|
+
|
|
334
|
+
```tsx
|
|
335
|
+
import { ViteReactSSG } from 'vite-react-ssg'
|
|
336
|
+
import getStyledComponentsCollector from 'vite-react-ssg/style-collectors/styled-components'
|
|
337
|
+
import { routes } from './App.js'
|
|
338
|
+
import './index.css'
|
|
339
|
+
|
|
340
|
+
export const createRoot = ViteReactSSG(
|
|
341
|
+
{ routes },
|
|
342
|
+
() => { },
|
|
343
|
+
{ getStyleCollector: getStyledComponentsCollector }
|
|
344
|
+
)
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
You can provide your own by looking at the [implementation](./src/style-collectors/) of any of the existing collectors.
|
|
348
|
+
|
|
349
|
+
## Critical CSS
|
|
350
|
+
|
|
351
|
+
Vite React SSG has built-in support for generating [Critical CSS](https://web.dev/extract-critical-css/) inlined in the HTML via the [`critters`](https://github.com/GoogleChromeLabs/critters) package.
|
|
352
|
+
Install it with:
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
npm i -D critters
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Critical CSS generation will automatically be enabled for you.
|
|
359
|
+
|
|
360
|
+
To configure `critters`, pass [its options](https://github.com/GoogleChromeLabs/critters#usage) into `ssgOptions.crittersOptions` in `vite.config.ts`:
|
|
361
|
+
|
|
362
|
+
```ts
|
|
363
|
+
// vite.config.ts
|
|
364
|
+
export default defineConfig({
|
|
365
|
+
ssgOptions: {
|
|
366
|
+
crittersOptions: {
|
|
367
|
+
// E.g., change the preload strategy
|
|
368
|
+
preload: 'media',
|
|
369
|
+
// Other options: https://github.com/GoogleChromeLabs/critters#usage
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
})
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
## Configuration
|
|
376
|
+
|
|
377
|
+
You can pass options to Vite SSG in the `ssgOptions` field of your `vite.config.js`
|
|
378
|
+
|
|
379
|
+
```js
|
|
380
|
+
// vite.config.js
|
|
381
|
+
|
|
382
|
+
export default {
|
|
383
|
+
plugins: [],
|
|
384
|
+
ssgOptions: {
|
|
385
|
+
script: 'async',
|
|
386
|
+
},
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
```ts
|
|
391
|
+
interface ViteReactSSGOptions {
|
|
392
|
+
/**
|
|
393
|
+
* Set the scripts' loading mode. Only works for `type="module"`.
|
|
394
|
+
*
|
|
395
|
+
* @default 'sync'
|
|
396
|
+
*/
|
|
397
|
+
script?: 'sync' | 'async' | 'defer' | 'async defer'
|
|
398
|
+
/**
|
|
399
|
+
* Build format.
|
|
400
|
+
*
|
|
401
|
+
* @default 'esm'
|
|
402
|
+
*/
|
|
403
|
+
format?: 'esm' | 'cjs'
|
|
404
|
+
/**
|
|
405
|
+
* The path of the main entry file (relative to the project root).
|
|
406
|
+
*
|
|
407
|
+
* @default 'src/main.ts'
|
|
408
|
+
*/
|
|
409
|
+
entry?: string
|
|
410
|
+
/**
|
|
411
|
+
* Mock browser global variables (window, document, etc...) from SSG.
|
|
412
|
+
*
|
|
413
|
+
* @default false
|
|
414
|
+
*/
|
|
415
|
+
mock?: boolean
|
|
416
|
+
/**
|
|
417
|
+
* Apply formatter to the generated index file.
|
|
418
|
+
*
|
|
419
|
+
* **It will cause Hydration Failed.**
|
|
420
|
+
*
|
|
421
|
+
* @default 'none'
|
|
422
|
+
*/
|
|
423
|
+
formatting?: 'minify' | 'prettify' | 'none'
|
|
424
|
+
/**
|
|
425
|
+
* Vite environmeng mode.
|
|
426
|
+
*/
|
|
427
|
+
mode?: string
|
|
428
|
+
/**
|
|
429
|
+
* Directory style of the output directory.
|
|
430
|
+
*
|
|
431
|
+
* flat: `/foo` -> `/foo.html`
|
|
432
|
+
* nested: `/foo` -> `/foo/index.html`
|
|
433
|
+
*
|
|
434
|
+
* @default 'flat'
|
|
435
|
+
*/
|
|
436
|
+
dirStyle?: 'flat' | 'nested'
|
|
437
|
+
/**
|
|
438
|
+
* Generate for all routes, including dynamic routes.
|
|
439
|
+
* If enabled, you will need to configGure your serve
|
|
440
|
+
* manually to handle dynamic routes properly.
|
|
441
|
+
*
|
|
442
|
+
* @default false
|
|
443
|
+
*/
|
|
444
|
+
includeAllRoutes?: boolean
|
|
445
|
+
/**
|
|
446
|
+
* Options for the critters packages.
|
|
447
|
+
*
|
|
448
|
+
* @see https://github.com/GoogleChromeLabs/critters
|
|
449
|
+
*/
|
|
450
|
+
crittersOptions?: CrittersOptions | false
|
|
451
|
+
/**
|
|
452
|
+
* Custom function to modify the routes to do the SSG.
|
|
453
|
+
*
|
|
454
|
+
* Works only when `includeAllRoutes` is set to false.
|
|
455
|
+
*
|
|
456
|
+
* Defaults to a handler that filters out all the dynamic routes.
|
|
457
|
+
* When passing your custom handler, you should also take care of the dynamic routes yourself.
|
|
458
|
+
*/
|
|
459
|
+
includedRoutes?: (paths: string[], routes: Readonly<RouteRecord[]>) => Promise<string[]> | string[]
|
|
460
|
+
/**
|
|
461
|
+
* Callback to be called before every page render.
|
|
462
|
+
*
|
|
463
|
+
* It can be used to transform the project's `index.html` file before passing it to the renderer.
|
|
464
|
+
*
|
|
465
|
+
* To do so, you can change the 'index.html' file contents (passed in through the `indexHTML` parameter), and return it.
|
|
466
|
+
* The returned value will then be passed to renderer.
|
|
467
|
+
*/
|
|
468
|
+
onBeforePageRender?: (route: string, indexHTML: string, appCtx: ViteReactSSGContext<true>) => Promise<string | null | undefined> | string | null | undefined
|
|
469
|
+
/**
|
|
470
|
+
* Callback to be called on every rendered page.
|
|
471
|
+
*
|
|
472
|
+
* It can be used to transform the current route's rendered HTML.
|
|
473
|
+
*
|
|
474
|
+
* To do so, you can transform the route's rendered HTML (passed in through the `renderedHTML` parameter), and return it.
|
|
475
|
+
* The returned value will be used as the HTML of the route.
|
|
476
|
+
*/
|
|
477
|
+
onPageRendered?: (route: string, renderedHTML: string, appCtx: ViteReactSSGContext<true>) => Promise<string | null | undefined> | string | null | undefined
|
|
478
|
+
|
|
479
|
+
onFinished?: () => Promise<void> | void
|
|
480
|
+
/**
|
|
481
|
+
* The application's root container `id`.
|
|
482
|
+
*
|
|
483
|
+
* @default `root`
|
|
484
|
+
*/
|
|
485
|
+
rootContainerId?: string
|
|
486
|
+
/**
|
|
487
|
+
* The size of the SSG processing queue.
|
|
488
|
+
*
|
|
489
|
+
* @default 20
|
|
490
|
+
*/
|
|
491
|
+
concurrency?: number
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
See [src/types.ts](./src/types.ts). for more options available.
|
|
496
|
+
|
|
497
|
+
### Custom Routes to Render
|
|
498
|
+
|
|
499
|
+
You can use the `includedRoutes` hook to include or exclude route paths to render, or even provide some completely custom ones.
|
|
500
|
+
|
|
501
|
+
```js
|
|
502
|
+
// vite.config.js
|
|
503
|
+
|
|
504
|
+
export default {
|
|
505
|
+
plugins: [],
|
|
506
|
+
ssgOptions: {
|
|
507
|
+
includedRoutes(paths, routes) {
|
|
508
|
+
// exclude all the route paths that contains 'foo'
|
|
509
|
+
return paths.filter(i => !i.includes('foo'))
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
```js
|
|
516
|
+
// vite.config.js
|
|
517
|
+
|
|
518
|
+
export default {
|
|
519
|
+
plugins: [],
|
|
520
|
+
ssgOptions: {
|
|
521
|
+
includedRoutes(paths, routes) {
|
|
522
|
+
// use original route records
|
|
523
|
+
return routes.flatMap(route => {
|
|
524
|
+
return route.name === 'Blog'
|
|
525
|
+
? myBlogSlugs.map(slug => `/blog/${slug}`)
|
|
526
|
+
: route.path
|
|
527
|
+
})
|
|
528
|
+
},
|
|
529
|
+
},
|
|
530
|
+
}
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
```ts
|
|
534
|
+
export default defineConfig({
|
|
535
|
+
server: {
|
|
536
|
+
https: true,
|
|
537
|
+
},
|
|
538
|
+
})
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
## Roadmap
|
|
542
|
+
|
|
543
|
+
- [x] Preload assets
|
|
544
|
+
- [x] Document head
|
|
545
|
+
- [x] SSR in dev environment
|
|
546
|
+
- [x] More Client components, such as `<ClientOnly />`
|
|
547
|
+
- [x] `getStaticPaths` for dynamic routes
|
|
548
|
+
|
|
549
|
+
## Credits
|
|
550
|
+
|
|
551
|
+
This project inspired by [vite-ssg](https://github.com/antfu/vite-ssg), thanks to [@antfu](https://github.com/antfu) for his awesome work.
|
|
552
|
+
|
|
553
|
+
## License
|
|
554
|
+
|
|
555
|
+
[MIT](./LICENSE) License © 2023 [Riri](https://github.com/Daydreamer-riri)
|