vite-react-ssg 0.0.2 → 0.0.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 CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2023 Riri
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.
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Riri
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 CHANGED
@@ -1,172 +1,239 @@
1
- # Vite React SSG
2
-
3
- Static-site generation for React on Vite.
4
-
5
- [![NPM version](https://img.shields.io/npm/v/vite-react-ssg?color=a1b858&label=)](https://www.npmjs.com/package/vite-react-ssg)
6
-
7
- ## Install
8
-
9
- > **This library requires Node.js version >= 17**
10
- > or `Request` is available
11
- <pre>
12
- <b>npm i -D vite-react-ssg</b> <em>react-router-dom</em>
13
- </pre>
14
-
15
- ```diff
16
- // package.json
17
- {
18
- "scripts": {
19
- "dev": "vite",
20
- - "build": "vite build"
21
- + "build": "vite-react-ssg build"
22
-
23
- // OR if you want to use another vite config file
24
- + "build": "vite-react-ssg build -c another-vite.config.ts"
25
- }
26
- }
27
- ```
28
-
29
- ```ts
30
- // src/main.ts
31
- import { ViteReactSSG } from 'vite-react-ssg'
32
- import routes from './App.tsx'
33
-
34
- export const createRoot = ViteReactSSG(
35
- // react-router-dom data routes
36
- { routes },
37
- // function to have custom setups
38
- ({ router, routes, isClient, initialState }) => {
39
- // do something.
40
- },
41
- )
42
- ```
43
-
44
- ```tsx
45
- // src/App.tsx
46
- import React from 'react'
47
- import type { RouteRecord } from 'vite-react-ssg'
48
- import './App.css'
49
-
50
- const pages = import.meta.glob<any>('./pages/**/*.tsx')
51
-
52
- const children: RouteRecord[] = Object.entries(pages).map(([filepath, component]) => {
53
- let path = filepath.split('/pages')[1]
54
- path = path.split('.')[0].replace('index', '')
55
- const entry = `src${filepath.slice(1)}`
56
-
57
- if (path.endsWith('/')) {
58
- return {
59
- index: true,
60
- Component: React.lazy(component),
61
- entry,
62
- }
63
- }
64
- return {
65
- path,
66
- Component: React.lazy(component),
67
- // Used to obtain static resources through manifest
68
- entry,
69
- }
70
- })
71
-
72
- const Layout = React.lazy(() => import('./Layout'))
73
- export const routes: RouteRecord[] = [
74
- {
75
- path: '/',
76
- element: <Layout />,
77
- children,
78
- // Used to obtain static resources through manifest
79
- entry: 'src/Layout.tsx',
80
- },
81
- ]
82
- ```
83
-
84
- ## Critical CSS
85
-
86
- Vite 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.
87
- Install it with:
88
-
89
- ```bash
90
- npm i -D critters
91
- ```
92
-
93
- Critical CSS generation will automatically be enabled for you.
94
-
95
- To configure `critters`, pass [its options](https://github.com/GoogleChromeLabs/critters#usage) into `ssgOptions.crittersOptions` in `vite.config.ts`:
96
-
97
- ```ts
98
- // vite.config.ts
99
- export default defineConfig({
100
- ssgOptions: {
101
- crittersOptions: {
102
- // E.g., change the preload strategy
103
- preload: 'media',
104
- // Other options: https://github.com/GoogleChromeLabs/critters#usage
105
- },
106
- },
107
- })
108
- ```
109
-
110
- ## Configuration
111
-
112
- You can pass options to Vite SSG in the `ssgOptions` field of your `vite.config.js`
113
-
114
- ```js
115
- // vite.config.js
116
-
117
- export default {
118
- plugins: [],
119
- ssgOptions: {
120
- script: 'async',
121
- },
122
- }
123
- ```
124
-
125
- See [src/types.ts](./src/types.ts). for more options available.
126
-
127
- ### Custom Routes to Render
128
-
129
- You can use the `includedRoutes` hook to include or exclude route paths to render, or even provide some completely custom ones.
130
-
131
- ```js
132
- // vite.config.js
133
-
134
- export default {
135
- plugins: [],
136
- ssgOptions: {
137
- includedRoutes(paths, routes) {
138
- // exclude all the route paths that contains 'foo'
139
- return paths.filter(i => !i.includes('foo'))
140
- },
141
- },
142
- }
143
- ```
144
- ```js
145
- // vite.config.js
146
-
147
- export default {
148
- plugins: [],
149
- ssgOptions: {
150
- includedRoutes(paths, routes) {
151
- // use original route records
152
- return routes.flatMap((route) => {
153
- return route.name === 'Blog'
154
- ? myBlogSlugs.map(slug => `/blog/${slug}`)
155
- : route.path
156
- })
157
- },
158
- },
159
- }
160
- ```
161
-
162
- ## Roadmap
163
-
164
- - [x] Preload assets
165
- - [ ] SSR under dev
166
- - [ ] Initial State
167
- - [ ] Document head
168
- - [ ] More Client components, such as `<ClientOnly />`
169
-
170
- ## License
171
-
1
+ # Vite React SSG
2
+
3
+ Static-site generation for React on Vite.
4
+
5
+ [![NPM version](https://img.shields.io/npm/v/vite-react-ssg?color=a1b858&label=)](https://www.npmjs.com/package/vite-react-ssg)
6
+
7
+ ## Install
8
+
9
+ > **This library requires Node.js version >= 17**
10
+ > or `Request` is available
11
+ <pre>
12
+ <b>npm i -D vite-react-ssg</b> <em>react-router-dom</em>
13
+ </pre>
14
+
15
+ ```diff
16
+ // package.json
17
+ {
18
+ "scripts": {
19
+ "dev": "vite",
20
+ - "build": "vite build"
21
+ + "build": "vite-react-ssg build"
22
+
23
+ // OR if you want to use another vite config file
24
+ + "build": "vite-react-ssg build -c another-vite.config.ts"
25
+ }
26
+ }
27
+ ```
28
+
29
+ ```ts
30
+ // src/main.ts
31
+ import { ViteReactSSG } from 'vite-react-ssg'
32
+ import routes from './App.tsx'
33
+
34
+ export const createRoot = ViteReactSSG(
35
+ // react-router-dom data routes
36
+ { routes },
37
+ // function to have custom setups
38
+ ({ router, routes, isClient, initialState }) => {
39
+ // do something.
40
+ },
41
+ )
42
+ ```
43
+
44
+ ```tsx
45
+ // src/App.tsx
46
+ import React from 'react'
47
+ import type { RouteRecord } from 'vite-react-ssg'
48
+ import './App.css'
49
+
50
+ const pages = import.meta.glob<any>('./pages/**/*.tsx')
51
+
52
+ const children: RouteRecord[] = Object.entries(pages).map(([filepath, component]) => {
53
+ let path = filepath.split('/pages')[1]
54
+ path = path.split('.')[0].replace('index', '')
55
+ const entry = `src${filepath.slice(1)}`
56
+
57
+ if (path.endsWith('/')) {
58
+ return {
59
+ index: true,
60
+ Component: React.lazy(component),
61
+ entry,
62
+ }
63
+ }
64
+ return {
65
+ path,
66
+ Component: React.lazy(component),
67
+ // Used to obtain static resources through manifest
68
+ entry,
69
+ }
70
+ })
71
+
72
+ const Layout = React.lazy(() => import('./Layout'))
73
+ export const routes: RouteRecord[] = [
74
+ {
75
+ path: '/',
76
+ element: <Layout />,
77
+ children,
78
+ // Used to obtain static resources through manifest
79
+ entry: 'src/Layout.tsx',
80
+ },
81
+ ]
82
+ ```
83
+
84
+ ## Document head
85
+
86
+ 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).
87
+
88
+ ```tsx
89
+ import { Head } from 'vite-react-ssg'
90
+
91
+ const MyHead = () => (
92
+ <Head>
93
+ <meta property="og:description" content="My custom description" />
94
+ <meta charSet="utf-8" />
95
+ <title>My Title</title>
96
+ <link rel="canonical" href="http://mysite.com/example" />
97
+ </Head>
98
+ )
99
+ ```
100
+
101
+ Nested or latter components will override duplicate usages:
102
+
103
+ ```tsx
104
+ import { Head } from 'vite-react-ssg'
105
+
106
+ const MyHead = () => (
107
+ <parent>
108
+ <Head>
109
+ <title>My Title</title>
110
+ <meta name="description" content="Helmet application" />
111
+ </Head>
112
+ <child>
113
+ <Head>
114
+ <title>Nested Title</title>
115
+ <meta name="description" content="Nested component" />
116
+ </Head>
117
+ </child>
118
+ </parent>
119
+ )
120
+ ```
121
+
122
+ Outputs:
123
+ ```html
124
+ <head>
125
+ <title>Nested Title</title>
126
+ <meta name="description" content="Nested component" />
127
+ </head>
128
+ ```
129
+
130
+ ### Reactive head
131
+
132
+ ```tsx
133
+ import { useState } from 'react'
134
+ import { Head } from 'vite-react-ssg'
135
+
136
+ export default function MyHead() {
137
+ const [state, setState] = useState(false)
138
+
139
+ return (
140
+ <Head>
141
+ <meta charSet="UTF-8" />
142
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
143
+ <title>head test {state ? 'A' : 'B'}</title>
144
+ {/* You can also set the 'body' attributes here */}
145
+ <body className={`body-class-in-head-${state ? 'a' : 'b'}`} />
146
+ </Head>
147
+ )
148
+ }
149
+ ```
150
+
151
+ ## Critical CSS
152
+
153
+ Vite 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.
154
+ Install it with:
155
+
156
+ ```bash
157
+ npm i -D critters
158
+ ```
159
+
160
+ Critical CSS generation will automatically be enabled for you.
161
+
162
+ To configure `critters`, pass [its options](https://github.com/GoogleChromeLabs/critters#usage) into `ssgOptions.crittersOptions` in `vite.config.ts`:
163
+
164
+ ```ts
165
+ // vite.config.ts
166
+ export default defineConfig({
167
+ ssgOptions: {
168
+ crittersOptions: {
169
+ // E.g., change the preload strategy
170
+ preload: 'media',
171
+ // Other options: https://github.com/GoogleChromeLabs/critters#usage
172
+ },
173
+ },
174
+ })
175
+ ```
176
+
177
+ ## Configuration
178
+
179
+ You can pass options to Vite SSG in the `ssgOptions` field of your `vite.config.js`
180
+
181
+ ```js
182
+ // vite.config.js
183
+
184
+ export default {
185
+ plugins: [],
186
+ ssgOptions: {
187
+ script: 'async',
188
+ },
189
+ }
190
+ ```
191
+
192
+ See [src/types.ts](./src/types.ts). for more options available.
193
+
194
+ ### Custom Routes to Render
195
+
196
+ You can use the `includedRoutes` hook to include or exclude route paths to render, or even provide some completely custom ones.
197
+
198
+ ```js
199
+ // vite.config.js
200
+
201
+ export default {
202
+ plugins: [],
203
+ ssgOptions: {
204
+ includedRoutes(paths, routes) {
205
+ // exclude all the route paths that contains 'foo'
206
+ return paths.filter(i => !i.includes('foo'))
207
+ },
208
+ },
209
+ }
210
+ ```
211
+ ```js
212
+ // vite.config.js
213
+
214
+ export default {
215
+ plugins: [],
216
+ ssgOptions: {
217
+ includedRoutes(paths, routes) {
218
+ // use original route records
219
+ return routes.flatMap((route) => {
220
+ return route.name === 'Blog'
221
+ ? myBlogSlugs.map(slug => `/blog/${slug}`)
222
+ : route.path
223
+ })
224
+ },
225
+ },
226
+ }
227
+ ```
228
+
229
+ ## Roadmap
230
+
231
+ - [x] Preload assets
232
+ - [x] Document head
233
+ - [ ] SSR under dev
234
+ - [ ] Initial State
235
+ - [ ] More Client components, such as `<ClientOnly />`
236
+
237
+ ## License
238
+
172
239
  [MIT](./LICENSE) License © 2023 [Riri](https://github.com/Daydreamer-riri)
@@ -1,3 +1,3 @@
1
- #!/usr/bin/env node
2
- 'use strict'
3
- import('../dist/node/cli.mjs')
1
+ #!/usr/bin/env node
2
+ 'use strict'
3
+ import('../dist/node/cli.mjs')
@@ -6,85 +6,85 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
6
6
 
7
7
  const JSDOM__default = /*#__PURE__*/_interopDefaultCompat(JSDOM);
8
8
 
9
- /*
10
- MIT License
11
-
12
- Copyright for portions of global-jsdom are held by Rico Sta. Cruz, 2016 as part of
13
- jsdom-global. All other copyright for global-jsdom are held by jonathan schatz, 2017.
14
-
15
- Permission is hereby granted, free of charge, to any person obtaining a copy
16
- of this software and associated documentation files (the "Software"), to deal
17
- in the Software without restriction, including without limitation the rights
18
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
- copies of the Software, and to permit persons to whom the Software is
20
- furnished to do so, subject to the following conditions:
21
-
22
- The above copyright notice and this permission notice shall be included in all
23
- copies or substantial portions of the Software.
24
-
25
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31
- SOFTWARE.
32
- */
33
-
34
-
35
- const defaultHtml = '<!doctype html><html><head><meta charset="utf-8"></head><body></body></html>';
36
-
37
- // define this here so that we only ever dynamically populate KEYS once.
38
-
39
- const KEYS = [];
40
-
41
- function jsdomGlobal(html = defaultHtml, options = {}) {
42
- // Idempotency
43
- if (global.navigator
44
- && global.navigator.userAgent
45
- && global.navigator.userAgent.includes('Node.js')
46
- && global.document
47
- && typeof global.document.destroy === 'function')
48
- return global.document.destroy
49
-
50
- // set a default url if we don't get one - otherwise things explode when we copy localstorage keys
51
- if (!('url' in options))
52
- Object.assign(options, { url: 'http://localhost:3000' });
53
-
54
- // enable pretendToBeVisual by default since react needs
55
- // window.requestAnimationFrame, see https://github.com/jsdom/jsdom#pretending-to-be-a-visual-browser
56
- if (!('pretendToBeVisual' in options))
57
- Object.assign(options, { pretendToBeVisual: true });
58
-
59
- const jsdom = new JSDOM__default.JSDOM(html, options);
60
- const { window } = jsdom;
61
- const { document } = window;
62
-
63
- // generate our list of keys by enumerating document.window - this list may vary
64
- // based on the jsdom version. filter out internal methods as well as anything
65
- // that node already defines
66
-
67
- if (KEYS.length === 0) {
68
- KEYS.push(...Object.getOwnPropertyNames(window).filter(k => !k.startsWith('_')).filter(k => !(k in global)));
69
- // going to add our jsdom instance, see below
70
- KEYS.push('$jsdom');
71
- }
72
-
73
- KEYS.forEach(key => global[key] = window[key]);
74
-
75
- // setup document / window / window.console
76
- global.document = document;
77
- global.window = window;
78
- window.console = global.console;
79
-
80
- // add access to our jsdom instance
81
- global.$jsdom = jsdom;
82
-
83
- const cleanup = () => KEYS.forEach(key => delete global[key]);
84
-
85
- document.destroy = cleanup;
86
-
87
- return cleanup
9
+ /*
10
+ MIT License
11
+
12
+ Copyright for portions of global-jsdom are held by Rico Sta. Cruz, 2016 as part of
13
+ jsdom-global. All other copyright for global-jsdom are held by jonathan schatz, 2017.
14
+
15
+ Permission is hereby granted, free of charge, to any person obtaining a copy
16
+ of this software and associated documentation files (the "Software"), to deal
17
+ in the Software without restriction, including without limitation the rights
18
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
+ copies of the Software, and to permit persons to whom the Software is
20
+ furnished to do so, subject to the following conditions:
21
+
22
+ The above copyright notice and this permission notice shall be included in all
23
+ copies or substantial portions of the Software.
24
+
25
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31
+ SOFTWARE.
32
+ */
33
+
34
+
35
+ const defaultHtml = '<!doctype html><html><head><meta charset="utf-8"></head><body></body></html>';
36
+
37
+ // define this here so that we only ever dynamically populate KEYS once.
38
+
39
+ const KEYS = [];
40
+
41
+ function jsdomGlobal(html = defaultHtml, options = {}) {
42
+ // Idempotency
43
+ if (global.navigator
44
+ && global.navigator.userAgent
45
+ && global.navigator.userAgent.includes('Node.js')
46
+ && global.document
47
+ && typeof global.document.destroy === 'function')
48
+ return global.document.destroy
49
+
50
+ // set a default url if we don't get one - otherwise things explode when we copy localstorage keys
51
+ if (!('url' in options))
52
+ Object.assign(options, { url: 'http://localhost:3000' });
53
+
54
+ // enable pretendToBeVisual by default since react needs
55
+ // window.requestAnimationFrame, see https://github.com/jsdom/jsdom#pretending-to-be-a-visual-browser
56
+ if (!('pretendToBeVisual' in options))
57
+ Object.assign(options, { pretendToBeVisual: true });
58
+
59
+ const jsdom = new JSDOM__default.JSDOM(html, options);
60
+ const { window } = jsdom;
61
+ const { document } = window;
62
+
63
+ // generate our list of keys by enumerating document.window - this list may vary
64
+ // based on the jsdom version. filter out internal methods as well as anything
65
+ // that node already defines
66
+
67
+ if (KEYS.length === 0) {
68
+ KEYS.push(...Object.getOwnPropertyNames(window).filter(k => !k.startsWith('_')).filter(k => !(k in global)));
69
+ // going to add our jsdom instance, see below
70
+ KEYS.push('$jsdom');
71
+ }
72
+
73
+ KEYS.forEach(key => global[key] = window[key]);
74
+
75
+ // setup document / window / window.console
76
+ global.document = document;
77
+ global.window = window;
78
+ window.console = global.console;
79
+
80
+ // add access to our jsdom instance
81
+ global.$jsdom = jsdom;
82
+
83
+ const cleanup = () => KEYS.forEach(key => delete global[key]);
84
+
85
+ document.destroy = cleanup;
86
+
87
+ return cleanup
88
88
  }
89
89
 
90
90
  exports.jsdomGlobal = jsdomGlobal;
@@ -1,84 +1,84 @@
1
1
  import JSDOM from 'jsdom';
2
2
 
3
- /*
4
- MIT License
5
-
6
- Copyright for portions of global-jsdom are held by Rico Sta. Cruz, 2016 as part of
7
- jsdom-global. All other copyright for global-jsdom are held by jonathan schatz, 2017.
8
-
9
- Permission is hereby granted, free of charge, to any person obtaining a copy
10
- of this software and associated documentation files (the "Software"), to deal
11
- in the Software without restriction, including without limitation the rights
12
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
- copies of the Software, and to permit persons to whom the Software is
14
- furnished to do so, subject to the following conditions:
15
-
16
- The above copyright notice and this permission notice shall be included in all
17
- copies or substantial portions of the Software.
18
-
19
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
- SOFTWARE.
26
- */
27
-
28
-
29
- const defaultHtml = '<!doctype html><html><head><meta charset="utf-8"></head><body></body></html>';
30
-
31
- // define this here so that we only ever dynamically populate KEYS once.
32
-
33
- const KEYS = [];
34
-
35
- function jsdomGlobal(html = defaultHtml, options = {}) {
36
- // Idempotency
37
- if (global.navigator
38
- && global.navigator.userAgent
39
- && global.navigator.userAgent.includes('Node.js')
40
- && global.document
41
- && typeof global.document.destroy === 'function')
42
- return global.document.destroy
43
-
44
- // set a default url if we don't get one - otherwise things explode when we copy localstorage keys
45
- if (!('url' in options))
46
- Object.assign(options, { url: 'http://localhost:3000' });
47
-
48
- // enable pretendToBeVisual by default since react needs
49
- // window.requestAnimationFrame, see https://github.com/jsdom/jsdom#pretending-to-be-a-visual-browser
50
- if (!('pretendToBeVisual' in options))
51
- Object.assign(options, { pretendToBeVisual: true });
52
-
53
- const jsdom = new JSDOM.JSDOM(html, options);
54
- const { window } = jsdom;
55
- const { document } = window;
56
-
57
- // generate our list of keys by enumerating document.window - this list may vary
58
- // based on the jsdom version. filter out internal methods as well as anything
59
- // that node already defines
60
-
61
- if (KEYS.length === 0) {
62
- KEYS.push(...Object.getOwnPropertyNames(window).filter(k => !k.startsWith('_')).filter(k => !(k in global)));
63
- // going to add our jsdom instance, see below
64
- KEYS.push('$jsdom');
65
- }
66
-
67
- KEYS.forEach(key => global[key] = window[key]);
68
-
69
- // setup document / window / window.console
70
- global.document = document;
71
- global.window = window;
72
- window.console = global.console;
73
-
74
- // add access to our jsdom instance
75
- global.$jsdom = jsdom;
76
-
77
- const cleanup = () => KEYS.forEach(key => delete global[key]);
78
-
79
- document.destroy = cleanup;
80
-
81
- return cleanup
3
+ /*
4
+ MIT License
5
+
6
+ Copyright for portions of global-jsdom are held by Rico Sta. Cruz, 2016 as part of
7
+ jsdom-global. All other copyright for global-jsdom are held by jonathan schatz, 2017.
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+ */
27
+
28
+
29
+ const defaultHtml = '<!doctype html><html><head><meta charset="utf-8"></head><body></body></html>';
30
+
31
+ // define this here so that we only ever dynamically populate KEYS once.
32
+
33
+ const KEYS = [];
34
+
35
+ function jsdomGlobal(html = defaultHtml, options = {}) {
36
+ // Idempotency
37
+ if (global.navigator
38
+ && global.navigator.userAgent
39
+ && global.navigator.userAgent.includes('Node.js')
40
+ && global.document
41
+ && typeof global.document.destroy === 'function')
42
+ return global.document.destroy
43
+
44
+ // set a default url if we don't get one - otherwise things explode when we copy localstorage keys
45
+ if (!('url' in options))
46
+ Object.assign(options, { url: 'http://localhost:3000' });
47
+
48
+ // enable pretendToBeVisual by default since react needs
49
+ // window.requestAnimationFrame, see https://github.com/jsdom/jsdom#pretending-to-be-a-visual-browser
50
+ if (!('pretendToBeVisual' in options))
51
+ Object.assign(options, { pretendToBeVisual: true });
52
+
53
+ const jsdom = new JSDOM.JSDOM(html, options);
54
+ const { window } = jsdom;
55
+ const { document } = window;
56
+
57
+ // generate our list of keys by enumerating document.window - this list may vary
58
+ // based on the jsdom version. filter out internal methods as well as anything
59
+ // that node already defines
60
+
61
+ if (KEYS.length === 0) {
62
+ KEYS.push(...Object.getOwnPropertyNames(window).filter(k => !k.startsWith('_')).filter(k => !(k in global)));
63
+ // going to add our jsdom instance, see below
64
+ KEYS.push('$jsdom');
65
+ }
66
+
67
+ KEYS.forEach(key => global[key] = window[key]);
68
+
69
+ // setup document / window / window.console
70
+ global.document = document;
71
+ global.window = window;
72
+ window.console = global.console;
73
+
74
+ // add access to our jsdom instance
75
+ global.$jsdom = jsdom;
76
+
77
+ const cleanup = () => KEYS.forEach(key => delete global[key]);
78
+
79
+ document.destroy = cleanup;
80
+
81
+ return cleanup
82
82
  }
83
83
 
84
84
  export { jsdomGlobal };
package/dist/node/cli.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
  const kolorist = require('kolorist');
4
4
  const yargs = require('yargs');
5
5
  const helpers = require('yargs/helpers');
6
- const build = require('../shared/vite-react-ssg.0e1b881e.cjs');
6
+ const build = require('../shared/vite-react-ssg.a5dfecac.cjs');
7
7
  require('node:path');
8
8
  require('node:module');
9
9
  require('fs-extra');
package/dist/node/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { gray, bold, red, reset, underline } from 'kolorist';
2
2
  import yargs from 'yargs';
3
3
  import { hideBin } from 'yargs/helpers';
4
- import { b as build } from '../shared/vite-react-ssg.f66e4ba5.mjs';
4
+ import { b as build } from '../shared/vite-react-ssg.524ba00d.mjs';
5
5
  import 'node:path';
6
6
  import 'node:module';
7
7
  import 'fs-extra';
package/dist/node.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const build = require('./shared/vite-react-ssg.0e1b881e.cjs');
3
+ const build = require('./shared/vite-react-ssg.a5dfecac.cjs');
4
4
  require('node:path');
5
5
  require('node:module');
6
6
  require('kolorist');
package/dist/node.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { b as build } from './shared/vite-react-ssg.f66e4ba5.mjs';
1
+ export { b as build } from './shared/vite-react-ssg.524ba00d.mjs';
2
2
  import 'node:path';
3
3
  import 'node:module';
4
4
  import 'kolorist';
@@ -868,9 +868,10 @@ function routesToPaths(routes) {
868
868
  pathToEntry[path] = /* @__PURE__ */ new Set([entry]);
869
869
  }
870
870
  if (!routes)
871
- return { paths: ["/"] };
871
+ return { paths: ["/"], pathToEntry };
872
872
  const paths = /* @__PURE__ */ new Set();
873
873
  const getPaths = (routes2, prefix = "") => {
874
+ const parentPath = prefix;
874
875
  prefix = prefix.replace(/\/$/g, "");
875
876
  for (const route of routes2) {
876
877
  let path = route.path;
@@ -878,6 +879,10 @@ function routesToPaths(routes) {
878
879
  path = prefix && !route.path.startsWith("/") ? `${prefix}${route.path ? `/${route.path}` : ""}` : route.path;
879
880
  paths.add(path);
880
881
  addEntry(path, route.entry);
882
+ if (pathToEntry[parentPath]) {
883
+ const pathCopy = path;
884
+ pathToEntry[parentPath].forEach((entry) => addEntry(pathCopy, entry));
885
+ }
881
886
  }
882
887
  if (route.index)
883
888
  addEntry(prefix, route.entry);
@@ -958,17 +963,18 @@ async function render(routes, request) {
958
963
  throw context;
959
964
  const router = createStaticRouter(dataRoutes, context);
960
965
  const app = /* @__PURE__ */ React.createElement(HelmetProvider, { context: helmetContext }, /* @__PURE__ */ React.createElement(SiteMetadataDefaults, null), /* @__PURE__ */ React.createElement(StaticRouterProvider, { router, context }));
961
- const appHtml = await renderStaticApp(app);
966
+ const appHTML = await renderStaticApp(app);
962
967
  const { helmet } = helmetContext;
963
- helmet.htmlAttributes.toString();
964
- helmet.bodyAttributes.toString();
965
- [
968
+ const htmlAttributes = helmet.htmlAttributes.toString();
969
+ const bodyAttributes = helmet.bodyAttributes.toString();
970
+ const metaStrings = [
966
971
  helmet.title.toString(),
967
972
  helmet.meta.toString(),
968
973
  helmet.link.toString(),
969
974
  helmet.script.toString()
970
975
  ];
971
- return appHtml;
976
+ const metaAttributes = metaStrings.filter(Boolean);
977
+ return { appHTML, htmlAttributes, bodyAttributes, metaAttributes };
972
978
  }
973
979
 
974
980
  function renderPreloadLinks(document, modules, ssrManifest) {
@@ -1118,12 +1124,15 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1118
1124
  url.hash = "";
1119
1125
  url.pathname = route;
1120
1126
  const request = new Request(url.href);
1121
- const appHTML = await render([...routes2], request);
1127
+ const { appHTML, bodyAttributes, htmlAttributes, metaAttributes } = await render([...routes2], request);
1122
1128
  await triggerOnSSRAppRendered?.(route, appHTML, appCtx);
1123
1129
  const renderedHTML = await renderHTML({
1124
1130
  rootContainerId,
1125
1131
  appHTML,
1126
1132
  indexHTML,
1133
+ metaAttributes,
1134
+ bodyAttributes,
1135
+ htmlAttributes,
1127
1136
  initialState: null
1128
1137
  });
1129
1138
  const jsdom = new JSDOM(renderedHTML);
@@ -1189,10 +1198,20 @@ async function renderHTML({
1189
1198
  rootContainerId,
1190
1199
  indexHTML,
1191
1200
  appHTML,
1201
+ metaAttributes,
1202
+ bodyAttributes,
1203
+ htmlAttributes,
1192
1204
  initialState
1193
1205
  }) {
1194
1206
  const stateScript = initialState ? `
1195
1207
  <script>window.__INITIAL_STATE__=${initialState}<\/script>` : "";
1208
+ const headStartTag = "<head>";
1209
+ const metaTags = metaAttributes.join("");
1210
+ indexHTML = indexHTML.replace(headStartTag, headStartTag + metaTags);
1211
+ const bodyStartTag = "<body";
1212
+ indexHTML = indexHTML.replace(bodyStartTag, `${bodyStartTag} ${bodyAttributes}`);
1213
+ const htmlStartTag = "<html";
1214
+ indexHTML = indexHTML.replace(htmlStartTag, `${htmlStartTag} ${htmlAttributes}`);
1196
1215
  const container = `<div id="${rootContainerId}"></div>`;
1197
1216
  if (indexHTML.includes(container)) {
1198
1217
  return indexHTML.replace(
@@ -875,9 +875,10 @@ function routesToPaths(routes) {
875
875
  pathToEntry[path] = /* @__PURE__ */ new Set([entry]);
876
876
  }
877
877
  if (!routes)
878
- return { paths: ["/"] };
878
+ return { paths: ["/"], pathToEntry };
879
879
  const paths = /* @__PURE__ */ new Set();
880
880
  const getPaths = (routes2, prefix = "") => {
881
+ const parentPath = prefix;
881
882
  prefix = prefix.replace(/\/$/g, "");
882
883
  for (const route of routes2) {
883
884
  let path = route.path;
@@ -885,6 +886,10 @@ function routesToPaths(routes) {
885
886
  path = prefix && !route.path.startsWith("/") ? `${prefix}${route.path ? `/${route.path}` : ""}` : route.path;
886
887
  paths.add(path);
887
888
  addEntry(path, route.entry);
889
+ if (pathToEntry[parentPath]) {
890
+ const pathCopy = path;
891
+ pathToEntry[parentPath].forEach((entry) => addEntry(pathCopy, entry));
892
+ }
888
893
  }
889
894
  if (route.index)
890
895
  addEntry(prefix, route.entry);
@@ -965,17 +970,18 @@ async function render(routes, request) {
965
970
  throw context;
966
971
  const router = server_js.createStaticRouter(dataRoutes, context);
967
972
  const app = /* @__PURE__ */ React__default.createElement(reactHelmetAsync.HelmetProvider, { context: helmetContext }, /* @__PURE__ */ React__default.createElement(SiteMetadataDefaults.SiteMetadataDefaults, null), /* @__PURE__ */ React__default.createElement(server_js.StaticRouterProvider, { router, context }));
968
- const appHtml = await renderStaticApp(app);
973
+ const appHTML = await renderStaticApp(app);
969
974
  const { helmet } = helmetContext;
970
- helmet.htmlAttributes.toString();
971
- helmet.bodyAttributes.toString();
972
- [
975
+ const htmlAttributes = helmet.htmlAttributes.toString();
976
+ const bodyAttributes = helmet.bodyAttributes.toString();
977
+ const metaStrings = [
973
978
  helmet.title.toString(),
974
979
  helmet.meta.toString(),
975
980
  helmet.link.toString(),
976
981
  helmet.script.toString()
977
982
  ];
978
- return appHtml;
983
+ const metaAttributes = metaStrings.filter(Boolean);
984
+ return { appHTML, htmlAttributes, bodyAttributes, metaAttributes };
979
985
  }
980
986
 
981
987
  function renderPreloadLinks(document, modules, ssrManifest) {
@@ -1098,7 +1104,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1098
1104
  const prefix = format === "esm" && process.platform === "win32" ? "file://" : "";
1099
1105
  const ext = format === "esm" ? ".mjs" : ".cjs";
1100
1106
  const serverEntry = node_path.join(prefix, ssgOut, node_path.parse(ssrEntry).name + ext);
1101
- const _require = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (document.currentScript && document.currentScript.src || new URL('shared/vite-react-ssg.0e1b881e.cjs', document.baseURI).href)));
1107
+ const _require = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (document.currentScript && document.currentScript.src || new URL('shared/vite-react-ssg.a5dfecac.cjs', document.baseURI).href)));
1102
1108
  const { createRoot, includedRoutes: serverEntryIncludedRoutes } = format === "esm" ? await import(serverEntry) : _require(serverEntry);
1103
1109
  const includedRoutes = serverEntryIncludedRoutes || configIncludedRoutes;
1104
1110
  const { routes } = await createRoot(false);
@@ -1125,12 +1131,15 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1125
1131
  url.hash = "";
1126
1132
  url.pathname = route;
1127
1133
  const request = new Request(url.href);
1128
- const appHTML = await render([...routes2], request);
1134
+ const { appHTML, bodyAttributes, htmlAttributes, metaAttributes } = await render([...routes2], request);
1129
1135
  await triggerOnSSRAppRendered?.(route, appHTML, appCtx);
1130
1136
  const renderedHTML = await renderHTML({
1131
1137
  rootContainerId,
1132
1138
  appHTML,
1133
1139
  indexHTML,
1140
+ metaAttributes,
1141
+ bodyAttributes,
1142
+ htmlAttributes,
1134
1143
  initialState: null
1135
1144
  });
1136
1145
  const jsdom = new JSDOM.JSDOM(renderedHTML);
@@ -1196,10 +1205,20 @@ async function renderHTML({
1196
1205
  rootContainerId,
1197
1206
  indexHTML,
1198
1207
  appHTML,
1208
+ metaAttributes,
1209
+ bodyAttributes,
1210
+ htmlAttributes,
1199
1211
  initialState
1200
1212
  }) {
1201
1213
  const stateScript = initialState ? `
1202
1214
  <script>window.__INITIAL_STATE__=${initialState}<\/script>` : "";
1215
+ const headStartTag = "<head>";
1216
+ const metaTags = metaAttributes.join("");
1217
+ indexHTML = indexHTML.replace(headStartTag, headStartTag + metaTags);
1218
+ const bodyStartTag = "<body";
1219
+ indexHTML = indexHTML.replace(bodyStartTag, `${bodyStartTag} ${bodyAttributes}`);
1220
+ const htmlStartTag = "<html";
1221
+ indexHTML = indexHTML.replace(htmlStartTag, `${htmlStartTag} ${htmlAttributes}`);
1203
1222
  const container = `<div id="${rootContainerId}"></div>`;
1204
1223
  if (indexHTML.includes(container)) {
1205
1224
  return indexHTML.replace(
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vite-react-ssg",
3
3
  "type": "module",
4
- "version": "0.0.2",
4
+ "version": "0.0.3",
5
5
  "packageManager": "pnpm@8.6.6",
6
6
  "description": "",
7
7
  "author": "Riri <Daydreamerriri@outlook.com>",
@@ -110,4 +110,4 @@
110
110
  "vite-plugin-pwa": "^0.16.4",
111
111
  "vitest": "0.33.0"
112
112
  }
113
- }
113
+ }