vite-plugin-react-server 0.3.18 → 0.3.19

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.
Files changed (208) hide show
  1. package/README.md +119 -118
  2. package/bin/patch.mjs +8 -2
  3. package/dist/package.json +3 -3
  4. package/dist/plugin/checkFilesExist.d.ts.map +1 -1
  5. package/dist/plugin/checkFilesExist.js +6 -2
  6. package/dist/plugin/checkFilesExist.js.map +1 -1
  7. package/dist/plugin/collect-manifest-client-files.d.ts +23 -0
  8. package/dist/plugin/collect-manifest-client-files.d.ts.map +1 -0
  9. package/dist/plugin/collect-manifest-client-files.js +117 -0
  10. package/dist/plugin/collect-manifest-client-files.js.map +1 -0
  11. package/dist/plugin/components.d.ts +9 -9
  12. package/dist/plugin/components.d.ts.map +1 -1
  13. package/dist/plugin/components.js +50 -9
  14. package/dist/plugin/components.js.map +1 -0
  15. package/dist/plugin/config/defaults.d.ts +7 -6
  16. package/dist/plugin/config/defaults.d.ts.map +1 -1
  17. package/dist/plugin/config/defaults.js +8 -5
  18. package/dist/plugin/config/defaults.js.map +1 -1
  19. package/dist/plugin/config/getPaths.d.ts +0 -1
  20. package/dist/plugin/config/getPaths.d.ts.map +1 -1
  21. package/dist/plugin/config/getPaths.js +2 -7
  22. package/dist/plugin/config/getPaths.js.map +1 -1
  23. package/dist/plugin/config/mimeTypes.d.ts +2 -0
  24. package/dist/plugin/config/mimeTypes.d.ts.map +1 -0
  25. package/dist/plugin/config/mimeTypes.js +24 -0
  26. package/dist/plugin/config/mimeTypes.js.map +1 -0
  27. package/dist/plugin/config/resolveOptions.d.ts +1 -1
  28. package/dist/plugin/config/resolveOptions.d.ts.map +1 -1
  29. package/dist/plugin/config/resolveOptions.js +41 -28
  30. package/dist/plugin/config/resolveOptions.js.map +1 -1
  31. package/dist/plugin/config/resolvePages.d.ts +1 -0
  32. package/dist/plugin/config/resolvePages.d.ts.map +1 -1
  33. package/dist/plugin/config/resolvePages.js +9 -5
  34. package/dist/plugin/config/resolvePages.js.map +1 -1
  35. package/dist/plugin/config/resolveUserConfig.d.ts +2 -1
  36. package/dist/plugin/config/resolveUserConfig.d.ts.map +1 -1
  37. package/dist/plugin/config/resolveUserConfig.js +10 -5
  38. package/dist/plugin/config/resolveUserConfig.js.map +1 -1
  39. package/dist/plugin/copy-dir.js +23 -18
  40. package/dist/plugin/copy-dir.js.map +1 -0
  41. package/dist/plugin/helpers/createHandler.d.ts +22 -0
  42. package/dist/plugin/helpers/createHandler.d.ts.map +1 -0
  43. package/dist/plugin/{react-server → helpers}/createHandler.js +36 -48
  44. package/dist/plugin/helpers/createHandler.js.map +1 -0
  45. package/dist/plugin/{react-server → helpers}/createRscStream.d.ts +2 -1
  46. package/dist/plugin/helpers/createRscStream.d.ts.map +1 -0
  47. package/dist/plugin/helpers/createRscStream.js +71 -0
  48. package/dist/plugin/helpers/createRscStream.js.map +1 -0
  49. package/dist/plugin/helpers/getBundleManifest.d.ts.map +1 -1
  50. package/dist/plugin/helpers/getBundleManifest.js +12 -4
  51. package/dist/plugin/helpers/getBundleManifest.js.map +1 -1
  52. package/dist/plugin/loader/createBuildLoader.d.ts +1 -1
  53. package/dist/plugin/loader/createBuildLoader.d.ts.map +1 -1
  54. package/dist/plugin/loader/createBuildLoader.js +8 -5
  55. package/dist/plugin/loader/createBuildLoader.js.map +1 -1
  56. package/dist/plugin/loader/css-loader.d.ts.map +1 -1
  57. package/dist/plugin/loader/css-loader.js.map +1 -1
  58. package/dist/plugin/loader/react-loader.js +2 -2
  59. package/dist/plugin/loader/react-loader.js.map +1 -1
  60. package/dist/plugin/preserver/plugin.d.ts.map +1 -1
  61. package/dist/plugin/preserver/plugin.js +49 -14
  62. package/dist/plugin/preserver/plugin.js.map +1 -1
  63. package/dist/plugin/react-client/plugin.d.ts.map +1 -1
  64. package/dist/plugin/react-client/plugin.js +18 -76
  65. package/dist/plugin/react-client/plugin.js.map +1 -1
  66. package/dist/plugin/react-server/index.d.ts.map +1 -1
  67. package/dist/plugin/react-server/index.js +2 -0
  68. package/dist/plugin/react-server/index.js.map +1 -1
  69. package/dist/plugin/react-server/plugin.d.ts +2 -1
  70. package/dist/plugin/react-server/plugin.d.ts.map +1 -1
  71. package/dist/plugin/react-server/plugin.js +53 -217
  72. package/dist/plugin/react-server/plugin.js.map +1 -1
  73. package/dist/plugin/react-static/index.d.ts +2 -0
  74. package/dist/plugin/react-static/index.d.ts.map +1 -0
  75. package/dist/plugin/react-static/index.js +1 -0
  76. package/dist/plugin/react-static/plugin.d.ts +7 -0
  77. package/dist/plugin/react-static/plugin.d.ts.map +1 -0
  78. package/dist/plugin/react-static/plugin.js +199 -0
  79. package/dist/plugin/react-static/plugin.js.map +1 -0
  80. package/dist/plugin/resolvePage.d.ts.map +1 -1
  81. package/dist/plugin/resolvePage.js +9 -0
  82. package/dist/plugin/resolvePage.js.map +1 -1
  83. package/dist/plugin/root.d.ts +2 -0
  84. package/dist/plugin/root.d.ts.map +1 -0
  85. package/dist/plugin/root.js +12 -0
  86. package/dist/plugin/root.js.map +1 -0
  87. package/dist/plugin/transformer/plugin.d.ts.map +1 -1
  88. package/dist/plugin/transformer/plugin.js +32 -23
  89. package/dist/plugin/transformer/plugin.js.map +1 -1
  90. package/dist/plugin/transformer/types.d.ts +1 -18
  91. package/dist/plugin/transformer/types.d.ts.map +1 -1
  92. package/dist/plugin/types.d.ts +24 -6
  93. package/dist/plugin/types.d.ts.map +1 -1
  94. package/dist/plugin/worker/createWorker.js +0 -1
  95. package/dist/plugin/worker/createWorker.js.map +1 -1
  96. package/dist/plugin/worker/html/html-worker.development.d.ts +30 -0
  97. package/dist/plugin/worker/html/html-worker.development.d.ts.map +1 -1
  98. package/dist/plugin/worker/html/html-worker.development.js +30 -2
  99. package/dist/plugin/worker/html/html-worker.development.js.map +1 -1
  100. package/dist/plugin/worker/html/html-worker.production.js +3 -5
  101. package/dist/plugin/worker/html/html-worker.production.js.map +1 -1
  102. package/dist/plugin/worker/html/messageHandler.d.ts.map +1 -1
  103. package/dist/plugin/worker/html/messageHandler.js +8 -2
  104. package/dist/plugin/worker/html/messageHandler.js.map +1 -1
  105. package/dist/plugin/worker/html/plugin.d.ts.map +1 -1
  106. package/dist/plugin/worker/html/plugin.js +2 -3
  107. package/dist/plugin/worker/html/renderPages.d.ts +8 -4
  108. package/dist/plugin/worker/html/renderPages.d.ts.map +1 -1
  109. package/dist/plugin/worker/html/renderPages.js +118 -83
  110. package/dist/plugin/worker/html/renderPages.js.map +1 -1
  111. package/dist/plugin/worker/rsc/messageHandler.d.ts.map +1 -1
  112. package/dist/plugin/worker/rsc/messageHandler.js +89 -84
  113. package/dist/plugin/worker/rsc/messageHandler.js.map +1 -1
  114. package/dist/plugin/worker/rsc/plugin.d.ts.map +1 -1
  115. package/dist/plugin/worker/rsc/plugin.js +1 -2
  116. package/dist/plugin/worker/rsc/rsc-worker.development.js +13 -18
  117. package/dist/plugin/worker/rsc/rsc-worker.development.js.map +1 -1
  118. package/dist/plugin/worker/rsc/rsc-worker.production.js +4 -1
  119. package/dist/plugin/worker/rsc/rsc-worker.production.js.map +1 -1
  120. package/dist/plugin/worker/rsc/state.d.ts.map +1 -1
  121. package/dist/plugin/worker/rsc/state.js.map +1 -1
  122. package/dist/tsconfig.tsbuildinfo +1 -1
  123. package/package.json +3 -3
  124. package/plugin/checkFilesExist.ts +7 -3
  125. package/plugin/collect-manifest-client-files.ts +152 -0
  126. package/plugin/components.tsx +55 -10
  127. package/plugin/config/defaults.tsx +69 -0
  128. package/plugin/config/getPaths.ts +1 -7
  129. package/plugin/config/mimeTypes.ts +17 -0
  130. package/plugin/config/resolveOptions.ts +48 -40
  131. package/plugin/config/resolvePages.ts +8 -4
  132. package/plugin/config/resolveUserConfig.ts +12 -9
  133. package/plugin/{react-server → helpers}/createHandler.ts +46 -63
  134. package/plugin/helpers/createRscStream.ts +81 -0
  135. package/plugin/helpers/getBundleManifest.ts +14 -5
  136. package/plugin/loader/createBuildLoader.ts +9 -6
  137. package/plugin/loader/css-loader.ts +0 -2
  138. package/plugin/loader/react-loader.ts +2 -2
  139. package/plugin/preserver/plugin.ts +64 -17
  140. package/plugin/react-client/plugin.ts +20 -91
  141. package/plugin/react-server/index.ts +2 -0
  142. package/plugin/react-server/plugin.ts +66 -293
  143. package/plugin/react-static/index.ts +1 -0
  144. package/plugin/react-static/plugin.ts +247 -0
  145. package/plugin/resolvePage.ts +9 -0
  146. package/plugin/root.ts +4 -0
  147. package/plugin/transformer/plugin.ts +40 -31
  148. package/plugin/transformer/types.ts +0 -19
  149. package/plugin/types.ts +25 -6
  150. package/plugin/worker/createWorker.ts +1 -1
  151. package/plugin/worker/html/README.md +63 -0
  152. package/plugin/worker/html/html-worker.development.tsx +89 -2
  153. package/plugin/worker/html/html-worker.production.tsx +8 -10
  154. package/plugin/worker/html/messageHandler.ts +8 -2
  155. package/plugin/worker/html/plugin.ts +2 -3
  156. package/plugin/worker/html/renderPages.ts +150 -114
  157. package/plugin/worker/rsc/README.md +58 -0
  158. package/plugin/worker/rsc/messageHandler.tsx +95 -111
  159. package/plugin/worker/rsc/plugin.ts +1 -2
  160. package/plugin/worker/rsc/rsc-worker.development.ts +12 -22
  161. package/plugin/worker/rsc/rsc-worker.production.ts +5 -1
  162. package/plugin/worker/rsc/state.ts +0 -3
  163. package/scripts/react+0.0.0-experimental-eda36a1c-20250228.patch +114 -12
  164. package/scripts/react-dom+0.0.0-experimental-eda36a1c-20250228.patch +10571 -121
  165. package/tsconfig.json +2 -2
  166. package/dist/plugin/collect-css-manifest.d.ts +0 -4
  167. package/dist/plugin/collect-css-manifest.d.ts.map +0 -1
  168. package/dist/plugin/collect-css-manifest.js +0 -65
  169. package/dist/plugin/collect-css-manifest.js.map +0 -1
  170. package/dist/plugin/config/createModuleIdGenerator.d.ts +0 -11
  171. package/dist/plugin/config/createModuleIdGenerator.d.ts.map +0 -1
  172. package/dist/plugin/config/createModuleIdGenerator.js +0 -44
  173. package/dist/plugin/config/createModuleIdGenerator.js.map +0 -1
  174. package/dist/plugin/loader/createCssLoader.d.ts +0 -30
  175. package/dist/plugin/loader/createCssLoader.d.ts.map +0 -1
  176. package/dist/plugin/loader/createCssLoader.js +0 -35
  177. package/dist/plugin/loader/createPageLoader.d.ts +0 -24
  178. package/dist/plugin/loader/createPageLoader.d.ts.map +0 -1
  179. package/dist/plugin/loader/createPageLoader.js +0 -50
  180. package/dist/plugin/loader/rsc/messageHandler.d.ts +0 -2
  181. package/dist/plugin/loader/rsc/messageHandler.d.ts.map +0 -1
  182. package/dist/plugin/loader/rsc/messageHandler.js +0 -1
  183. package/dist/plugin/loader/rsc/rsc-worker.development.d.ts +0 -2
  184. package/dist/plugin/loader/rsc/rsc-worker.development.d.ts.map +0 -1
  185. package/dist/plugin/loader/rsc/rsc-worker.development.js +0 -1
  186. package/dist/plugin/react-server/createHandler.d.ts +0 -17
  187. package/dist/plugin/react-server/createHandler.d.ts.map +0 -1
  188. package/dist/plugin/react-server/createHandler.js.map +0 -1
  189. package/dist/plugin/react-server/createRscStream.d.ts.map +0 -1
  190. package/dist/plugin/react-server/createRscStream.js +0 -70
  191. package/dist/plugin/react-server/createRscStream.js.map +0 -1
  192. package/dist/plugin/react-server/createSsrHandler.d.ts +0 -4
  193. package/dist/plugin/react-server/createSsrHandler.d.ts.map +0 -1
  194. package/dist/plugin/react-server/createSsrHandler.js +0 -95
  195. package/dist/plugin/utils/logger.d.ts +0 -9
  196. package/dist/plugin/utils/logger.d.ts.map +0 -1
  197. package/dist/plugin/utils/logger.js +0 -68
  198. package/dist/plugin/utils/logger.js.map +0 -1
  199. package/plugin/collect-css-manifest.ts +0 -82
  200. package/plugin/config/createModuleIdGenerator.ts +0 -52
  201. package/plugin/config/defaults.ts +0 -51
  202. package/plugin/loader/createCssLoader.ts +0 -73
  203. package/plugin/loader/createPageLoader.ts +0 -103
  204. package/plugin/loader/rsc/messageHandler.tsx +0 -1
  205. package/plugin/loader/rsc/rsc-worker.development.ts +0 -1
  206. package/plugin/react-server/createRscStream.ts +0 -86
  207. package/plugin/react-server/createSsrHandler.ts +0 -125
  208. package/plugin/utils/logger.ts +0 -52
package/README.md CHANGED
@@ -1,107 +1,103 @@
1
- # Vite React Server Components Plugin
1
+ # Vite React Server Plugin
2
2
 
3
3
  A Vite plugin that enables React Server Components (RSC) streaming and static building of html pages. Uses experimental dependencies from React, specifically `react-server-dom-esm`.
4
4
 
5
- ## Features
6
5
 
7
- - 🚀 Super fast static build times
8
- - 🔄 Use vite to create your personal meta-framework
9
- - ⚡ Full RSC streaming support
10
- - 📦 Dual-worker architecture for optimal performance
11
- - 🔧 Automatic client/server code splitting
12
- - 🎯 Directive-based component targeting
6
+ ## Example Projects
13
7
 
14
- ## Example Project
15
-
16
- The [mmcelebration.com project](https://github.com/nicobrinkkemper/mmc) uses this plugin. Build time for ~200 html pages is just a few seconds.
8
+ - [The official demo](https://github.com/nicobrinkkemper/vite-plugin-react-server-demo-official)
9
+ - [The mmcelebration.com project](https://github.com/nicobrinkkemper/mmc)
17
10
 
18
11
  ## Installation
19
12
 
20
- ```bash
21
- npm install vite-plugin-react-server
13
+ ```ts
14
+ npm install -D vite-plugin-react-stream
22
15
  ```
23
16
 
24
- ### React Canary Setup
25
-
26
- This plugin requires React's experimental features. You'll need to:
17
+ ## Open source and work in progress
27
18
 
28
- 1. Install react-server-dom-esm (currently an empty stub package):
29
- ```bash
30
- npm install react-server-dom-esm
19
+ This project uses the latest* oss-experimental React version taken from [the offical React github repository](https://github.com/facebook/react). This plugin offers a patch system that can get you up and running quickly. First run `npm install -D patch-package react@experimental react-dom@experimental react-server-dom-esm` and add the follow command to the scripts
20
+ ```json
21
+ "patch": "check-react-version && patch",
31
22
  ```
32
-
33
- 2. Apply the patch:
34
- ```bash
35
- npx vite-plugin-react-server/patch
23
+ Now run `npm run patch` to create the patch. It will tell you to add this as well:
24
+ ```json
25
+ "postinstall": "patch-package",
36
26
  ```
37
- This will:
38
- - Detect your installed React version
39
- - Adapt the patch accordingly
40
- - Create `patches/react-server-dom-esm+YOUR-REACT-VERSION.patch`
41
-
42
- 3. Install patch-package:
43
- ```bash
44
- npm install patch-package --save-dev
27
+ This will apply the patch for us after running `npm install`
28
+
29
+ If errors start showing up related to the missing `react-server-dom-esm` package - it's likely the postinstall step didn't run.
30
+
31
+ ## Included plugins
32
+ ### vite-plugin-react-server/client
33
+ - Client build
34
+ - Server Worker thread (rsc-worker)
35
+
36
+ ### vite-plugin-react-server
37
+ - Server build
38
+ - Client Worker thread (html-worker)
39
+
40
+ ### vite-plugin-react-server/preserver
41
+ - Preserves "use client" and "use server" condition in source code
42
+
43
+ ### vite-plugin-react-server/transformer
44
+ - Transforms client components for server environment or vice versa
45
+
46
+ ### vite-plugin-react-server/worker/html
47
+ - Create your own html worker (client side worker)
48
+ - Make html worker part of the application
49
+
50
+ ### vite-plugin-react-server/worker/rsc
51
+ - Create your own rsc worker (server side worker)
52
+ - Make rsc worker part of the application
53
+
54
+ ## Configuration
55
+ For client and server boundaries to work, it's very important to know which environment (or thread) the system is in. Let's setup the client first.
56
+ ```ts
57
+ import { vitePluginReactClient } from "vite-plugin-react-server/client";
58
+ import { config } from "./config.js";
59
+ import { defineConfig } from "vite";
60
+ export default defineConfig(()=> {
61
+ return {
62
+ plugins: vitePluginReactClient(config),
63
+ }
64
+ });
45
65
  ```
46
-
47
- 4. Add to package.json:
48
- ```json
49
- {
50
- "scripts": {
51
- "postinstall": "patch-package"
66
+ The client plugin can help to quickly setup a client side build that'll work with the server build. It also allows you to serve the application. To handle the rsc streams, it uses the rsc-worker. You can read more about the rsc-worker [here](/docs)
67
+
68
+ ```ts
69
+ import { vitePluginReactServer } from "vite-plugin-react-server";
70
+ import { config } from "./config.js";
71
+ import { defineConfig } from "vite";
72
+ export default defineConfig(()=> {
73
+ return {
74
+ plugins: vitePluginReactServer(config),
52
75
  }
53
- }
76
+ });
54
77
  ```
78
+ The server plugin will look the same when you serve it, but under the hood works quite differently. This plugin requires you to write `NODE_OPTIONS="--conditions=react-server" vite --ssr --config vite.server.config.ts`. Aside from building the server dist files, it will populate the client's folder with index files for all your routes.
55
79
 
56
- Alternative: You can build React from source and use `npm link` for react-server-dom-esm, react, and react-dom (though the patch system is recommended).
57
-
58
- ## Basic Configuration
59
-
60
- ```typescript
61
- // vite.react-server.config.ts
62
- import { defineConfig } from 'vite'
63
- import type { Options } from 'vite-plugin-react-server'
80
+ ```ts
81
+ import React from "react"
64
82
 
65
- // Custom router example
66
- const createRouter = (file: 'props.ts' | 'Page.tsx') => (url: string) => {
67
- if(url.includes('bidoof')) return `src/page/bidoof/${file}`
68
- if(url === '/index.rsc') return `src/page/${file}`;
69
- return `src/page/404/${file}`;
70
- }
71
-
72
- export const streamPluginOptions: Options = {
83
+ export const config = {
84
+ // set the root dir
73
85
  moduleBase: "src",
74
- Page: createRouter('Page.tsx'),
75
- props: createRouter('props.ts'),
76
- pageExportName: "Page",
77
- propsExportName: "props",
78
- }
79
- ```
80
-
81
- ### Client Build Config
82
-
83
- ```typescript
84
- // vite.config.ts
85
- import { defineConfig } from 'vite'
86
- import { viteReactStreamPlugin } from 'vite-plugin-react-server/client'
87
- import { streamPluginOptions } from './vite.react-server.config.js'
88
-
89
- export default defineConfig({
90
- plugins: [viteReactStreamPlugin(streamPluginOptions)]
91
- })
92
- ```
93
-
94
- ### Server Build Config
95
-
96
- ```typescript
97
- // vite.server.config.ts
98
- import { defineConfig } from 'vite'
99
- import { viteReactStreamPlugin } from 'vite-plugin-react-server/server'
100
- import { streamPluginOptions } from './vite.react-server.config.js'
101
-
102
- export default defineConfig({
103
- plugins: [viteReactStreamPlugin(streamPluginOptions)]
104
- })
86
+ // map the id to any page/prop file
87
+ Page: (id)=>'page.tsx'),
88
+ props: (id)=>'props.ts',
89
+ // production html (not used during development)
90
+ Html: ({ children, url }) => (
91
+ <html>
92
+ <title>{url}</title>
93
+ <body>{children}</body>
94
+ </html>
95
+ ),
96
+ build: {
97
+ // routes to build index.html and index.rsc files for
98
+ pages: ["/", "/about"],
99
+ },
100
+ };
105
101
  ```
106
102
 
107
103
  ### Scripts Setup
@@ -109,11 +105,13 @@ export default defineConfig({
109
105
  ```json
110
106
  {
111
107
  "scripts": {
112
- "start": "NODE_OPTIONS=--conditions=react-server vite",
108
+ "start": "vite",
109
+ "dev": "NODE_OPTIONS=--conditions=react-server vite --config vite.server.config.ts",
113
110
  "build": "npm run build:client && npm run build:server",
114
111
  "build:client": "vite build",
115
112
  "build:server": "NODE_OPTIONS=--conditions=react-server vite build --ssr --config vite.server.config.ts",
116
- "test:server": "NODE_OPTIONS=--conditions=react-server vitest --config vite.server.config.ts"
113
+ "test:server": "NODE_OPTIONS=--conditions=react-server vitest",
114
+ "test:client": "vitest",
117
115
  }
118
116
  }
119
117
  ```
@@ -165,49 +163,52 @@ export async function submitForm(data: FormData) {
165
163
  - Components are streamed only when visited
166
164
  - Supports both sync and async props, and all kinds of combinations I haven't tried or tested yet!
167
165
 
168
- ## License
166
+ ## Architecture and Implementation Details
169
167
 
170
- MIT
168
+ ### Dual Implementation Strategy
171
169
 
172
- ## FAQ
170
+ The plugin provides two complete implementations of RSC handling:
173
171
 
174
- ### Why does this plugin use two separate workers?
172
+ 1. **Direct In-Thread Implementation** (when running under react-server condition)
173
+ - Simpler architecture
174
+ - Better error reporting (same errors in console and browser)
175
+ - Easier to debug
176
+ - More direct stream handling
175
177
 
176
- The plugin uses a dual-worker architecture to handle the complex boundary between server and client code in React 19's new paradigm. This design enables four key scenarios:
178
+ 2. **Worker-Based Implementation** (when not under react-server condition)
179
+ - Allows running without react-server condition
180
+ - Uses message passing for communication
181
+ - Requires explicit support for features over message channels
182
+ - More complex but more flexible
177
183
 
178
- 1. **Pure Client** - Client-side React rendering
179
- 2. **Pure Server** - Server-side RSC streaming
180
- 3. **Client-with-server-worker** - Thread WITHOUT React server conditions using dependencies that are ONLY available using the server condition
181
- 4. **Server-with-client-worker** - Thread WITH React server conditions using dependencies that are ONLY available WITHOUT the server condition
184
+ This dual implementation approach gives users choice in how they want to structure their build process, recognizing that different projects may have different needs or constraints.
182
185
 
183
- This architecture is necessary because React 19 introduces a fundamental shift in how server/client boundaries work. While React was traditionally client/browser-oriented, version 19 moves much of the functionality to the server by default.
186
+ ### Node Conditions and Worker Threads
184
187
 
185
- ## When to use which directive?
188
+ The use of worker threads isn't primarily about parallelization - it's about handling Node conditions:
186
189
 
187
- The "use client" should be used for components that use client-only things, like hooks - browser window, localStorage, etc.
190
+ - The main Node process conditions (`NODE_OPTIONS`) are set at startup and can't be changed
191
+ - When running under `--conditions=react-server`, we need a way to handle client-side bundling
192
+ - Worker threads allow us to run code under different conditions than the main thread
193
+ - This makes worker threads essential for supporting both server and client code in the same build process
188
194
 
195
+ ### Future Possibilities: Application-Level Workers
189
196
 
190
- The "use server" directive is *not* for server components, it's for server actions. Every component is a server component by default. If the "use server" directive is present,
191
- this plugin will register it as a server action - which means you can intend the exported functions to be API endpoints.
197
+ While currently focused on build-time RSC handling, the worker pattern could be extended to the end application:
192
198
 
193
- ## What about .client.tsx files, .server.tsx files?
199
+ - Users could create their own RSC workers
200
+ - Run in client dev mode to naturally support their worker setup
201
+ - Get worker isolation benefits in their application
202
+ - Have more control over RSC boundaries
194
203
 
195
- Whenever you use the directive, it's recommended to also name the file with the .client.tsx or .server.tsx extension respectively.
196
- While this isn't required for the plugin to work - it's the only way to make sure all client and server files are included in the build -
197
- even if they are not a main entry point.
198
-
199
- ### What's the deal with NODE_OPTIONS and conditions?
200
-
201
- The React Server Components system requires specific Node conditions:
202
-
203
- - `NODE_OPTIONS='--conditions=react-server'` is required for RSC functionality (generating RSC streams)
204
- - This condition must NOT be present for client-side React operations (generating HTML from RSC streams)
205
-
206
- The plugin recommends using the react-server condition in the main thread to enable React server features from the start. When different conditions are needed (e.g., generating static HTML), the plugin uses workers with appropriate conditions.
207
-
208
- ### Why start with "use client"?
209
-
210
- With React 19's server-first approach, many existing ecosystem tools and patterns need the "use client" directive to work as they did before. It's becoming a common pattern to start with "use client" to maintain compatibility with existing code while gradually adopting the new server-first paradigm.
204
+ This would require:
205
+ - APIs for user-defined workers
206
+ - Documentation for worker patterns
207
+ - Examples of worker-based architectures
208
+ - Support for different worker strategies
211
209
 
210
+ While this isn't currently implemented, the architecture is designed to potentially support this kind of extension in the future.
212
211
 
212
+ ## License
213
213
 
214
+ MIT
package/bin/patch.mjs CHANGED
@@ -13,8 +13,6 @@ async function patchReactExperimental() {
13
13
  try {
14
14
  // Read installed React version from user's project
15
15
 
16
- const installedVersion = React.version
17
- const PATCH_RECONCILER_VERSION = installedVersion.replace('19.1.0', '0.0.0')
18
16
  if(TEMPLATE_VERSION === PATCH_RECONCILER_VERSION) {
19
17
  console.log('React version is patched')
20
18
  }
@@ -25,6 +23,14 @@ async function patchReactExperimental() {
25
23
  template: `../scripts/react-server-dom-esm+${TEMPLATE_VERSION}.patch`,
26
24
  output: `react-server-dom-esm+${STUB_VERSION}.patch`
27
25
  },
26
+ {
27
+ template: `../scripts/react+${TEMPLATE_VERSION}.patch`,
28
+ output: `react+${PATCH_RECONCILER_VERSION}.patch`
29
+ },
30
+ {
31
+ template: `../scripts/react-dom+${TEMPLATE_VERSION}.patch`,
32
+ output: `react-dom+${PATCH_RECONCILER_VERSION}.patch`
33
+ },
28
34
  ]
29
35
 
30
36
  // Create patches dir in user's project
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-react-server",
3
- "version": "0.3.18",
3
+ "version": "0.3.19",
4
4
  "description": "Vite plugin for React Server Components (RSC)",
5
5
  "type": "module",
6
6
  "main": "./dist/plugin/index.js",
@@ -91,8 +91,8 @@
91
91
  },
92
92
  "homepage": "https://github.com/nicobrinkkemper/vite-plugin-react-server#readme",
93
93
  "peerDependencies": {
94
- "react": "^0.0.0-experimental-d55cc79b-20250228",
95
- "react-dom": "^0.0.0-experimental-d55cc79b-20250228",
94
+ "react": "^0.0.0-experimental-f9d78089-20250306",
95
+ "react-dom": "^0.0.0-experimental-f9d78089-20250306",
96
96
  "vite": "*"
97
97
  },
98
98
  "peerDependenciesMeta": {
@@ -1 +1 @@
1
- {"version":3,"file":"checkFilesExist.d.ts","sourceRoot":"","sources":["../../plugin/checkFilesExist.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAW7E,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,qBAAqB,CAAC,CA4ChC"}
1
+ {"version":3,"file":"checkFilesExist.d.ts","sourceRoot":"","sources":["../../plugin/checkFilesExist.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAY7E,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,qBAAqB,CAAC,CA+ChC"}
@@ -5,9 +5,9 @@
5
5
  */
6
6
  import { existsSync } from 'node:fs';
7
7
  import { join } from 'node:path';
8
- import 'vite';
9
8
  import { createInputNormalizer } from './helpers/inputNormalizer.js';
10
9
 
10
+ let stashedFiles = null;
11
11
  const resolveFileOption = (pageOrProps) => {
12
12
  if (typeof pageOrProps === "string") {
13
13
  return () => pageOrProps;
@@ -15,6 +15,9 @@ const resolveFileOption = (pageOrProps) => {
15
15
  return pageOrProps;
16
16
  };
17
17
  async function checkFilesExist(pages, options, root) {
18
+ if (stashedFiles) {
19
+ return stashedFiles;
20
+ }
18
21
  if (!root || root === "") {
19
22
  throw new Error("Root not found");
20
23
  }
@@ -56,7 +59,8 @@ async function checkFilesExist(pages, options, root) {
56
59
  pageMap.set(pageKey, pageValue);
57
60
  propsMap.set(propsKey, propsValue);
58
61
  }
59
- return { pageMap, pageSet, propsMap, propsSet, urlMap, errors };
62
+ stashedFiles = { pageMap, pageSet, propsMap, propsSet, urlMap, errors };
63
+ return stashedFiles;
60
64
  }
61
65
 
62
66
  export { checkFilesExist };
@@ -1 +1 @@
1
- {"version":3,"file":"checkFilesExist.js","sources":["../../plugin/checkFilesExist.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { CheckFilesExistReturn, ResolvedUserOptions } from \"./types.js\";\nimport { normalizePath } from \"vite\";\nimport { createInputNormalizer } from \"./helpers/inputNormalizer.js\";\n\nconst resolveFileOption = (pageOrProps: string | ((url: string) => string)) => {\n if (typeof pageOrProps === \"string\") {\n return () => pageOrProps;\n }\n return pageOrProps;\n};\n\nexport async function checkFilesExist(\n pages: string[],\n options: ResolvedUserOptions,\n root: string\n): Promise<CheckFilesExistReturn> {\n if (!root || root === \"\") {\n throw new Error(\"Root not found\");\n }\n const errors: string[] = [];\n const pageSet = new Set<string>();\n const propsSet = new Set<string>();\n const pageMap = new Map<string, string>();\n const propsMap = new Map<string, string>();\n const urlMap = new Map<string, { props: string; page: string }>();\n const normalizer = createInputNormalizer({\n root,\n preserveModulesRoot: options.build.preserveModulesRoot === true ? options.moduleBase : undefined,\n removeExtension: true,\n });\n const pageFn = resolveFileOption(options.Page);\n const propsFn = resolveFileOption(options.props);\n for (const page of pages) {\n const pagePath = pageFn(page);\n const propsPath = propsFn(page);\n const [pageKey, pageValue] = normalizer(pagePath);\n const [propsKey, propsValue] = normalizer(propsPath);\n try {\n if (!existsSync(join(root, pageValue))) {\n errors.push(\n `Page file not found: ${pagePath}, ${join(root, pagePath)}`\n );\n }\n if (!existsSync(join(root, propsValue))) {\n errors.push(\n `Props file not found: ${propsPath}, ${join(root, propsPath)}`\n );\n }\n } catch (error) {\n errors.push(`Error checking files: ${error}`);\n }\n urlMap.set(page, { props: propsPath, page: pagePath });\n pageSet.add(pagePath);\n propsSet.add(propsPath);\n pageMap.set(pageKey, pageValue);\n propsMap.set(propsKey, propsValue);\n }\n\n return { pageMap, pageSet, propsMap, propsSet, urlMap, errors };\n}\n"],"names":[],"mappings":";;;;;;;;;;AAMA,MAAM,iBAAA,GAAoB,CAAC,WAAoD,KAAA;AAC7E,EAAI,IAAA,OAAO,gBAAgB,QAAU,EAAA;AACnC,IAAA,OAAO,MAAM,WAAA;AAAA;AAEf,EAAO,OAAA,WAAA;AACT,CAAA;AAEsB,eAAA,eAAA,CACpB,KACA,EAAA,OAAA,EACA,IACgC,EAAA;AAChC,EAAI,IAAA,CAAC,IAAQ,IAAA,IAAA,KAAS,EAAI,EAAA;AACxB,IAAM,MAAA,IAAI,MAAM,gBAAgB,CAAA;AAAA;AAElC,EAAA,MAAM,SAAmB,EAAC;AAC1B,EAAM,MAAA,OAAA,uBAAc,GAAY,EAAA;AAChC,EAAM,MAAA,QAAA,uBAAe,GAAY,EAAA;AACjC,EAAM,MAAA,OAAA,uBAAc,GAAoB,EAAA;AACxC,EAAM,MAAA,QAAA,uBAAe,GAAoB,EAAA;AACzC,EAAM,MAAA,MAAA,uBAAa,GAA6C,EAAA;AAChE,EAAA,MAAM,aAAa,qBAAsB,CAAA;AAAA,IACvC,IAAA;AAAA,IACA,qBAAqB,OAAQ,CAAA,KAAA,CAAM,mBAAwB,KAAA,IAAA,GAAO,QAAQ,UAAa,GAAA,SAAA;AAAA,IACvF,eAAiB,EAAA;AAAA,GAClB,CAAA;AACD,EAAM,MAAA,MAAA,GAAS,iBAAkB,CAAA,OAAA,CAAQ,IAAI,CAAA;AAC7C,EAAM,MAAA,OAAA,GAAU,iBAAkB,CAAA,OAAA,CAAQ,KAAK,CAAA;AAC/C,EAAA,KAAA,MAAW,QAAQ,KAAO,EAAA;AACxB,IAAM,MAAA,QAAA,GAAW,OAAO,IAAI,CAAA;AAC5B,IAAM,MAAA,SAAA,GAAY,QAAQ,IAAI,CAAA;AAC9B,IAAA,MAAM,CAAC,OAAA,EAAS,SAAS,CAAA,GAAI,WAAW,QAAQ,CAAA;AAChD,IAAA,MAAM,CAAC,QAAA,EAAU,UAAU,CAAA,GAAI,WAAW,SAAS,CAAA;AACnD,IAAI,IAAA;AACF,MAAA,IAAI,CAAC,UAAW,CAAA,IAAA,CAAK,IAAM,EAAA,SAAS,CAAC,CAAG,EAAA;AACtC,QAAO,MAAA,CAAA,IAAA;AAAA,UACL,wBAAwB,QAAQ,CAAA,EAAA,EAAK,IAAK,CAAA,IAAA,EAAM,QAAQ,CAAC,CAAA;AAAA,SAC3D;AAAA;AAEF,MAAA,IAAI,CAAC,UAAW,CAAA,IAAA,CAAK,IAAM,EAAA,UAAU,CAAC,CAAG,EAAA;AACvC,QAAO,MAAA,CAAA,IAAA;AAAA,UACL,yBAAyB,SAAS,CAAA,EAAA,EAAK,IAAK,CAAA,IAAA,EAAM,SAAS,CAAC,CAAA;AAAA,SAC9D;AAAA;AACF,aACO,KAAO,EAAA;AACd,MAAO,MAAA,CAAA,IAAA,CAAK,CAAyB,sBAAA,EAAA,KAAK,CAAE,CAAA,CAAA;AAAA;AAE9C,IAAA,MAAA,CAAO,IAAI,IAAM,EAAA,EAAE,OAAO,SAAW,EAAA,IAAA,EAAM,UAAU,CAAA;AACrD,IAAA,OAAA,CAAQ,IAAI,QAAQ,CAAA;AACpB,IAAA,QAAA,CAAS,IAAI,SAAS,CAAA;AACtB,IAAQ,OAAA,CAAA,GAAA,CAAI,SAAS,SAAS,CAAA;AAC9B,IAAS,QAAA,CAAA,GAAA,CAAI,UAAU,UAAU,CAAA;AAAA;AAGnC,EAAA,OAAO,EAAE,OAAS,EAAA,OAAA,EAAS,QAAU,EAAA,QAAA,EAAU,QAAQ,MAAO,EAAA;AAChE;;;;"}
1
+ {"version":3,"file":"checkFilesExist.js","sources":["../../plugin/checkFilesExist.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { CheckFilesExistReturn, ResolvedUserOptions } from \"./types.js\";\nimport { createInputNormalizer } from \"./helpers/inputNormalizer.js\";\n\nlet stashedFiles: CheckFilesExistReturn | null = null;\n\nconst resolveFileOption = (pageOrProps: string | ((url: string) => string)) => {\n if (typeof pageOrProps === \"string\") {\n return () => pageOrProps;\n }\n return pageOrProps;\n};\n\nexport async function checkFilesExist(\n pages: string[],\n options: ResolvedUserOptions,\n root: string\n): Promise<CheckFilesExistReturn> {\n if(stashedFiles){\n return stashedFiles;\n }\n if (!root || root === \"\") {\n throw new Error(\"Root not found\");\n }\n const errors: string[] = [];\n const pageSet = new Set<string>();\n const propsSet = new Set<string>();\n const pageMap = new Map<string, string>();\n const propsMap = new Map<string, string>();\n const urlMap = new Map<string, { props: string; page: string }>();\n const normalizer = createInputNormalizer({\n root,\n preserveModulesRoot: options.build.preserveModulesRoot === true ? options.moduleBase : undefined,\n removeExtension: true,\n });\n const pageFn = resolveFileOption(options.Page);\n const propsFn = resolveFileOption(options.props);\n for (const page of pages) {\n const pagePath = pageFn(page);\n const propsPath = propsFn(page);\n const [pageKey, pageValue] = normalizer(pagePath);\n const [propsKey, propsValue] = normalizer(propsPath);\n try {\n if (!existsSync(join(root, pageValue))) {\n errors.push(\n `Page file not found: ${pagePath}, ${join(root, pagePath)}`\n );\n }\n if (!existsSync(join(root, propsValue))) {\n errors.push(\n `Props file not found: ${propsPath}, ${join(root, propsPath)}`\n );\n }\n } catch (error) {\n errors.push(`Error checking files: ${error}`);\n }\n urlMap.set(page, { props: propsPath, page: pagePath });\n pageSet.add(pagePath);\n propsSet.add(propsPath);\n pageMap.set(pageKey, pageValue);\n propsMap.set(propsKey, propsValue);\n }\n stashedFiles = { pageMap, pageSet, propsMap, propsSet, urlMap, errors };\n return stashedFiles;\n}\n"],"names":[],"mappings":";;;;;;;;;AAKA,IAAI,YAA6C,GAAA,IAAA;AAEjD,MAAM,iBAAA,GAAoB,CAAC,WAAoD,KAAA;AAC7E,EAAI,IAAA,OAAO,gBAAgB,QAAU,EAAA;AACnC,IAAA,OAAO,MAAM,WAAA;AAAA;AAEf,EAAO,OAAA,WAAA;AACT,CAAA;AAEsB,eAAA,eAAA,CACpB,KACA,EAAA,OAAA,EACA,IACgC,EAAA;AAChC,EAAA,IAAG,YAAa,EAAA;AACd,IAAO,OAAA,YAAA;AAAA;AAET,EAAI,IAAA,CAAC,IAAQ,IAAA,IAAA,KAAS,EAAI,EAAA;AACxB,IAAM,MAAA,IAAI,MAAM,gBAAgB,CAAA;AAAA;AAElC,EAAA,MAAM,SAAmB,EAAC;AAC1B,EAAM,MAAA,OAAA,uBAAc,GAAY,EAAA;AAChC,EAAM,MAAA,QAAA,uBAAe,GAAY,EAAA;AACjC,EAAM,MAAA,OAAA,uBAAc,GAAoB,EAAA;AACxC,EAAM,MAAA,QAAA,uBAAe,GAAoB,EAAA;AACzC,EAAM,MAAA,MAAA,uBAAa,GAA6C,EAAA;AAChE,EAAA,MAAM,aAAa,qBAAsB,CAAA;AAAA,IACvC,IAAA;AAAA,IACA,qBAAqB,OAAQ,CAAA,KAAA,CAAM,mBAAwB,KAAA,IAAA,GAAO,QAAQ,UAAa,GAAA,SAAA;AAAA,IACvF,eAAiB,EAAA;AAAA,GAClB,CAAA;AACD,EAAM,MAAA,MAAA,GAAS,iBAAkB,CAAA,OAAA,CAAQ,IAAI,CAAA;AAC7C,EAAM,MAAA,OAAA,GAAU,iBAAkB,CAAA,OAAA,CAAQ,KAAK,CAAA;AAC/C,EAAA,KAAA,MAAW,QAAQ,KAAO,EAAA;AACxB,IAAM,MAAA,QAAA,GAAW,OAAO,IAAI,CAAA;AAC5B,IAAM,MAAA,SAAA,GAAY,QAAQ,IAAI,CAAA;AAC9B,IAAA,MAAM,CAAC,OAAA,EAAS,SAAS,CAAA,GAAI,WAAW,QAAQ,CAAA;AAChD,IAAA,MAAM,CAAC,QAAA,EAAU,UAAU,CAAA,GAAI,WAAW,SAAS,CAAA;AACnD,IAAI,IAAA;AACF,MAAA,IAAI,CAAC,UAAW,CAAA,IAAA,CAAK,IAAM,EAAA,SAAS,CAAC,CAAG,EAAA;AACtC,QAAO,MAAA,CAAA,IAAA;AAAA,UACL,wBAAwB,QAAQ,CAAA,EAAA,EAAK,IAAK,CAAA,IAAA,EAAM,QAAQ,CAAC,CAAA;AAAA,SAC3D;AAAA;AAEF,MAAA,IAAI,CAAC,UAAW,CAAA,IAAA,CAAK,IAAM,EAAA,UAAU,CAAC,CAAG,EAAA;AACvC,QAAO,MAAA,CAAA,IAAA;AAAA,UACL,yBAAyB,SAAS,CAAA,EAAA,EAAK,IAAK,CAAA,IAAA,EAAM,SAAS,CAAC,CAAA;AAAA,SAC9D;AAAA;AACF,aACO,KAAO,EAAA;AACd,MAAO,MAAA,CAAA,IAAA,CAAK,CAAyB,sBAAA,EAAA,KAAK,CAAE,CAAA,CAAA;AAAA;AAE9C,IAAA,MAAA,CAAO,IAAI,IAAM,EAAA,EAAE,OAAO,SAAW,EAAA,IAAA,EAAM,UAAU,CAAA;AACrD,IAAA,OAAA,CAAQ,IAAI,QAAQ,CAAA;AACpB,IAAA,QAAA,CAAS,IAAI,SAAS,CAAA;AACtB,IAAQ,OAAA,CAAA,GAAA,CAAI,SAAS,SAAS,CAAA;AAC9B,IAAS,QAAA,CAAA,GAAA,CAAI,UAAU,UAAU,CAAA;AAAA;AAEnC,EAAA,YAAA,GAAe,EAAE,OAAS,EAAA,OAAA,EAAS,QAAU,EAAA,QAAA,EAAU,QAAQ,MAAO,EAAA;AACtE,EAAO,OAAA,YAAA;AACT;;;;"}
@@ -0,0 +1,23 @@
1
+ import type { Manifest, ModuleGraph } from "vite";
2
+ export declare function collectModuleGraphCss({ moduleGraph, pagePath, onCss, parentUrl, }: {
3
+ moduleGraph: ModuleGraph;
4
+ pagePath: string;
5
+ onCss?: (path: string, parentUrl: string) => void;
6
+ parentUrl?: string;
7
+ }): Promise<Map<string, string>>;
8
+ export declare function collectManifestClientFiles({ manifest, root, pagePath, preserveModulesRoot, moduleBase, onCss, onClientModule, testClient, testJson, }: {
9
+ manifest: Manifest;
10
+ root: string;
11
+ pagePath: string;
12
+ preserveModulesRoot?: boolean;
13
+ moduleBase?: string;
14
+ onCss?: (path: string, parentUrl: string) => void;
15
+ onClientModule?: (path: string, parentUrl: string) => void;
16
+ parentUrl?: string;
17
+ testClient?: (id: string) => boolean;
18
+ testJson?: (id: string) => boolean;
19
+ }): {
20
+ cssFiles: Map<string, string>;
21
+ clientFiles: Map<string, string>;
22
+ };
23
+ //# sourceMappingURL=collect-manifest-client-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-manifest-client-files.d.ts","sourceRoot":"","sources":["../../plugin/collect-manifest-client-files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC;AAIlD,wBAAsB,qBAAqB,CAAC,EAC1C,WAAW,EACX,QAAQ,EACR,KAAK,EACL,SAAS,GACV,EAAE;IACD,WAAW,EAAE,WAAW,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,gCAoBA;AAED,wBAAgB,0BAA0B,CAAC,EACzC,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,mBAAmB,EACnB,UAAU,EACV,KAAK,EACL,cAAc,EACd,UAA0D,EAC1D,QAAmD,GACpD,EAAE;IACD,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACrC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;CACpC;;;EA8FA"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * vite-plugin-react-server
3
+ * Copyright (c) Nico Brinkkemper
4
+ * MIT License
5
+ */
6
+ import { createInputNormalizer } from './helpers/inputNormalizer.js';
7
+ import { DEFAULT_CONFIG } from './config/defaults.js';
8
+
9
+ async function collectModuleGraphCss({
10
+ moduleGraph,
11
+ pagePath,
12
+ onCss,
13
+ parentUrl
14
+ }) {
15
+ if (!pagePath) return /* @__PURE__ */ new Map();
16
+ const cssFiles = /* @__PURE__ */ new Map();
17
+ const pageModule = await moduleGraph.getModuleByUrl(pagePath, true);
18
+ if (!pageModule) {
19
+ return /* @__PURE__ */ new Map();
20
+ }
21
+ const seen = /* @__PURE__ */ new Set();
22
+ const walkModule = (mod) => {
23
+ if (!mod?.id || seen.has(mod.id)) return;
24
+ seen.add(mod.id);
25
+ if (mod?.id?.endsWith(".css")) {
26
+ cssFiles.set(mod?.url, mod?.id);
27
+ onCss?.(mod?.url, parentUrl ?? pagePath);
28
+ }
29
+ mod?.importedModules?.forEach((imp) => walkModule(imp));
30
+ };
31
+ walkModule(pageModule);
32
+ return cssFiles;
33
+ }
34
+ function collectManifestClientFiles({
35
+ manifest,
36
+ root,
37
+ pagePath,
38
+ preserveModulesRoot,
39
+ moduleBase,
40
+ onCss,
41
+ onClientModule,
42
+ testClient = DEFAULT_CONFIG.AUTO_DISCOVER.clientComponents,
43
+ testJson = DEFAULT_CONFIG.AUTO_DISCOVER.jsonPattern
44
+ }) {
45
+ const normalizer = createInputNormalizer({
46
+ root,
47
+ removeExtension: true,
48
+ preserveModulesRoot: preserveModulesRoot ? moduleBase : undefined
49
+ });
50
+ const [_, value] = normalizer(pagePath);
51
+ const cssFiles = /* @__PURE__ */ new Map();
52
+ const clientFiles = /* @__PURE__ */ new Map();
53
+ const seen = /* @__PURE__ */ new Set();
54
+ const manifestValues = Object.values(manifest);
55
+ const possibleKeys = [
56
+ value
57
+ // Relative path
58
+ ];
59
+ const walkManifestEntry = (id, parentUrl) => {
60
+ if (seen.has(id)) return;
61
+ seen.add(id);
62
+ const entry = manifest[id] ?? manifestValues.find((e) => id === e.file);
63
+ if (!entry) {
64
+ console.log(
65
+ `No manifest entry found for ${id}, possible keys: ${Object.keys(
66
+ manifest
67
+ ).join(", ")}`
68
+ );
69
+ return;
70
+ }
71
+ if (typeof testClient === "function" && typeof onClientModule === "function" && testClient(entry.file) || typeof testJson === "function" && testJson(entry.file)) {
72
+ onClientModule?.(entry.file ?? "", parentUrl);
73
+ clientFiles.set(id, entry.name ?? "");
74
+ }
75
+ if (entry.css) {
76
+ entry.css.forEach((css) => {
77
+ cssFiles.set(css, css);
78
+ onCss?.(css, id);
79
+ onClientModule?.(css, id);
80
+ });
81
+ }
82
+ if (entry.imports) {
83
+ entry.imports.forEach(
84
+ (imp) => walkManifestEntry(imp, entry.file)
85
+ );
86
+ }
87
+ if (entry.dynamicImports) {
88
+ entry.dynamicImports.forEach(
89
+ (imp) => walkManifestEntry(imp, entry.file)
90
+ );
91
+ }
92
+ };
93
+ for (const possibleKey of possibleKeys) {
94
+ if (manifest[possibleKey]) {
95
+ walkManifestEntry(possibleKey, pagePath);
96
+ break;
97
+ }
98
+ }
99
+ if (cssFiles.size === 0) {
100
+ const entry = manifestValues.find(
101
+ (e) => possibleKeys.includes(e.file) || e.src && possibleKeys.includes(e.src) || e.name && possibleKeys.includes(e.name)
102
+ );
103
+ if (entry) {
104
+ walkManifestEntry(value, pagePath);
105
+ } else {
106
+ console.warn(
107
+ `No manifest entry found for ${value} (tried all possible keys: ${possibleKeys.join(
108
+ ", "
109
+ )})`
110
+ );
111
+ }
112
+ }
113
+ return { cssFiles, clientFiles };
114
+ }
115
+
116
+ export { collectManifestClientFiles, collectModuleGraphCss };
117
+ //# sourceMappingURL=collect-manifest-client-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-manifest-client-files.js","sources":["../../plugin/collect-manifest-client-files.ts"],"sourcesContent":["import type { Manifest, ModuleGraph } from \"vite\";\nimport { createInputNormalizer } from \"./helpers/inputNormalizer.js\";\nimport { DEFAULT_CONFIG } from \"./config/defaults.js\";\n\nexport async function collectModuleGraphCss({\n moduleGraph,\n pagePath,\n onCss,\n parentUrl,\n}: {\n moduleGraph: ModuleGraph;\n pagePath: string;\n onCss?: (path: string, parentUrl: string) => void;\n parentUrl?: string;\n}) {\n if (!pagePath) return new Map<string, string>();\n\n const cssFiles = new Map<string, string>();\n const pageModule = await moduleGraph.getModuleByUrl(pagePath, true);\n if (!pageModule) {\n return new Map<string, string>();\n }\n const seen = new Set<string>();\n const walkModule = (mod: any) => {\n if (!mod?.id || seen.has(mod.id)) return;\n seen.add(mod.id);\n if (mod?.id?.endsWith(\".css\")) {\n cssFiles.set(mod?.url, mod?.id);\n onCss?.(mod?.url, parentUrl ?? pagePath);\n }\n mod?.importedModules?.forEach((imp: any) => walkModule(imp));\n };\n walkModule(pageModule);\n return cssFiles;\n}\n\nexport function collectManifestClientFiles({\n manifest,\n root,\n pagePath,\n preserveModulesRoot,\n moduleBase,\n onCss,\n onClientModule,\n testClient = DEFAULT_CONFIG.AUTO_DISCOVER.clientComponents,\n testJson = DEFAULT_CONFIG.AUTO_DISCOVER.jsonPattern,\n}: {\n manifest: Manifest;\n root: string;\n pagePath: string;\n preserveModulesRoot?: boolean;\n moduleBase?: string;\n onCss?: (path: string, parentUrl: string) => void;\n onClientModule?: (path: string, parentUrl: string) => void;\n parentUrl?: string;\n testClient?: (id: string) => boolean;\n testJson?: (id: string) => boolean;\n}) {\n const normalizer = createInputNormalizer({\n root,\n removeExtension: true,\n preserveModulesRoot: preserveModulesRoot ? moduleBase : undefined,\n });\n const [_, value] = normalizer(pagePath);\n\n const cssFiles = new Map<string, string>();\n const clientFiles = new Map<string, string>();\n const seen = new Set<string>();\n const manifestValues = Object.values(manifest);\n\n // Try different variations of the path\n const possibleKeys = [\n value, // Relative path\n ];\n\n const walkManifestEntry = (id: string, parentUrl: string) => {\n if (seen.has(id)) return;\n seen.add(id);\n\n // Get the manifest entry\n const entry = manifest[id] ?? manifestValues.find((e) => id === e.file);\n if (!entry) {\n console.log(\n `No manifest entry found for ${id}, possible keys: ${Object.keys(\n manifest\n ).join(\", \")}`\n );\n return;\n }\n if (\n (typeof testClient === \"function\" &&\n typeof onClientModule === \"function\" &&\n testClient(entry.file)) ||\n (typeof testJson === \"function\" && testJson(entry.file))\n ) {\n onClientModule?.(entry.file ?? \"\", parentUrl);\n clientFiles.set(id, entry.name ?? \"\");\n }\n\n // Add direct CSS from the css array\n if (entry.css) {\n entry.css.forEach((css: string) => {\n cssFiles.set(css, css);\n onCss?.(css, id);\n onClientModule?.(css, id);\n });\n }\n\n // Walk imports recursively\n if (entry.imports) {\n entry.imports.forEach((imp: string) =>\n walkManifestEntry(imp, entry.file)\n );\n }\n\n // Also check dynamicImports\n if (entry.dynamicImports) {\n entry.dynamicImports.forEach((imp: string) =>\n walkManifestEntry(imp, entry.file)\n );\n }\n };\n\n // Try all possible keys\n for (const possibleKey of possibleKeys) {\n if (manifest[possibleKey]) {\n walkManifestEntry(possibleKey, pagePath);\n break;\n }\n }\n\n // If no entry found by key, try matching by file\n if (cssFiles.size === 0) {\n const entry = manifestValues.find(\n (e) =>\n possibleKeys.includes(e.file) ||\n (e.src && possibleKeys.includes(e.src)) ||\n (e.name && possibleKeys.includes(e.name))\n );\n if (entry) {\n walkManifestEntry(value, pagePath);\n } else {\n console.warn(\n `No manifest entry found for ${value} (tried all possible keys: ${possibleKeys.join(\n \", \"\n )})`\n );\n }\n }\n\n return { cssFiles, clientFiles };\n}\n"],"names":[],"mappings":"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"}
@@ -1,14 +1,14 @@
1
+ import React from "react";
1
2
  /**
2
3
  * A component that emits <link> tags for CSS files during streaming.
3
4
  * The high precedence ensures they bubble up to the document head.
4
5
  */
5
- export declare function CssCollector({ url, moduleBasePath }: {
6
- url: string;
7
- moduleBasePath: string;
8
- }): import("react").DetailedReactHTMLElement<{
9
- key: string;
10
- rel: string;
11
- href: string;
12
- precedence: string;
13
- }, HTMLElement>;
6
+ export declare function CssCollector({ children, cssFiles, moduleBaseUrl, route }: {
7
+ children?: React.ReactNode;
8
+ cssFiles: string[];
9
+ moduleBaseUrl?: string;
10
+ route?: string;
11
+ }): React.FunctionComponentElement<{
12
+ children?: React.ReactNode | undefined;
13
+ }>;
14
14
  //# sourceMappingURL=components.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../plugin/components.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE;;;;;gBAO5F"}
1
+ {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../plugin/components.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,QAAQ,EACR,aAAa,EACb,KAAW,EACZ,EAAE;IACD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;GA2CA"}
@@ -1,13 +1,54 @@
1
- import { createElement } from 'react';
2
1
  /**
3
- * A component that emits <link> tags for CSS files during streaming.
4
- * The high precedence ensures they bubble up to the document head.
2
+ * vite-plugin-react-server
3
+ * Copyright (c) Nico Brinkkemper
4
+ * MIT License
5
5
  */
6
- export function CssCollector({ url, moduleBasePath }) {
7
- return createElement('link', {
8
- key: url,
9
- rel: 'stylesheet',
6
+ import React__default from 'react';
7
+
8
+ function CssCollector({
9
+ children,
10
+ cssFiles,
11
+ moduleBaseUrl,
12
+ route = "/"
13
+ }) {
14
+ const depth = route.split("/").filter(Boolean).length;
15
+ const prefix = depth > 0 ? "../".repeat(depth) : "./";
16
+ const base = typeof moduleBaseUrl === "string" && moduleBaseUrl !== "" ? moduleBaseUrl : prefix;
17
+ return React__default.createElement(
18
+ React__default.Fragment,
19
+ null,
20
+ ...cssFiles.map((css) => {
21
+ try {
22
+ if (moduleBaseUrl) {
23
+ new URL(css, moduleBaseUrl);
24
+ } else {
25
+ new URL(`file://${base}${css}`);
26
+ }
27
+ } catch (error) {
28
+ return React__default.createElement(
29
+ "style",
30
+ { type: "text/css" },
31
+ css
32
+ );
33
+ }
34
+ let url = css;
35
+ if (css.startsWith("http") || css.startsWith("data:")) {
36
+ url = css;
37
+ } else if (css.startsWith("/")) {
38
+ url = base + css.slice(1);
39
+ } else if (!css.startsWith("./") && !css.startsWith("../")) {
40
+ url = base + css;
41
+ }
42
+ return React__default.createElement("link", {
43
+ key: css,
44
+ rel: "stylesheet",
10
45
  href: url,
11
- precedence: 'high'
12
- });
46
+ precedence: "high"
47
+ });
48
+ }),
49
+ children
50
+ );
13
51
  }
52
+
53
+ export { CssCollector };
54
+ //# sourceMappingURL=components.js.map