vite-plugin-ssr-config 1.0.3 → 1.0.4

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 CHANGED
@@ -16,18 +16,19 @@ For more detailed information and resources related to `vite-plugin-ssr-config`,
16
16
  To add this plugin to your project, run the following commands:
17
17
 
18
18
  ```bash
19
- yarn add vite-plugin-ssr-config vite-plugin-pages react-router-dom -D
19
+ yarn add vite-plugin-ssr-config vite-plugin-web-routes -D
20
+ ```
21
+ ```bash
22
+ yarn add react-query react-router express
20
23
  ```
21
24
 
22
25
  This will install:
23
26
 
24
27
  - vite-plugin-ssr-config: The plugin for server-side rendering (SSR) with Vite.
25
- - vite-plugin-pages: Automatically generate route files for your pages.
26
- - react-router-dom: The routing library for React, used to manage navigation within the app.
28
+ - vite-plugin-web-routes: Automatically generate route files for your pages.
29
+ - react-query: Delegate Hydrated State
30
+ - react-router: The routing library for React, used to manage navigation within the app.
27
31
 
28
- ```bash
29
- yarn add react-router-dom
30
- ```
31
32
 
32
33
  ## Basic Configuration Example
33
34
 
@@ -36,11 +37,19 @@ To use the plugin, you need to integrate it with Vite’s `defineConfig` method
36
37
  ```typescript
37
38
  import { defineConfig } from "vite";
38
39
  import react from "@vitejs/plugin-react";
39
- import pages from "vite-plugin-pages";
40
+ import web from "vite-plugin-web-routes";
40
41
  import ssr from "vite-plugin-ssr-config";
41
42
 
42
43
  export default defineConfig({
43
- plugins: [react(), pages(), ssr()],
44
+ plugins: [
45
+ react(),
46
+ web({
47
+ moduleFile: '.ssr/routes.tsx'
48
+ }),
49
+ ssr({
50
+ chacheDir: '.ssr'
51
+ })
52
+ ],
44
53
  });
45
54
  ```
46
55
 
@@ -49,15 +58,6 @@ export default defineConfig({
49
58
  The following default values are provided for each configurable attribute in the plugin:
50
59
 
51
60
  ```typescript
52
- import { defineConfig } from "vite";
53
- import react from "@vitejs/plugin-react";
54
- import pages from "vite-plugin-pages";
55
- import ssr from "vite-plugin-ssr-config";
56
-
57
- export default defineConfig({
58
- plugins: [
59
- react(),
60
- pages(),
61
61
  ssr({
62
62
  root: process.cwd(), // Root directory, typically the project root.
63
63
 
@@ -92,9 +92,7 @@ export default defineConfig({
92
92
  // Config callbacks
93
93
  clientBuild: (config: UserConfig) => config, // Client-side Vite configuration.
94
94
  serverBuild: (config: UserConfig) => config, // Server-side Vite configuration.
95
- }),
96
- ],
97
- });
95
+ })
98
96
  ```
99
97
 
100
98
  > Important Note: PageServer uses `suspense: true` in all requests to ensure proper SSR rendering. On the other hand, PageBrowser uses `suspense: false` to allow smooth client-side navigation. This setup guarantees correct SSR rendering while preventing flickering and inconsistencies between the server-rendered content and the client-side state during hydration.
package/dist/index.cjs CHANGED
@@ -61,7 +61,7 @@ var assertSSRConfig = (ssrOpts = {}) => {
61
61
  serverOutDir = "dist/",
62
62
  serverMinify = false,
63
63
  serverBuild = (config) => config,
64
- clientOutDir = "dist/client",
64
+ clientOutDir = "dist/public",
65
65
  clientMinify = true,
66
66
  clientBuild = (config) => config
67
67
  } = ssrOpts;
@@ -140,9 +140,22 @@ var copyFilesDirectory = (origin, target, {
140
140
  }
141
141
  });
142
142
  };
143
+ var readManifest = (dir) => {
144
+ const candidates = [
145
+ import_path.default.resolve(dir, ".vite/manifest.json"),
146
+ import_path.default.resolve(dir, "manifest.json")
147
+ ];
148
+ const manifestFile = candidates.find((file) => import_fs_extra.default.existsSync(file));
149
+ if (!manifestFile) {
150
+ throw new Error(
151
+ `manifest.json not found in:
152
+ ${candidates.join("\n")}`
153
+ );
154
+ }
155
+ return import_fs_extra.default.readJsonSync(manifestFile);
156
+ };
143
157
 
144
158
  // src/plugin-build/build.ts
145
- var import_fs_extra2 = __toESM(require("fs-extra"), 1);
146
159
  var import_path2 = __toESM(require("path"), 1);
147
160
  var import_vite = require("vite");
148
161
  var doBuildServer = async (ssrConfig2, viteConfig) => {
@@ -167,9 +180,7 @@ var doBuildServer = async (ssrConfig2, viteConfig) => {
167
180
  const { base = "/" } = viteConfig;
168
181
  const ssrServerFile = import_path2.default.resolve(root, server);
169
182
  const ssrPublicDir = import_path2.default.relative(serverOutDir, clientOutDir);
170
- const manifestFile = import_path2.default.resolve(`${clientOutDir}/manifest.json`);
171
- const manifestContent = import_fs_extra2.default.readFileSync(manifestFile, "utf-8");
172
- const manifest = JSON.parse(manifestContent);
183
+ const manifest = readManifest(clientOutDir);
173
184
  const manifestOut = manifest[entryClient].file;
174
185
  const ssrEntryClientURL = finalUrl(base, manifestOut);
175
186
  const ssrFiles = [
@@ -258,7 +269,6 @@ var doBuildClient = async (ssrConfig2, viteConfig) => {
258
269
  write: true,
259
270
  manifest: true,
260
271
  minify: clientMinify,
261
- target: "modules",
262
272
  emptyOutDir: false,
263
273
  outDir: clientOutDir,
264
274
  rollupOptions: {
@@ -402,7 +412,7 @@ var pluginResolve = (ssrConfig2) => {
402
412
  };
403
413
 
404
414
  // src/plugin-serve/index.ts
405
- var import_fs_extra3 = __toESM(require("fs-extra"), 1);
415
+ var import_fs_extra2 = __toESM(require("fs-extra"), 1);
406
416
  var import_path4 = __toESM(require("path"), 1);
407
417
  var pluginServe = (ssrConfig2) => {
408
418
  const { entryClient, root } = ssrConfig2;
@@ -424,10 +434,10 @@ var pluginServe = (ssrConfig2) => {
424
434
  return async () => {
425
435
  devServer.middlewares.use(async (req, res, next) => {
426
436
  const indexHtmlPath = import_path4.default.join(root, req.url, "index.html");
427
- if (import_fs_extra3.default.existsSync(indexHtmlPath)) {
437
+ if (import_fs_extra2.default.existsSync(indexHtmlPath)) {
428
438
  return devServer.transformIndexHtml(
429
439
  req.url,
430
- import_fs_extra3.default.readFileSync(indexHtmlPath, "utf-8")
440
+ import_fs_extra2.default.readFileSync(indexHtmlPath, "utf-8")
431
441
  ).then((html) => {
432
442
  res.setHeader("Content-Type", "text/html");
433
443
  res.setHeader("Pragma", "no-cache");
package/dist/index.js CHANGED
@@ -25,7 +25,7 @@ var assertSSRConfig = (ssrOpts = {}) => {
25
25
  serverOutDir = "dist/",
26
26
  serverMinify = false,
27
27
  serverBuild = (config) => config,
28
- clientOutDir = "dist/client",
28
+ clientOutDir = "dist/public",
29
29
  clientMinify = true,
30
30
  clientBuild = (config) => config
31
31
  } = ssrOpts;
@@ -103,9 +103,22 @@ var copyFilesDirectory = (origin, target, {
103
103
  }
104
104
  });
105
105
  };
106
+ var readManifest = (dir) => {
107
+ const candidates = [
108
+ path.resolve(dir, ".vite/manifest.json"),
109
+ path.resolve(dir, "manifest.json")
110
+ ];
111
+ const manifestFile = candidates.find((file) => fs.existsSync(file));
112
+ if (!manifestFile) {
113
+ throw new Error(
114
+ `manifest.json not found in:
115
+ ${candidates.join("\n")}`
116
+ );
117
+ }
118
+ return fs.readJsonSync(manifestFile);
119
+ };
106
120
 
107
121
  // src/plugin-build/build.ts
108
- import fs2 from "fs-extra";
109
122
  import path2 from "path";
110
123
  import { build, mergeConfig } from "vite";
111
124
  var doBuildServer = async (ssrConfig2, viteConfig) => {
@@ -130,9 +143,7 @@ var doBuildServer = async (ssrConfig2, viteConfig) => {
130
143
  const { base = "/" } = viteConfig;
131
144
  const ssrServerFile = path2.resolve(root, server);
132
145
  const ssrPublicDir = path2.relative(serverOutDir, clientOutDir);
133
- const manifestFile = path2.resolve(`${clientOutDir}/manifest.json`);
134
- const manifestContent = fs2.readFileSync(manifestFile, "utf-8");
135
- const manifest = JSON.parse(manifestContent);
146
+ const manifest = readManifest(clientOutDir);
136
147
  const manifestOut = manifest[entryClient].file;
137
148
  const ssrEntryClientURL = finalUrl(base, manifestOut);
138
149
  const ssrFiles = [
@@ -221,7 +232,6 @@ var doBuildClient = async (ssrConfig2, viteConfig) => {
221
232
  write: true,
222
233
  manifest: true,
223
234
  minify: clientMinify,
224
- target: "modules",
225
235
  emptyOutDir: false,
226
236
  outDir: clientOutDir,
227
237
  rollupOptions: {
@@ -365,7 +375,7 @@ var pluginResolve = (ssrConfig2) => {
365
375
  };
366
376
 
367
377
  // src/plugin-serve/index.ts
368
- import fs3 from "fs-extra";
378
+ import fs2 from "fs-extra";
369
379
  import path4 from "path";
370
380
  var pluginServe = (ssrConfig2) => {
371
381
  const { entryClient, root } = ssrConfig2;
@@ -387,10 +397,10 @@ var pluginServe = (ssrConfig2) => {
387
397
  return async () => {
388
398
  devServer.middlewares.use(async (req, res, next) => {
389
399
  const indexHtmlPath = path4.join(root, req.url, "index.html");
390
- if (fs3.existsSync(indexHtmlPath)) {
400
+ if (fs2.existsSync(indexHtmlPath)) {
391
401
  return devServer.transformIndexHtml(
392
402
  req.url,
393
- fs3.readFileSync(indexHtmlPath, "utf-8")
403
+ fs2.readFileSync(indexHtmlPath, "utf-8")
394
404
  ).then((html) => {
395
405
  res.setHeader("Content-Type", "text/html");
396
406
  res.setHeader("Pragma", "no-cache");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-ssr-config",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "license": "MIT",
5
5
  "description": "A powerful Vite plugin designed to enable Server-Side Rendering (SSR) for React applications. It provides a comprehensive solution for bundling both SSR and CSR, with built-in support for React Router and React Query for efficient API handling and page rendering.",
6
6
  "keywords": [
@@ -40,10 +40,14 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "dotenv-local": "^1.0.2",
43
- "fs-extra": "^11.2.0"
43
+ "fs-extra": "^11.2.0",
44
+ "react": "^19.2.7",
45
+ "react-dom": "^19.2.7",
46
+ "react-router": "^7.17.0",
47
+ "react-router-dom": "^7.17.0"
44
48
  },
45
49
  "peerDependencies": {
46
- "vite": "^2 || ^3 || ^4 || ^5 || ^6"
50
+ "vite": ">=4.0.0"
47
51
  },
48
52
  "devDependencies": {
49
53
  "@types/express": "^5.0.0",
@@ -1,25 +1,25 @@
1
- import { PageBrowser } from "@ssr/pageBrowser.jsx";
2
- import { startTransition, StrictMode } from "react";
3
- import { hydrateRoot } from "react-dom/client";
4
-
5
- startTransition(() => {
6
- const hydratedState = JSON.parse(atob(window.__HYDRATED_STATE__));
7
- const setHydratedState = () => {
8
- throw Error("Changes Not Allowed");
9
- };
10
- hydrateRoot(
11
- document,
12
- <StrictMode>
13
- <PageBrowser
14
- hydratedState={hydratedState}
15
- setHydratedState={setHydratedState}
16
- />
17
- </StrictMode>,
18
- {
19
- onRecoverableError: (error, { componentStack }) => {
20
- const logs = componentStack?.split("\n");
21
- console.log("Error:", error, logs);
22
- },
23
- }
24
- );
25
- });
1
+ import { PageBrowser } from "@ssr/pageBrowser.jsx";
2
+ import { startTransition, StrictMode } from "react";
3
+ import { hydrateRoot } from "react-dom/client";
4
+
5
+ startTransition(() => {
6
+ const hydratedState = JSON.parse(atob(window.__HYDRATED_STATE__));
7
+ const setHydratedState = () => {
8
+ throw Error("Changes Not Allowed");
9
+ };
10
+ hydrateRoot(
11
+ document,
12
+ <StrictMode>
13
+ <PageBrowser
14
+ hydratedState={hydratedState}
15
+ setHydratedState={setHydratedState}
16
+ />
17
+ </StrictMode>,
18
+ {
19
+ onRecoverableError: (error, { componentStack }) => {
20
+ const logs = componentStack?.split("\n");
21
+ console.log("Error:", error, logs);
22
+ },
23
+ }
24
+ );
25
+ });
@@ -1,18 +1,28 @@
1
1
  import { PageServer } from "@ssr/pageServer.jsx";
2
2
  import { StrictMode } from "react";
3
+ import { Transform } from "stream";
3
4
  import { renderToPipeableStream } from "react-dom/server";
4
5
 
5
- const renderDefault = async (request, response, next) => {
6
- let hydratedState = {};
7
- const setHydratedState = (state) => {
8
- hydratedState = state;
6
+ const createRequestContext = () => {
7
+ return {
8
+ state: {},
9
+ setHydratedState(partial) {
10
+ this.state = {
11
+ ...this.state,
12
+ ...partial,
13
+ };
14
+ },
9
15
  };
16
+ };
17
+
18
+ const renderDefault = async (request, response, next) => {
19
+ const context = createRequestContext();
10
20
  const { pipe } = renderToPipeableStream(
11
21
  <StrictMode>
12
22
  <PageServer
13
23
  path={request.originalUrl}
14
24
  hydratedState={""} // Access is Not Allowed
15
- setHydratedState={setHydratedState}
25
+ setHydratedState={context.setHydratedState.bind(context)}
16
26
  />
17
27
  </StrictMode>,
18
28
  {
@@ -22,13 +32,30 @@ const renderDefault = async (request, response, next) => {
22
32
  },
23
33
  onAllReady: () => {
24
34
  // console.log(request.originalUrl, "onAllReady");
25
- response.setHeader("content-type", "text/html");
26
- pipe(response);
27
- response.write(
28
- `<script>window.__HYDRATED_STATE__ = "${btoa(
29
- JSON.stringify(hydratedState)
30
- )}";</script>`
31
- );
35
+ let injected = false;
36
+ const transform = new Transform({
37
+ transform(chunk, encoding, callback) {
38
+ if (!injected) {
39
+ const stateScript = `<script>window.__HYDRATED_STATE__ = "${btoa(JSON.stringify(context.state))}";</script>`;
40
+ const str = chunk.toString();
41
+ const idx = str.lastIndexOf("</head>");
42
+ if (idx !== -1) {
43
+ injected = true;
44
+ const out = str.slice(0, idx) + stateScript + str.slice(idx);
45
+ callback(null, out);
46
+ return;
47
+ }
48
+ }
49
+ callback(null, chunk);
50
+ },
51
+ });
52
+ try {
53
+ response.setHeader("content-type", "text/html");
54
+ } catch (error) {
55
+ console.error("Set-Header Context-Type text/html Error:", error);
56
+ }
57
+ transform.pipe(response);
58
+ pipe(transform);
32
59
  },
33
60
  onShellError: (error) => {
34
61
  // console.log(request.originalUrl, "onShellError", error);
@@ -37,7 +64,7 @@ const renderDefault = async (request, response, next) => {
37
64
  // console.log(request.originalUrl, "onError", error, errorInfo);
38
65
  next(error);
39
66
  },
40
- }
67
+ },
41
68
  );
42
69
  };
43
70
 
@@ -2,7 +2,7 @@ import { ErrorBoundary } from "@ssr/errorBoundary.jsx";
2
2
  import { RootRoutes } from "@ssr/rootRoutes.jsx";
3
3
  import { StrictMode, Suspense } from "react";
4
4
  import { QueryClient, QueryClientProvider, hydrate } from "react-query";
5
- import { BrowserRouter } from "react-router-dom";
5
+ import { BrowserRouter } from "react-router";
6
6
 
7
7
  const queryClient = new QueryClient({
8
8
  defaultOptions: {
@@ -2,7 +2,7 @@ import { ErrorBoundary } from "@ssr/errorBoundary.jsx";
2
2
  import { RootRoutes } from "@ssr/rootRoutes.jsx";
3
3
  import { StrictMode, Suspense } from "react";
4
4
  import { dehydrate, QueryClient, QueryClientProvider } from "react-query";
5
- import { StaticRouter } from "react-router-dom/server";
5
+ import { StaticRouter } from "react-router";
6
6
 
7
7
  export const PageServer = ({ path, hydratedState, setHydratedState }) => {
8
8
  const queryServer = new QueryClient({
package/ssr/root.jsx CHANGED
@@ -1,6 +1,6 @@
1
1
  import { LiveReload } from "@ssr/liveReload.jsx";
2
2
  import { ViteScripts } from "@ssr/viteScripts.jsx";
3
- import { Outlet } from "react-router-dom";
3
+ import { Outlet } from "react-router";
4
4
 
5
5
  export const RootDocument = () => {
6
6
  return (
@@ -1,7 +1,7 @@
1
1
  import { RootDocument } from "@ssr/root.jsx";
2
2
  import React from "react";
3
- import { useRoutes } from "react-router-dom";
4
- import routes from "~react-pages";
3
+ import { useRoutes } from "react-router";
4
+ import routes from "./routes";
5
5
 
6
6
  export const RootRoutes = (props) => {
7
7
  const newRoutes = [