vite-plugin-ssr-config 1.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/README.md +244 -0
- package/dist/index.cjs +489 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +452 -0
- package/package.json +57 -0
- package/ssr/entryClient.jsx +25 -0
- package/ssr/entryRender.jsx +44 -0
- package/ssr/errorBoundary.jsx +165 -0
- package/ssr/handler.js +13 -0
- package/ssr/liveReload.jsx +33 -0
- package/ssr/pageBrowser.jsx +36 -0
- package/ssr/pageServer.jsx +39 -0
- package/ssr/root.jsx +21 -0
- package/ssr/rootRoutes.jsx +16 -0
- package/ssr/server.js +49 -0
- package/ssr/viteScripts.jsx +7 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import React, { Component, useState } from "react";
|
|
2
|
+
|
|
3
|
+
const parseError = (error, showModules) => {
|
|
4
|
+
if (error === null) {
|
|
5
|
+
return "";
|
|
6
|
+
}
|
|
7
|
+
if (error instanceof Error) {
|
|
8
|
+
return {
|
|
9
|
+
message: error.message,
|
|
10
|
+
stack:
|
|
11
|
+
error.stack
|
|
12
|
+
?.trim()
|
|
13
|
+
.split("\n")
|
|
14
|
+
.filter((line) =>
|
|
15
|
+
showModules ? true : !line.includes("node_modules")
|
|
16
|
+
) || [],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (typeof error === "object") {
|
|
20
|
+
return {
|
|
21
|
+
...error,
|
|
22
|
+
componentStack:
|
|
23
|
+
error.componentStack
|
|
24
|
+
?.trim()
|
|
25
|
+
.split("\n")
|
|
26
|
+
.filter((line) =>
|
|
27
|
+
showModules ? true : !line.includes("node_modules")
|
|
28
|
+
) || [],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return error?.toString();
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const ErrorPanel = ({ error, errorInfo }) => {
|
|
35
|
+
const [showModules, setShowModules] = useState({
|
|
36
|
+
errorStack: false,
|
|
37
|
+
componentStack: false,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const showModulesToogle = (name) => {
|
|
41
|
+
setShowModules((prev) => ({
|
|
42
|
+
...prev,
|
|
43
|
+
[name]: !prev[name],
|
|
44
|
+
}));
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const errorData = parseError(error, showModules.errorStack);
|
|
48
|
+
const errorInfoData = parseError(errorInfo, showModules.componentStack);
|
|
49
|
+
|
|
50
|
+
const cssStyles = `
|
|
51
|
+
.ssrkBody {
|
|
52
|
+
font-family: Arial, sans-serif;
|
|
53
|
+
margin: 0;
|
|
54
|
+
padding: 0;
|
|
55
|
+
background-color: #f4f4f9;
|
|
56
|
+
color: #333;
|
|
57
|
+
}
|
|
58
|
+
.ssrkContainer {
|
|
59
|
+
width: 100%;
|
|
60
|
+
max-width: 800px;
|
|
61
|
+
margin: 20px auto;
|
|
62
|
+
padding: 20px;
|
|
63
|
+
background-color: white;
|
|
64
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
65
|
+
}
|
|
66
|
+
.ssrkError, .ssrkDetails {
|
|
67
|
+
margin-bottom: 20px;
|
|
68
|
+
}
|
|
69
|
+
h3 {
|
|
70
|
+
font-size: 1.5rem;
|
|
71
|
+
margin-bottom: 10px;
|
|
72
|
+
}
|
|
73
|
+
label {
|
|
74
|
+
margin-right: 10px;
|
|
75
|
+
}
|
|
76
|
+
pre {
|
|
77
|
+
background-color: #f7f7f7;
|
|
78
|
+
padding: 10px;
|
|
79
|
+
border-radius: 5px;
|
|
80
|
+
font-size: 0.9rem;
|
|
81
|
+
overflow-x: auto;
|
|
82
|
+
overflow-y: auto;
|
|
83
|
+
max-height: 300px;
|
|
84
|
+
max-width: 100%;
|
|
85
|
+
line-height: 1.3em;
|
|
86
|
+
}
|
|
87
|
+
code {
|
|
88
|
+
display: block;
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<html lang="en">
|
|
94
|
+
<head>
|
|
95
|
+
<meta charSet="UTF-8" />
|
|
96
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
97
|
+
<title>Error Occurred</title>
|
|
98
|
+
<style>{cssStyles}</style>
|
|
99
|
+
</head>
|
|
100
|
+
<body className="ssrkBody">
|
|
101
|
+
<div className="ssrkContainer">
|
|
102
|
+
<div className="ssrkError">
|
|
103
|
+
<h3>Trace Error:</h3>
|
|
104
|
+
<div>
|
|
105
|
+
<label>
|
|
106
|
+
<input
|
|
107
|
+
type="checkbox"
|
|
108
|
+
checked={showModules.errorStack}
|
|
109
|
+
onChange={() => showModulesToogle("errorStack")}
|
|
110
|
+
/>
|
|
111
|
+
Show Node Modules in stack trace
|
|
112
|
+
</label>
|
|
113
|
+
</div>
|
|
114
|
+
<code>
|
|
115
|
+
<pre>{JSON.stringify(errorData, null, 2)}</pre>
|
|
116
|
+
</code>
|
|
117
|
+
</div>
|
|
118
|
+
<div className="ssrkDetails">
|
|
119
|
+
<h3>Trace Component:</h3>
|
|
120
|
+
<div>
|
|
121
|
+
<label>
|
|
122
|
+
<input
|
|
123
|
+
type="checkbox"
|
|
124
|
+
checked={showModules.componentStack}
|
|
125
|
+
onChange={() => showModulesToogle("componentStack")}
|
|
126
|
+
/>
|
|
127
|
+
Show Node Modules in stack trace
|
|
128
|
+
</label>
|
|
129
|
+
</div>
|
|
130
|
+
<code>
|
|
131
|
+
<pre>{JSON.stringify(errorInfoData, null, 2)}</pre>
|
|
132
|
+
</code>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</body>
|
|
136
|
+
</html>
|
|
137
|
+
);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export class ErrorBoundary extends Component {
|
|
141
|
+
constructor(props) {
|
|
142
|
+
super(props);
|
|
143
|
+
this.state = {
|
|
144
|
+
hasError: false,
|
|
145
|
+
error: null,
|
|
146
|
+
errorInfo: null,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static getDerivedStateFromError(error) {
|
|
151
|
+
return { hasError: true, error };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
componentDidCatch(error, errorInfo) {
|
|
155
|
+
this.setState({ error, errorInfo });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
render() {
|
|
159
|
+
return this.state.hasError ? (
|
|
160
|
+
<ErrorPanel error={this.state.error} errorInfo={this.state.errorInfo} />
|
|
161
|
+
) : (
|
|
162
|
+
this.props.children
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
}
|
package/ssr/handler.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { render } from "@ssr/entryRender.jsx";
|
|
2
|
+
|
|
3
|
+
export const handler = async (req, res, next) => {
|
|
4
|
+
//Force Fix
|
|
5
|
+
if (req.url === "/%3Canonymous%20code%3E" || req.url.endsWith(".js.map")) {
|
|
6
|
+
return next();
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
await render(req, res, next);
|
|
10
|
+
} catch (error) {
|
|
11
|
+
next(error);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const PRODUCTION = () => null;
|
|
2
|
+
|
|
3
|
+
const DEVELOPMENT = () => {
|
|
4
|
+
const finalUrl = (base, path) => {
|
|
5
|
+
return `${base.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
|
|
6
|
+
};
|
|
7
|
+
const __html = `
|
|
8
|
+
import RefreshRuntime from "${finalUrl(
|
|
9
|
+
import.meta.env.BASE_URL,
|
|
10
|
+
"/@react-refresh"
|
|
11
|
+
)}"
|
|
12
|
+
RefreshRuntime.injectIntoGlobalHook(window)
|
|
13
|
+
window.$RefreshReg$ = () => {}
|
|
14
|
+
window.$RefreshSig$ = () => (type) => type
|
|
15
|
+
window.__vite_plugin_react_preamble_installed__ = true;
|
|
16
|
+
`;
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<script
|
|
20
|
+
type="module"
|
|
21
|
+
dangerouslySetInnerHTML={{ __html }}
|
|
22
|
+
suppressHydrationWarning={true}
|
|
23
|
+
></script>
|
|
24
|
+
<script
|
|
25
|
+
type="module"
|
|
26
|
+
src={finalUrl(import.meta.env.BASE_URL, "/@vite/client")}
|
|
27
|
+
suppressHydrationWarning={true}
|
|
28
|
+
></script>
|
|
29
|
+
</>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const LiveReload = import.meta.env.PROD ? PRODUCTION : DEVELOPMENT;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ErrorBoundary } from "@ssr/errorBoundary.jsx";
|
|
2
|
+
import { RootRoutes } from "@ssr/rootRoutes.jsx";
|
|
3
|
+
import { StrictMode, Suspense } from "react";
|
|
4
|
+
import { QueryClient, QueryClientProvider, hydrate } from "react-query";
|
|
5
|
+
import { BrowserRouter } from "react-router-dom";
|
|
6
|
+
|
|
7
|
+
const queryClient = new QueryClient({
|
|
8
|
+
defaultOptions: {
|
|
9
|
+
queries: {
|
|
10
|
+
suspense: false,
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const PageBrowser = ({ hydratedState = {}, setHydratedState }) => {
|
|
16
|
+
hydrate(queryClient, hydratedState);
|
|
17
|
+
return (
|
|
18
|
+
<ErrorBoundary>
|
|
19
|
+
<QueryClientProvider client={queryClient}>
|
|
20
|
+
<Suspense>
|
|
21
|
+
<BrowserRouter
|
|
22
|
+
basename={process.env.SSR_BASENAME}
|
|
23
|
+
future={{ v7_startTransition: true, v7_relativeSplatPath: true }}
|
|
24
|
+
>
|
|
25
|
+
<StrictMode>
|
|
26
|
+
<RootRoutes
|
|
27
|
+
hydratedState={hydratedState}
|
|
28
|
+
setHydratedState={setHydratedState}
|
|
29
|
+
/>
|
|
30
|
+
</StrictMode>
|
|
31
|
+
</BrowserRouter>
|
|
32
|
+
</Suspense>
|
|
33
|
+
</QueryClientProvider>
|
|
34
|
+
</ErrorBoundary>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ErrorBoundary } from "@ssr/errorBoundary.jsx";
|
|
2
|
+
import { RootRoutes } from "@ssr/rootRoutes.jsx";
|
|
3
|
+
import { StrictMode, Suspense } from "react";
|
|
4
|
+
import { dehydrate, QueryClient, QueryClientProvider } from "react-query";
|
|
5
|
+
import { StaticRouter } from "react-router-dom/server";
|
|
6
|
+
|
|
7
|
+
export const PageServer = ({ path, hydratedState, setHydratedState }) => {
|
|
8
|
+
const queryServer = new QueryClient({
|
|
9
|
+
defaultOptions: {
|
|
10
|
+
queries: {
|
|
11
|
+
suspense: true,
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
queryServer.getQueryCache().subscribe(() => {
|
|
16
|
+
const state = dehydrate(queryServer);
|
|
17
|
+
setHydratedState(state);
|
|
18
|
+
});
|
|
19
|
+
return (
|
|
20
|
+
<ErrorBoundary suppressHydrationWarning={true}>
|
|
21
|
+
<QueryClientProvider client={queryServer}>
|
|
22
|
+
<Suspense>
|
|
23
|
+
<StaticRouter
|
|
24
|
+
basename={process.env.SSR_BASENAME}
|
|
25
|
+
location={path}
|
|
26
|
+
future={{ v7_startTransition: true, v7_relativeSplatPath: true }}
|
|
27
|
+
>
|
|
28
|
+
<StrictMode>
|
|
29
|
+
<RootRoutes
|
|
30
|
+
hydratedState={hydratedState}
|
|
31
|
+
setHydratedState={setHydratedState}
|
|
32
|
+
/>
|
|
33
|
+
</StrictMode>
|
|
34
|
+
</StaticRouter>
|
|
35
|
+
</Suspense>
|
|
36
|
+
</QueryClientProvider>
|
|
37
|
+
</ErrorBoundary>
|
|
38
|
+
);
|
|
39
|
+
};
|
package/ssr/root.jsx
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { LiveReload } from "@ssr/liveReload.jsx";
|
|
2
|
+
import { ViteScripts } from "@ssr/viteScripts.jsx";
|
|
3
|
+
import { Outlet } from "react-router-dom";
|
|
4
|
+
|
|
5
|
+
export const RootDocument = () => {
|
|
6
|
+
return (
|
|
7
|
+
<html lang="en">
|
|
8
|
+
<head>
|
|
9
|
+
<meta charSet="utf-8" />
|
|
10
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
11
|
+
<title>Vite + React SSR</title>
|
|
12
|
+
<link rel="icon" href="vite.svg" type="image/svg" />
|
|
13
|
+
<LiveReload />
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<Outlet />
|
|
17
|
+
<ViteScripts />
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { RootDocument } from "@ssr/root.jsx";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { useRoutes } from "react-router-dom";
|
|
4
|
+
import routes from "~react-pages";
|
|
5
|
+
|
|
6
|
+
export const RootRoutes = (props) => {
|
|
7
|
+
const newRoutes = [
|
|
8
|
+
{
|
|
9
|
+
caseSensitive: false,
|
|
10
|
+
path: "",
|
|
11
|
+
element: React.createElement(RootDocument, props),
|
|
12
|
+
children: routes,
|
|
13
|
+
},
|
|
14
|
+
];
|
|
15
|
+
return useRoutes(newRoutes);
|
|
16
|
+
};
|
package/ssr/server.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { handler } from "@ssr/handler.js";
|
|
2
|
+
import { loadEnv } from "dotenv-local";
|
|
3
|
+
import express from "express";
|
|
4
|
+
|
|
5
|
+
const BASENAME = process.env.SSR_BASENAME || "/";
|
|
6
|
+
const PUBLIC_DIR = process.env.SSR_PUBLIC_DIR || "client";
|
|
7
|
+
|
|
8
|
+
const { HOST = "127.0.0.1", PORT = "3000" } = loadEnv({
|
|
9
|
+
envPrefix: ["SERVER_"],
|
|
10
|
+
removeEnvPrefix: true,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
// APP
|
|
14
|
+
const app = express();
|
|
15
|
+
if (BASENAME !== "/") {
|
|
16
|
+
app.use((req, res, next) => {
|
|
17
|
+
if (req.url === "/") {
|
|
18
|
+
res.redirect(BASENAME);
|
|
19
|
+
} else {
|
|
20
|
+
next();
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// SSR
|
|
26
|
+
const route = express.Router();
|
|
27
|
+
route.use(express.static(PUBLIC_DIR));
|
|
28
|
+
route.use(handler);
|
|
29
|
+
|
|
30
|
+
// SITE
|
|
31
|
+
app.use(BASENAME, route);
|
|
32
|
+
|
|
33
|
+
app.use((_, res) => {
|
|
34
|
+
res.status(404).send("Not Found");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
app.use((err, _, res) => {
|
|
38
|
+
console.error(err);
|
|
39
|
+
res.status(500).send("Internal Server Error");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// RUN
|
|
43
|
+
app
|
|
44
|
+
.listen(PORT, HOST, () => {
|
|
45
|
+
console.log(`Server is listening on http://${HOST}:${PORT}`);
|
|
46
|
+
})
|
|
47
|
+
.on("error", (error) => {
|
|
48
|
+
console.error("Server error:", error);
|
|
49
|
+
});
|