vite-react-ssg 0.1.0 → 0.1.2
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 +16 -1
- package/dist/index.cjs +67 -3
- package/dist/index.d.ts +8 -5
- package/dist/index.mjs +68 -6
- package/dist/node/cli.cjs +1 -1
- package/dist/node/cli.mjs +1 -1
- package/dist/node.cjs +1 -1
- package/dist/node.d.ts +2 -1
- package/dist/node.mjs +1 -1
- package/dist/shared/{vite-react-ssg.823b31c9.cjs → vite-react-ssg.b0fd1823.cjs} +17 -9
- package/dist/shared/{vite-react-ssg.7ee042ba.mjs → vite-react-ssg.b83f6ed3.mjs} +15 -7
- package/dist/style-collectors.cjs +20 -0
- package/dist/style-collectors.d.ts +10 -0
- package/dist/style-collectors.mjs +18 -0
- package/dist/{types-25b96ed0.d.ts → types-408e974a.d.ts} +9 -1
- package/package.json +14 -1
package/README.md
CHANGED
|
@@ -100,7 +100,7 @@ function MyComponent() {
|
|
|
100
100
|
}
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
> It's important that the children of
|
|
103
|
+
> It's important that the children of `<ClientOnly>` is not a JSX element, but a function that returns an element.
|
|
104
104
|
> Because React will try to render children, and may use the client's API on the server.
|
|
105
105
|
|
|
106
106
|
## Document head
|
|
@@ -170,6 +170,21 @@ export default function MyHead() {
|
|
|
170
170
|
}
|
|
171
171
|
```
|
|
172
172
|
|
|
173
|
+
## CSS in JS
|
|
174
|
+
|
|
175
|
+
Use the `getStyleCollector` option to specify an SSR/SSG style collector. Currently only supports `styled-components`.
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
import { ViteReactSSG } from 'vite-react-ssg'
|
|
179
|
+
import { getStyledComponentsCollector } from 'vite-react-ssg/style-collectors'
|
|
180
|
+
import { routes } from './App.js'
|
|
181
|
+
import './index.css'
|
|
182
|
+
|
|
183
|
+
export const createRoot = ViteReactSSG({ routes }, () => {}, { getStyleCollector: getStyledComponentsCollector })
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
You can provide your own by looking at the [implementation](./src/style-collectors/) of any of the existing collectors.
|
|
187
|
+
|
|
173
188
|
## Critical CSS
|
|
174
189
|
|
|
175
190
|
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.
|
package/dist/index.cjs
CHANGED
|
@@ -42,11 +42,73 @@ Current type: ${React.isValidElement(children) ? "React element" : typeof childr
|
|
|
42
42
|
return fallback ?? null;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
const Link = React.forwardRef((props, ref) => {
|
|
46
|
+
const {
|
|
47
|
+
replace,
|
|
48
|
+
state,
|
|
49
|
+
target,
|
|
50
|
+
preventScrollReset,
|
|
51
|
+
relative,
|
|
52
|
+
to,
|
|
53
|
+
onClick
|
|
54
|
+
} = props;
|
|
55
|
+
const internalOnClick = reactRouterDom.useLinkClickHandler(to, {
|
|
56
|
+
replace,
|
|
57
|
+
state,
|
|
58
|
+
target,
|
|
59
|
+
preventScrollReset,
|
|
60
|
+
relative
|
|
61
|
+
});
|
|
62
|
+
function handleClick(event) {
|
|
63
|
+
if (onClick)
|
|
64
|
+
onClick(event);
|
|
65
|
+
if (!event.defaultPrevented) {
|
|
66
|
+
React__default.startTransition(() => {
|
|
67
|
+
internalOnClick(event);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
event.defaultPrevented = true;
|
|
71
|
+
event.preventDefault();
|
|
72
|
+
}
|
|
73
|
+
return /* @__PURE__ */ React__default.createElement(reactRouterDom.Link, { ...props, ref, onClick: handleClick });
|
|
74
|
+
});
|
|
75
|
+
const NavLink = React.forwardRef((props, ref) => {
|
|
76
|
+
const {
|
|
77
|
+
replace,
|
|
78
|
+
state,
|
|
79
|
+
target,
|
|
80
|
+
preventScrollReset,
|
|
81
|
+
relative,
|
|
82
|
+
to,
|
|
83
|
+
onClick
|
|
84
|
+
} = props;
|
|
85
|
+
const internalOnClick = reactRouterDom.useLinkClickHandler(to, {
|
|
86
|
+
replace,
|
|
87
|
+
state,
|
|
88
|
+
target,
|
|
89
|
+
preventScrollReset,
|
|
90
|
+
relative
|
|
91
|
+
});
|
|
92
|
+
function handleClick(event) {
|
|
93
|
+
if (onClick)
|
|
94
|
+
onClick(event);
|
|
95
|
+
if (!event.defaultPrevented) {
|
|
96
|
+
React__default.startTransition(() => {
|
|
97
|
+
internalOnClick(event);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
event.defaultPrevented = true;
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
}
|
|
103
|
+
return /* @__PURE__ */ React__default.createElement(reactRouterDom.NavLink, { ...props, ref, onClick: handleClick });
|
|
104
|
+
});
|
|
105
|
+
|
|
45
106
|
function ViteReactSSG(routerOptions, fn, options = {}) {
|
|
46
107
|
const {
|
|
47
108
|
transformState,
|
|
48
109
|
rootContainer = "#root",
|
|
49
|
-
ssrWhenDev = true
|
|
110
|
+
ssrWhenDev = true,
|
|
111
|
+
getStyleCollector = null
|
|
50
112
|
} = options;
|
|
51
113
|
const isClient = typeof window !== "undefined";
|
|
52
114
|
async function createRoot(client = false, routePath) {
|
|
@@ -58,7 +120,6 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
|
|
|
58
120
|
return Promise.all(appRenderCallbacks.map((cb) => cb()));
|
|
59
121
|
};
|
|
60
122
|
const context = {
|
|
61
|
-
// app: App,
|
|
62
123
|
isClient,
|
|
63
124
|
routes: routerOptions.routes,
|
|
64
125
|
router: browserRouter,
|
|
@@ -67,7 +128,8 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
|
|
|
67
128
|
triggerOnSSRAppRendered,
|
|
68
129
|
initialState: {},
|
|
69
130
|
transformState,
|
|
70
|
-
routePath
|
|
131
|
+
routePath,
|
|
132
|
+
getStyleCollector
|
|
71
133
|
};
|
|
72
134
|
if (client) {
|
|
73
135
|
await documentReady();
|
|
@@ -106,4 +168,6 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
|
|
|
106
168
|
|
|
107
169
|
exports.Head = SiteMetadataDefaults.Head;
|
|
108
170
|
exports.ClientOnly = ClientOnly;
|
|
171
|
+
exports.Link = Link;
|
|
172
|
+
exports.NavLink = NavLink;
|
|
109
173
|
exports.ViteReactSSG = ViteReactSSG;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { R as RouterOptions, V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from './types-
|
|
2
|
-
export { I as IndexRouteRecord, N as NonIndexRouteRecord, c as RouteRecord, b as ViteReactSSGOptions } from './types-
|
|
3
|
-
import { ReactNode } from 'react';
|
|
1
|
+
import { R as RouterOptions, V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from './types-408e974a.js';
|
|
2
|
+
export { I as IndexRouteRecord, N as NonIndexRouteRecord, c as RouteRecord, S as StyleCollector, b as ViteReactSSGOptions } from './types-408e974a.js';
|
|
3
|
+
import React, { ReactNode } from 'react';
|
|
4
4
|
import { HelmetProps } from 'react-helmet-async';
|
|
5
|
+
import { LinkProps, NavLinkProps } from 'react-router-dom';
|
|
5
6
|
import 'critters';
|
|
6
|
-
import 'react-router-dom';
|
|
7
7
|
|
|
8
8
|
type Props = HelmetProps & {
|
|
9
9
|
children: ReactNode;
|
|
@@ -16,6 +16,9 @@ interface ClientOnlyProps {
|
|
|
16
16
|
}
|
|
17
17
|
declare function ClientOnly({ children, fallback, }: ClientOnlyProps): JSX.Element | null;
|
|
18
18
|
|
|
19
|
+
declare const Link: React.ForwardRefExoticComponent<LinkProps & React.RefAttributes<HTMLAnchorElement>>;
|
|
20
|
+
declare const NavLink: React.ForwardRefExoticComponent<NavLinkProps & React.RefAttributes<HTMLAnchorElement>>;
|
|
21
|
+
|
|
19
22
|
declare function ViteReactSSG(routerOptions: RouterOptions, fn?: (context: ViteReactSSGContext<true>) => Promise<void> | void, options?: ViteReactSSGClientOptions): (client?: boolean, routePath?: string) => Promise<ViteReactSSGContext<true>>;
|
|
20
23
|
|
|
21
|
-
export { ClientOnly, Head, RouterOptions, ViteReactSSG, ViteReactSSGClientOptions, ViteReactSSGContext };
|
|
24
|
+
export { ClientOnly, Head, Link, NavLink, RouterOptions, ViteReactSSG, ViteReactSSGClientOptions, ViteReactSSGContext };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import React, { useState, useEffect, isValidElement } from 'react';
|
|
1
|
+
import React, { useState, useEffect, isValidElement, forwardRef } from 'react';
|
|
2
2
|
import { createRoot, hydrateRoot } from 'react-dom/client';
|
|
3
3
|
import { HelmetProvider } from 'react-helmet-async';
|
|
4
|
-
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
|
|
4
|
+
import { useLinkClickHandler, Link as Link$1, NavLink as NavLink$1, createBrowserRouter, RouterProvider } from 'react-router-dom';
|
|
5
5
|
import { d as deserializeState, S as SiteMetadataDefaults } from './shared/vite-react-ssg.9d005d5e.mjs';
|
|
6
6
|
export { H as Head } from './shared/vite-react-ssg.9d005d5e.mjs';
|
|
7
7
|
|
|
@@ -37,11 +37,73 @@ Current type: ${isValidElement(children) ? "React element" : typeof children}`);
|
|
|
37
37
|
return fallback ?? null;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
const Link = forwardRef((props, ref) => {
|
|
41
|
+
const {
|
|
42
|
+
replace,
|
|
43
|
+
state,
|
|
44
|
+
target,
|
|
45
|
+
preventScrollReset,
|
|
46
|
+
relative,
|
|
47
|
+
to,
|
|
48
|
+
onClick
|
|
49
|
+
} = props;
|
|
50
|
+
const internalOnClick = useLinkClickHandler(to, {
|
|
51
|
+
replace,
|
|
52
|
+
state,
|
|
53
|
+
target,
|
|
54
|
+
preventScrollReset,
|
|
55
|
+
relative
|
|
56
|
+
});
|
|
57
|
+
function handleClick(event) {
|
|
58
|
+
if (onClick)
|
|
59
|
+
onClick(event);
|
|
60
|
+
if (!event.defaultPrevented) {
|
|
61
|
+
React.startTransition(() => {
|
|
62
|
+
internalOnClick(event);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
event.defaultPrevented = true;
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
}
|
|
68
|
+
return /* @__PURE__ */ React.createElement(Link$1, { ...props, ref, onClick: handleClick });
|
|
69
|
+
});
|
|
70
|
+
const NavLink = forwardRef((props, ref) => {
|
|
71
|
+
const {
|
|
72
|
+
replace,
|
|
73
|
+
state,
|
|
74
|
+
target,
|
|
75
|
+
preventScrollReset,
|
|
76
|
+
relative,
|
|
77
|
+
to,
|
|
78
|
+
onClick
|
|
79
|
+
} = props;
|
|
80
|
+
const internalOnClick = useLinkClickHandler(to, {
|
|
81
|
+
replace,
|
|
82
|
+
state,
|
|
83
|
+
target,
|
|
84
|
+
preventScrollReset,
|
|
85
|
+
relative
|
|
86
|
+
});
|
|
87
|
+
function handleClick(event) {
|
|
88
|
+
if (onClick)
|
|
89
|
+
onClick(event);
|
|
90
|
+
if (!event.defaultPrevented) {
|
|
91
|
+
React.startTransition(() => {
|
|
92
|
+
internalOnClick(event);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
event.defaultPrevented = true;
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
}
|
|
98
|
+
return /* @__PURE__ */ React.createElement(NavLink$1, { ...props, ref, onClick: handleClick });
|
|
99
|
+
});
|
|
100
|
+
|
|
40
101
|
function ViteReactSSG(routerOptions, fn, options = {}) {
|
|
41
102
|
const {
|
|
42
103
|
transformState,
|
|
43
104
|
rootContainer = "#root",
|
|
44
|
-
ssrWhenDev = true
|
|
105
|
+
ssrWhenDev = true,
|
|
106
|
+
getStyleCollector = null
|
|
45
107
|
} = options;
|
|
46
108
|
const isClient = typeof window !== "undefined";
|
|
47
109
|
async function createRoot$1(client = false, routePath) {
|
|
@@ -53,7 +115,6 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
|
|
|
53
115
|
return Promise.all(appRenderCallbacks.map((cb) => cb()));
|
|
54
116
|
};
|
|
55
117
|
const context = {
|
|
56
|
-
// app: App,
|
|
57
118
|
isClient,
|
|
58
119
|
routes: routerOptions.routes,
|
|
59
120
|
router: browserRouter,
|
|
@@ -62,7 +123,8 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
|
|
|
62
123
|
triggerOnSSRAppRendered,
|
|
63
124
|
initialState: {},
|
|
64
125
|
transformState,
|
|
65
|
-
routePath
|
|
126
|
+
routePath,
|
|
127
|
+
getStyleCollector
|
|
66
128
|
};
|
|
67
129
|
if (client) {
|
|
68
130
|
await documentReady();
|
|
@@ -99,4 +161,4 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
|
|
|
99
161
|
return createRoot$1;
|
|
100
162
|
}
|
|
101
163
|
|
|
102
|
-
export { ClientOnly, ViteReactSSG };
|
|
164
|
+
export { ClientOnly, Link, NavLink, ViteReactSSG };
|
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 dev = require('../shared/vite-react-ssg.
|
|
6
|
+
const dev = require('../shared/vite-react-ssg.b0fd1823.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, d as dev } from '../shared/vite-react-ssg.
|
|
4
|
+
import { b as build, d as dev } from '../shared/vite-react-ssg.b83f6ed3.mjs';
|
|
5
5
|
import 'node:path';
|
|
6
6
|
import 'node:module';
|
|
7
7
|
import 'fs-extra';
|
package/dist/node.cjs
CHANGED
package/dist/node.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { InlineConfig } from 'vite';
|
|
2
|
-
import { b as ViteReactSSGOptions } from './types-
|
|
2
|
+
import { b as ViteReactSSGOptions } from './types-408e974a.js';
|
|
3
3
|
import 'critters';
|
|
4
|
+
import 'react';
|
|
4
5
|
import 'react-router-dom';
|
|
5
6
|
|
|
6
7
|
declare function build(ssgOptions?: Partial<ViteReactSSGOptions>, viteConfig?: InlineConfig): Promise<void>;
|
package/dist/node.mjs
CHANGED
|
@@ -934,7 +934,7 @@ async function resolveAlias(config, entry) {
|
|
|
934
934
|
return result || node_path.join(config.root, entry);
|
|
935
935
|
}
|
|
936
936
|
const { version } = JSON.parse(
|
|
937
|
-
node_fs.readFileSync(new URL("../../package.json", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (document.currentScript && document.currentScript.src || new URL('shared/vite-react-ssg.
|
|
937
|
+
node_fs.readFileSync(new URL("../../package.json", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (document.currentScript && document.currentScript.src || new URL('shared/vite-react-ssg.b0fd1823.cjs', document.baseURI).href)))).toString()
|
|
938
938
|
);
|
|
939
939
|
|
|
940
940
|
async function getCritters(outDir, options = {}) {
|
|
@@ -998,14 +998,16 @@ class WritableAsPromise extends node_stream.Writable {
|
|
|
998
998
|
}
|
|
999
999
|
}
|
|
1000
1000
|
|
|
1001
|
-
async function render(routes, request) {
|
|
1001
|
+
async function render(routes, request, styleCollector) {
|
|
1002
1002
|
const { dataRoutes, query } = server_js.createStaticHandler(routes);
|
|
1003
1003
|
const context = await query(request);
|
|
1004
1004
|
const helmetContext = {};
|
|
1005
1005
|
if (context instanceof Response)
|
|
1006
1006
|
throw context;
|
|
1007
1007
|
const router = server_js.createStaticRouter(dataRoutes, context);
|
|
1008
|
-
|
|
1008
|
+
let 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 }));
|
|
1009
|
+
if (styleCollector)
|
|
1010
|
+
app = styleCollector.collect(app);
|
|
1009
1011
|
const appHTML = await renderStaticApp(app);
|
|
1010
1012
|
const { helmet } = helmetContext;
|
|
1011
1013
|
const htmlAttributes = helmet.htmlAttributes.toString();
|
|
@@ -1016,8 +1018,9 @@ async function render(routes, request) {
|
|
|
1016
1018
|
helmet.link.toString(),
|
|
1017
1019
|
helmet.script.toString()
|
|
1018
1020
|
];
|
|
1021
|
+
const styleTag = styleCollector?.toString?.(appHTML) ?? "";
|
|
1019
1022
|
const metaAttributes = metaStrings.filter(Boolean);
|
|
1020
|
-
return { appHTML, htmlAttributes, bodyAttributes, metaAttributes };
|
|
1023
|
+
return { appHTML, htmlAttributes, bodyAttributes, metaAttributes, styleTag };
|
|
1021
1024
|
}
|
|
1022
1025
|
|
|
1023
1026
|
function renderPreloadLinks(document, modules, ssrManifest) {
|
|
@@ -1197,7 +1200,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1197
1200
|
const prefix = format === "esm" && process.platform === "win32" ? "file://" : "";
|
|
1198
1201
|
const ext = format === "esm" ? ".mjs" : ".cjs";
|
|
1199
1202
|
const serverEntry = node_path.join(prefix, ssgOut, node_path.parse(ssrEntry).name + ext);
|
|
1200
|
-
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.
|
|
1203
|
+
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.b0fd1823.cjs', document.baseURI).href)));
|
|
1201
1204
|
const { createRoot, includedRoutes: serverEntryIncludedRoutes } = format === "esm" ? await import(serverEntry) : _require(serverEntry);
|
|
1202
1205
|
const includedRoutes = serverEntryIncludedRoutes || configIncludedRoutes;
|
|
1203
1206
|
const { routes } = await createRoot(false);
|
|
@@ -1217,14 +1220,15 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1217
1220
|
queue.add(async () => {
|
|
1218
1221
|
try {
|
|
1219
1222
|
const appCtx = await createRoot(false, route);
|
|
1220
|
-
const { routes: routes2, initialState, triggerOnSSRAppRendered, transformState = SiteMetadataDefaults.serializeState } = appCtx;
|
|
1223
|
+
const { routes: routes2, initialState, triggerOnSSRAppRendered, transformState = SiteMetadataDefaults.serializeState, getStyleCollector } = appCtx;
|
|
1224
|
+
const styleCollector = getStyleCollector ? await getStyleCollector() : null;
|
|
1221
1225
|
const transformedIndexHTML = await onBeforePageRender?.(route, indexHTML, appCtx) || indexHTML;
|
|
1222
1226
|
const url = new URL(route, "http://vite-react-ssg.com");
|
|
1223
1227
|
url.search = "";
|
|
1224
1228
|
url.hash = "";
|
|
1225
1229
|
url.pathname = route;
|
|
1226
1230
|
const request = new Request(url.href);
|
|
1227
|
-
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes } = await render([...routes2], request);
|
|
1231
|
+
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag } = await render([...routes2], request, styleCollector);
|
|
1228
1232
|
await triggerOnSSRAppRendered?.(route, appHTML, appCtx);
|
|
1229
1233
|
const renderedHTML = await renderHTML({
|
|
1230
1234
|
rootContainerId,
|
|
@@ -1242,6 +1246,8 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1242
1246
|
let transformed = await onPageRendered?.(route, html, appCtx) || html;
|
|
1243
1247
|
if (critters)
|
|
1244
1248
|
transformed = await critters.process(transformed);
|
|
1249
|
+
if (styleTag)
|
|
1250
|
+
transformed = transformed.replace("<head>", `<head>${styleTag}`);
|
|
1245
1251
|
const formatted = await formatHtml(transformed, formatting);
|
|
1246
1252
|
const relativeRouteFile = `${(route.endsWith("/") ? `${route}index` : route).replace(/^\//g, "")}.html`;
|
|
1247
1253
|
const filename = dirStyle === "nested" ? node_path.join(route.replace(/^\//g, ""), "index.html") : relativeRouteFile;
|
|
@@ -1418,9 +1424,11 @@ async function dev(ssgOptions = {}, viteConfig = {}) {
|
|
|
1418
1424
|
const indexHTML = await viteServer.transformIndexHtml(url, template);
|
|
1419
1425
|
const createRoot = await viteServer.ssrLoadModule(ssrEntry).then((m) => m.createRoot);
|
|
1420
1426
|
const appCtx = await createRoot(false, url);
|
|
1421
|
-
const { routes } = appCtx;
|
|
1427
|
+
const { routes, getStyleCollector } = appCtx;
|
|
1422
1428
|
const transformedIndexHTML = await onBeforePageRender?.(url, indexHTML, appCtx) || indexHTML;
|
|
1423
|
-
const
|
|
1429
|
+
const styleCollector = getStyleCollector ? await getStyleCollector() : null;
|
|
1430
|
+
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag } = await render([...routes], createFetchRequest(req), styleCollector);
|
|
1431
|
+
metaAttributes.push(styleTag);
|
|
1424
1432
|
const mod = await viteServer.moduleGraph.getModuleByUrl(entry);
|
|
1425
1433
|
const assetsUrls = /* @__PURE__ */ new Set();
|
|
1426
1434
|
const collectAssets = async (mod2) => {
|
|
@@ -990,14 +990,16 @@ class WritableAsPromise extends Writable {
|
|
|
990
990
|
}
|
|
991
991
|
}
|
|
992
992
|
|
|
993
|
-
async function render(routes, request) {
|
|
993
|
+
async function render(routes, request, styleCollector) {
|
|
994
994
|
const { dataRoutes, query } = createStaticHandler(routes);
|
|
995
995
|
const context = await query(request);
|
|
996
996
|
const helmetContext = {};
|
|
997
997
|
if (context instanceof Response)
|
|
998
998
|
throw context;
|
|
999
999
|
const router = createStaticRouter(dataRoutes, context);
|
|
1000
|
-
|
|
1000
|
+
let app = /* @__PURE__ */ React.createElement(HelmetProvider, { context: helmetContext }, /* @__PURE__ */ React.createElement(SiteMetadataDefaults, null), /* @__PURE__ */ React.createElement(StaticRouterProvider, { router, context }));
|
|
1001
|
+
if (styleCollector)
|
|
1002
|
+
app = styleCollector.collect(app);
|
|
1001
1003
|
const appHTML = await renderStaticApp(app);
|
|
1002
1004
|
const { helmet } = helmetContext;
|
|
1003
1005
|
const htmlAttributes = helmet.htmlAttributes.toString();
|
|
@@ -1008,8 +1010,9 @@ async function render(routes, request) {
|
|
|
1008
1010
|
helmet.link.toString(),
|
|
1009
1011
|
helmet.script.toString()
|
|
1010
1012
|
];
|
|
1013
|
+
const styleTag = styleCollector?.toString?.(appHTML) ?? "";
|
|
1011
1014
|
const metaAttributes = metaStrings.filter(Boolean);
|
|
1012
|
-
return { appHTML, htmlAttributes, bodyAttributes, metaAttributes };
|
|
1015
|
+
return { appHTML, htmlAttributes, bodyAttributes, metaAttributes, styleTag };
|
|
1013
1016
|
}
|
|
1014
1017
|
|
|
1015
1018
|
function renderPreloadLinks(document, modules, ssrManifest) {
|
|
@@ -1209,14 +1212,15 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1209
1212
|
queue.add(async () => {
|
|
1210
1213
|
try {
|
|
1211
1214
|
const appCtx = await createRoot(false, route);
|
|
1212
|
-
const { routes: routes2, initialState, triggerOnSSRAppRendered, transformState = serializeState } = appCtx;
|
|
1215
|
+
const { routes: routes2, initialState, triggerOnSSRAppRendered, transformState = serializeState, getStyleCollector } = appCtx;
|
|
1216
|
+
const styleCollector = getStyleCollector ? await getStyleCollector() : null;
|
|
1213
1217
|
const transformedIndexHTML = await onBeforePageRender?.(route, indexHTML, appCtx) || indexHTML;
|
|
1214
1218
|
const url = new URL(route, "http://vite-react-ssg.com");
|
|
1215
1219
|
url.search = "";
|
|
1216
1220
|
url.hash = "";
|
|
1217
1221
|
url.pathname = route;
|
|
1218
1222
|
const request = new Request(url.href);
|
|
1219
|
-
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes } = await render([...routes2], request);
|
|
1223
|
+
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag } = await render([...routes2], request, styleCollector);
|
|
1220
1224
|
await triggerOnSSRAppRendered?.(route, appHTML, appCtx);
|
|
1221
1225
|
const renderedHTML = await renderHTML({
|
|
1222
1226
|
rootContainerId,
|
|
@@ -1234,6 +1238,8 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1234
1238
|
let transformed = await onPageRendered?.(route, html, appCtx) || html;
|
|
1235
1239
|
if (critters)
|
|
1236
1240
|
transformed = await critters.process(transformed);
|
|
1241
|
+
if (styleTag)
|
|
1242
|
+
transformed = transformed.replace("<head>", `<head>${styleTag}`);
|
|
1237
1243
|
const formatted = await formatHtml(transformed, formatting);
|
|
1238
1244
|
const relativeRouteFile = `${(route.endsWith("/") ? `${route}index` : route).replace(/^\//g, "")}.html`;
|
|
1239
1245
|
const filename = dirStyle === "nested" ? join(route.replace(/^\//g, ""), "index.html") : relativeRouteFile;
|
|
@@ -1410,9 +1416,11 @@ async function dev(ssgOptions = {}, viteConfig = {}) {
|
|
|
1410
1416
|
const indexHTML = await viteServer.transformIndexHtml(url, template);
|
|
1411
1417
|
const createRoot = await viteServer.ssrLoadModule(ssrEntry).then((m) => m.createRoot);
|
|
1412
1418
|
const appCtx = await createRoot(false, url);
|
|
1413
|
-
const { routes } = appCtx;
|
|
1419
|
+
const { routes, getStyleCollector } = appCtx;
|
|
1414
1420
|
const transformedIndexHTML = await onBeforePageRender?.(url, indexHTML, appCtx) || indexHTML;
|
|
1415
|
-
const
|
|
1421
|
+
const styleCollector = getStyleCollector ? await getStyleCollector() : null;
|
|
1422
|
+
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag } = await render([...routes], createFetchRequest(req), styleCollector);
|
|
1423
|
+
metaAttributes.push(styleTag);
|
|
1416
1424
|
const mod = await viteServer.moduleGraph.getModuleByUrl(entry);
|
|
1417
1425
|
const assetsUrls = /* @__PURE__ */ new Set();
|
|
1418
1426
|
const collectAssets = async (mod2) => {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
async function ssrCollector() {
|
|
4
|
+
const { ServerStyleSheet } = await import('styled-components');
|
|
5
|
+
const sheet = new ServerStyleSheet();
|
|
6
|
+
return {
|
|
7
|
+
collect(app) {
|
|
8
|
+
return sheet.collectStyles(app);
|
|
9
|
+
},
|
|
10
|
+
toString() {
|
|
11
|
+
return sheet.getStyleTags();
|
|
12
|
+
},
|
|
13
|
+
cleanup() {
|
|
14
|
+
sheet.seal();
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const styledComponents = undefined.SSR ? ssrCollector : null;
|
|
19
|
+
|
|
20
|
+
exports.getStyledComponentsCollector = styledComponents;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
declare function ssrCollector(): Promise<{
|
|
4
|
+
collect(app: ReactElement): JSX.Element;
|
|
5
|
+
toString(): string;
|
|
6
|
+
cleanup(): void;
|
|
7
|
+
}>;
|
|
8
|
+
declare const _default: typeof ssrCollector | null;
|
|
9
|
+
|
|
10
|
+
export { _default as getStyledComponentsCollector };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
async function ssrCollector() {
|
|
2
|
+
const { ServerStyleSheet } = await import('styled-components');
|
|
3
|
+
const sheet = new ServerStyleSheet();
|
|
4
|
+
return {
|
|
5
|
+
collect(app) {
|
|
6
|
+
return sheet.collectStyles(app);
|
|
7
|
+
},
|
|
8
|
+
toString() {
|
|
9
|
+
return sheet.getStyleTags();
|
|
10
|
+
},
|
|
11
|
+
cleanup() {
|
|
12
|
+
sheet.seal();
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const styledComponents = import.meta.env.SSR ? ssrCollector : null;
|
|
17
|
+
|
|
18
|
+
export { styledComponents as getStyledComponentsCollector };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Options } from 'critters';
|
|
2
|
+
import { ReactElement } from 'react';
|
|
2
3
|
import { NonIndexRouteObject, IndexRouteObject, createBrowserRouter } from 'react-router-dom';
|
|
3
4
|
|
|
4
5
|
type Router = ReturnType<typeof createBrowserRouter>;
|
|
@@ -116,6 +117,7 @@ interface ViteReactSSGContext<HasRouter extends boolean = true> {
|
|
|
116
117
|
* Current router path on SSG, `undefined` on client side.
|
|
117
118
|
*/
|
|
118
119
|
routePath?: string;
|
|
120
|
+
getStyleCollector: (() => StyleCollector | Promise<StyleCollector>) | null;
|
|
119
121
|
}
|
|
120
122
|
interface ViteReactSSGClientOptions {
|
|
121
123
|
transformState?: (state: any) => any;
|
|
@@ -134,6 +136,7 @@ interface ViteReactSSGClientOptions {
|
|
|
134
136
|
* @default true
|
|
135
137
|
*/
|
|
136
138
|
ssrWhenDev?: boolean;
|
|
139
|
+
getStyleCollector?: (() => StyleCollector | Promise<StyleCollector>) | null;
|
|
137
140
|
}
|
|
138
141
|
interface CommonRouteOptions {
|
|
139
142
|
/**
|
|
@@ -152,10 +155,15 @@ interface RouterOptions {
|
|
|
152
155
|
routes: RouteRecord[];
|
|
153
156
|
createFetchRequest?: <T>(req: T) => Request;
|
|
154
157
|
}
|
|
158
|
+
interface StyleCollector {
|
|
159
|
+
collect: (app: ReactElement) => ReactElement;
|
|
160
|
+
toString: (html: string) => string;
|
|
161
|
+
cleanup?: () => void;
|
|
162
|
+
}
|
|
155
163
|
declare module 'vite' {
|
|
156
164
|
interface UserConfig {
|
|
157
165
|
ssgOptions?: ViteReactSSGOptions;
|
|
158
166
|
}
|
|
159
167
|
}
|
|
160
168
|
|
|
161
|
-
export { IndexRouteRecord as I, NonIndexRouteRecord as N, RouterOptions as R, ViteReactSSGContext as V, ViteReactSSGClientOptions as a, ViteReactSSGOptions as b, RouteRecord as c };
|
|
169
|
+
export { IndexRouteRecord as I, NonIndexRouteRecord as N, RouterOptions as R, StyleCollector as S, ViteReactSSGContext as V, ViteReactSSGClientOptions as a, ViteReactSSGOptions as b, RouteRecord as c };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-react-ssg",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2",
|
|
5
5
|
"packageManager": "pnpm@8.6.6",
|
|
6
6
|
"description": "",
|
|
7
7
|
"author": "Riri <Daydreamerriri@outlook.com>",
|
|
@@ -30,6 +30,11 @@
|
|
|
30
30
|
"types": "./dist/node.d.ts",
|
|
31
31
|
"require": "./dist/node.cjs",
|
|
32
32
|
"import": "./dist/node.mjs"
|
|
33
|
+
},
|
|
34
|
+
"./style-collectors": {
|
|
35
|
+
"types": "./dist/style-collectors.d.ts",
|
|
36
|
+
"require": "./dist/style-collectors.cjs",
|
|
37
|
+
"import": "./dist/style-collectors.mjs"
|
|
33
38
|
}
|
|
34
39
|
},
|
|
35
40
|
"main": "./dist/index.mjs",
|
|
@@ -39,6 +44,9 @@
|
|
|
39
44
|
"*": {
|
|
40
45
|
"node": [
|
|
41
46
|
"./dist/node.d.ts"
|
|
47
|
+
],
|
|
48
|
+
"style-collectors": [
|
|
49
|
+
"./dist/style-collectors.d.ts"
|
|
42
50
|
]
|
|
43
51
|
}
|
|
44
52
|
},
|
|
@@ -64,6 +72,7 @@
|
|
|
64
72
|
"react": "^18.0.0",
|
|
65
73
|
"react-dom": "^18.0.0",
|
|
66
74
|
"react-router-dom": "^6.14.1",
|
|
75
|
+
"styled-components": "^6.0.0",
|
|
67
76
|
"vite": "^2.0.0 || ^3.0.0 || ^4.0.0"
|
|
68
77
|
},
|
|
69
78
|
"peerDependenciesMeta": {
|
|
@@ -72,6 +81,9 @@
|
|
|
72
81
|
},
|
|
73
82
|
"react-router-dom": {
|
|
74
83
|
"optional": true
|
|
84
|
+
},
|
|
85
|
+
"styled-components": {
|
|
86
|
+
"optional": true
|
|
75
87
|
}
|
|
76
88
|
},
|
|
77
89
|
"dependencies": {
|
|
@@ -106,6 +118,7 @@
|
|
|
106
118
|
"react-router-dom": "^6.14.1",
|
|
107
119
|
"rimraf": "5.0.1",
|
|
108
120
|
"simple-git-hooks": "^2.8.1",
|
|
121
|
+
"styled-components": "6.0.5",
|
|
109
122
|
"typescript": "5.1.6",
|
|
110
123
|
"unbuild": "^1.2.1",
|
|
111
124
|
"vite": "^4.4.0",
|