@vitejs/plugin-rsc 0.4.20 → 0.4.21
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 +64 -60
- package/dist/index.js +1 -1
- package/dist/{plugin-DYOxE_xg.js → plugin-CzZCeIwj.js} +29 -5
- package/dist/plugin.js +1 -1
- package/package.json +4 -5
package/README.md
CHANGED
|
@@ -4,10 +4,10 @@ This package provides [React Server Components](https://react.dev/reference/rsc/
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **Framework-
|
|
8
|
-
- **
|
|
7
|
+
- **Framework-agnostic**: The plugin implements [RSC bundler features](https://react.dev/reference/rsc/server-components) and provides low level RSC runtime (`react-server-dom`) API without framework-specific abstractions.
|
|
8
|
+
- **Runtime-agnostic**: Built on [Vite environment API](https://vite.dev/guide/api-environment.html) and works with other runtimes (e.g., [`@cloudflare/vite-plugin`](https://github.com/cloudflare/workers-sdk/tree/main/packages/vite-plugin-cloudflare)).
|
|
9
9
|
- **HMR support**: Enables editing both client and server components without full page reloads.
|
|
10
|
-
- **
|
|
10
|
+
- **CSS support**: CSS is automatically code-split both at client and server components and they are injected upon rendering.
|
|
11
11
|
|
|
12
12
|
## Getting Started
|
|
13
13
|
|
|
@@ -19,14 +19,17 @@ npx degit vitejs/vite-plugin-react/packages/plugin-rsc/examples/starter my-app
|
|
|
19
19
|
|
|
20
20
|
## Examples
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- [`./examples/
|
|
29
|
-
-
|
|
22
|
+
**Start here:** [`./examples/starter`](./examples/starter) - Recommended for understanding the plugin
|
|
23
|
+
|
|
24
|
+
- Provides an in-depth overview of API with inline comments to explain how they function within RSC-powered React application.
|
|
25
|
+
|
|
26
|
+
**Integration examples:**
|
|
27
|
+
|
|
28
|
+
- [`./examples/basic`](./examples/basic) - Advanced RSC features and testing
|
|
29
|
+
- This is mainly used for e2e testing and includes various advanced RSC usages (e.g. `"use cache"` example).
|
|
30
|
+
- [`./examples/ssg`](./examples/ssg) - Static site generation with MDX and client components for interactivity.
|
|
31
|
+
- [`./examples/react-router`](./examples/react-router) - React Router RSC integration
|
|
32
|
+
- Demonstrates how to integrate [experimental React Router RSC API](https://remix.run/blog/rsc-preview). React Router now provides [official RSC support](https://reactrouter.com/how-to/react-server-components), so it's recommended to follow React Router's official documentation for the latest integration.
|
|
30
33
|
|
|
31
34
|
## Basic Concepts
|
|
32
35
|
|
|
@@ -128,11 +131,11 @@ export default defineConfig({
|
|
|
128
131
|
- [`entry.rsc.tsx`](./examples/starter/src/framework/entry.rsc.tsx)
|
|
129
132
|
|
|
130
133
|
```tsx
|
|
131
|
-
import
|
|
134
|
+
import { renderToReadableStream } from '@vitejs/plugin-rsc/rsc'
|
|
132
135
|
|
|
133
136
|
// the plugin assumes `rsc` entry having default export of request handler
|
|
134
137
|
export default async function handler(request: Request): Promise<Response> {
|
|
135
|
-
//
|
|
138
|
+
// serialize React VDOM to RSC stream
|
|
136
139
|
const root = (
|
|
137
140
|
<html>
|
|
138
141
|
<body>
|
|
@@ -140,7 +143,7 @@ export default async function handler(request: Request): Promise<Response> {
|
|
|
140
143
|
</body>
|
|
141
144
|
</html>
|
|
142
145
|
)
|
|
143
|
-
const rscStream =
|
|
146
|
+
const rscStream = renderToReadableStream(root)
|
|
144
147
|
|
|
145
148
|
// respond direct RSC stream request based on framework's convention
|
|
146
149
|
if (request.url.endsWith('.rsc')) {
|
|
@@ -170,19 +173,19 @@ export default async function handler(request: Request): Promise<Response> {
|
|
|
170
173
|
- [`entry.ssr.tsx`](./examples/starter/src/framework/entry.ssr.tsx)
|
|
171
174
|
|
|
172
175
|
```tsx
|
|
173
|
-
import
|
|
174
|
-
import
|
|
176
|
+
import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr'
|
|
177
|
+
import { renderToReadableStream } from 'react-dom/server.edge'
|
|
175
178
|
|
|
176
179
|
export async function handleSsr(rscStream: ReadableStream) {
|
|
177
180
|
// deserialize RSC stream back to React VDOM
|
|
178
|
-
const root = await
|
|
181
|
+
const root = await createFromReadableStream(rscStream)
|
|
179
182
|
|
|
180
183
|
// helper API to allow referencing browser entry content from SSR environment
|
|
181
184
|
const bootstrapScriptContent =
|
|
182
185
|
await import.meta.viteRsc.loadBootstrapScriptContent('index')
|
|
183
186
|
|
|
184
187
|
// render html (traditional SSR)
|
|
185
|
-
const htmlStream =
|
|
188
|
+
const htmlStream = renderToReadableStream(root, {
|
|
186
189
|
bootstrapScriptContent,
|
|
187
190
|
})
|
|
188
191
|
|
|
@@ -193,51 +196,26 @@ export async function handleSsr(rscStream: ReadableStream) {
|
|
|
193
196
|
- [`entry.browser.tsx`](./examples/starter/src/framework/entry.browser.tsx)
|
|
194
197
|
|
|
195
198
|
```tsx
|
|
196
|
-
import
|
|
197
|
-
import
|
|
199
|
+
import { createFromReadableStream } from '@vitejs/plugin-rsc/browser'
|
|
200
|
+
import { hydrateRoot } from 'react-dom/client'
|
|
198
201
|
|
|
199
202
|
async function main() {
|
|
200
203
|
// fetch and deserialize RSC stream back to React VDOM
|
|
201
|
-
const rscResponse = await fetch(window.location.href +
|
|
202
|
-
const root = await
|
|
204
|
+
const rscResponse = await fetch(window.location.href + '.rsc')
|
|
205
|
+
const root = await createFromReadableStream(rscResponse.body)
|
|
203
206
|
|
|
204
207
|
// hydration (traditional CSR)
|
|
205
|
-
|
|
208
|
+
hydrateRoot(document, root)
|
|
206
209
|
}
|
|
207
210
|
|
|
208
|
-
main()
|
|
211
|
+
main()
|
|
209
212
|
```
|
|
210
213
|
|
|
211
|
-
## `react-server-dom` API
|
|
212
|
-
|
|
213
|
-
### `@vitejs/plugin-rsc/rsc`
|
|
214
|
-
|
|
215
|
-
This module re-exports RSC runtime API provided by `react-server-dom/server.edge` and `react-server-dom/client.edge` such as:
|
|
216
|
-
|
|
217
|
-
- `renderToReadableStream`: RSC serialization (React VDOM -> RSC stream)
|
|
218
|
-
- `createFromReadableStream`: RSC deserialization (RSC stream -> React VDOM). This is also available on rsc environment itself. For example, it allows saving serailized RSC and deserializing it for later use.
|
|
219
|
-
- `decodeAction/decodeReply/decodeFormState/loadServerAction/createTemporaryReferenceSet`
|
|
220
|
-
- `encodeReply/createClientTemporaryReferenceSet`
|
|
221
|
-
|
|
222
|
-
### `@vitejs/plugin-rsc/ssr`
|
|
223
|
-
|
|
224
|
-
This module re-exports RSC runtime API provided by `react-server-dom/client.edge`
|
|
225
|
-
|
|
226
|
-
- `createFromReadableStream`: RSC deserialization (RSC stream -> React VDOM)
|
|
227
|
-
|
|
228
|
-
### `@vitejs/plugin-rsc/browser`
|
|
229
|
-
|
|
230
|
-
This module re-exports RSC runtime API provided by `react-server-dom/client.browser`
|
|
231
|
-
|
|
232
|
-
- `createFromReadableStream`: RSC deserialization (RSC stream -> React VDOM)
|
|
233
|
-
- `createFromFetch`: a robust way of `createFromReadableStream((await fetch("...")).body)`
|
|
234
|
-
- `encodeReply/setServerCallback`: server function related...
|
|
235
|
-
|
|
236
214
|
## Environment helper API
|
|
237
215
|
|
|
238
216
|
The plugin provides an additional helper for multi environment interaction.
|
|
239
217
|
|
|
240
|
-
###
|
|
218
|
+
### Available on `rsc` or `ssr` environment
|
|
241
219
|
|
|
242
220
|
#### `import.meta.viteRsc.loadModule`
|
|
243
221
|
|
|
@@ -260,7 +238,7 @@ ssrModule.renderHTML(...);
|
|
|
260
238
|
export function renderHTML(...) {}
|
|
261
239
|
```
|
|
262
240
|
|
|
263
|
-
###
|
|
241
|
+
### Available on `rsc` environment
|
|
264
242
|
|
|
265
243
|
#### `import.meta.viteRsc.loadCss`
|
|
266
244
|
|
|
@@ -287,7 +265,7 @@ export function ServerPage() {
|
|
|
287
265
|
}
|
|
288
266
|
```
|
|
289
267
|
|
|
290
|
-
|
|
268
|
+
When specifying `loadCss(<id>)`, it will collect css through the server module resolved by `<id>`.
|
|
291
269
|
|
|
292
270
|
```tsx
|
|
293
271
|
// virtual:my-framework-helper
|
|
@@ -324,7 +302,7 @@ export function Page(props) {
|
|
|
324
302
|
return <div>...</div>
|
|
325
303
|
}
|
|
326
304
|
|
|
327
|
-
// my-route.
|
|
305
|
+
// my-route.tsx?vite-rsc-css-export=Page
|
|
328
306
|
function Page(props) {
|
|
329
307
|
return <div>...</div>
|
|
330
308
|
}
|
|
@@ -341,14 +319,13 @@ function __Page(props) {
|
|
|
341
319
|
export { __Page as Page }
|
|
342
320
|
```
|
|
343
321
|
|
|
344
|
-
###
|
|
322
|
+
### Available on `ssr` environment
|
|
345
323
|
|
|
346
324
|
#### `import.meta.viteRsc.loadBootstrapScriptContent("index")`
|
|
347
325
|
|
|
348
326
|
This provides a raw js code to execute a browser entry file specified by `environments.client.build.rollupOptions.input.index`. This is intended to be used with React DOM SSR API, such as [`renderToReadableStream`](https://react.dev/reference/react-dom/server/renderToReadableStream)
|
|
349
327
|
|
|
350
328
|
```js
|
|
351
|
-
import bootstrapScriptContent from 'virtual:vite-rsc/bootstrap-script-content'
|
|
352
329
|
import { renderToReadableStream } from 'react-dom/server.edge'
|
|
353
330
|
|
|
354
331
|
const bootstrapScriptContent =
|
|
@@ -358,20 +335,18 @@ const htmlStream = await renderToReadableStream(reactNode, {
|
|
|
358
335
|
})
|
|
359
336
|
```
|
|
360
337
|
|
|
361
|
-
###
|
|
338
|
+
### Available on `client` environment
|
|
362
339
|
|
|
363
340
|
#### `rsc:update` event
|
|
364
341
|
|
|
365
342
|
This event is fired when server modules are updated, which can be used to trigger re-fetching and re-rendering of RSC components on browser.
|
|
366
343
|
|
|
367
344
|
```js
|
|
368
|
-
import
|
|
345
|
+
import { createFromFetch } from '@vitejs/plugin-rsc/browser'
|
|
369
346
|
|
|
370
347
|
import.meta.hot.on('rsc:update', async () => {
|
|
371
348
|
// re-fetch RSC stream
|
|
372
|
-
const rscPayload = await
|
|
373
|
-
fetch(window.location.href + '.rsc'),
|
|
374
|
-
)
|
|
349
|
+
const rscPayload = await createFromFetch(fetch(window.location.href + '.rsc'))
|
|
375
350
|
// re-render ...
|
|
376
351
|
})
|
|
377
352
|
```
|
|
@@ -420,6 +395,31 @@ export default defineConfig({
|
|
|
420
395
|
})
|
|
421
396
|
```
|
|
422
397
|
|
|
398
|
+
## RSC runtime (react-server-dom) API
|
|
399
|
+
|
|
400
|
+
### `@vitejs/plugin-rsc/rsc`
|
|
401
|
+
|
|
402
|
+
This module re-exports RSC runtime API provided by `react-server-dom/server.edge` and `react-server-dom/client.edge` such as:
|
|
403
|
+
|
|
404
|
+
- `renderToReadableStream`: RSC serialization (React VDOM -> RSC stream)
|
|
405
|
+
- `createFromReadableStream`: RSC deserialization (RSC stream -> React VDOM). This is also available on rsc environment itself. For example, it allows saving serialized RSC and deserializing it for later use.
|
|
406
|
+
- `decodeAction/decodeReply/decodeFormState/loadServerAction/createTemporaryReferenceSet`
|
|
407
|
+
- `encodeReply/createClientTemporaryReferenceSet`
|
|
408
|
+
|
|
409
|
+
### `@vitejs/plugin-rsc/ssr`
|
|
410
|
+
|
|
411
|
+
This module re-exports RSC runtime API provided by `react-server-dom/client.edge`
|
|
412
|
+
|
|
413
|
+
- `createFromReadableStream`: RSC deserialization (RSC stream -> React VDOM)
|
|
414
|
+
|
|
415
|
+
### `@vitejs/plugin-rsc/browser`
|
|
416
|
+
|
|
417
|
+
This module re-exports RSC runtime API provided by `react-server-dom/client.browser`
|
|
418
|
+
|
|
419
|
+
- `createFromReadableStream`: RSC deserialization (RSC stream -> React VDOM)
|
|
420
|
+
- `createFromFetch`: a robust way of `createFromReadableStream((await fetch("...")).body)`
|
|
421
|
+
- `encodeReply/setServerCallback`: server function related...
|
|
422
|
+
|
|
423
423
|
## High level API
|
|
424
424
|
|
|
425
425
|
> [!NOTE]
|
|
@@ -473,6 +473,10 @@ export function Page() {
|
|
|
473
473
|
}
|
|
474
474
|
```
|
|
475
475
|
|
|
476
|
+
## Canary and Experimental channel releases
|
|
477
|
+
|
|
478
|
+
See https://github.com/vitejs/vite-plugin-react/pull/524 for how to install the package for React [canary](https://react.dev/community/versioning-policy#canary-channel) and [experimental](https://react.dev/community/versioning-policy#all-release-channels) usages.
|
|
479
|
+
|
|
476
480
|
## Credits
|
|
477
481
|
|
|
478
482
|
This project builds on fundamental techniques and insights from pioneering Vite RSC implementations.
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./dist-DiJnRA1C.js";
|
|
2
2
|
import "./plugin-CZbI4rhS.js";
|
|
3
|
-
import { transformHoistInlineDirective, vitePluginRsc } from "./plugin-
|
|
3
|
+
import { transformHoistInlineDirective, vitePluginRsc } from "./plugin-CzZCeIwj.js";
|
|
4
4
|
import "./encryption-utils-BDwwcMVT.js";
|
|
5
5
|
import "./rpc-tGuLT8PD.js";
|
|
6
6
|
import "./vite-utils-Vzd7cqfv.js";
|
|
@@ -417,6 +417,25 @@ function extractPackageKey(id) {
|
|
|
417
417
|
return id;
|
|
418
418
|
}
|
|
419
419
|
|
|
420
|
+
//#endregion
|
|
421
|
+
//#region src/plugins/scan.ts
|
|
422
|
+
const importGlobRE = /\bimport\.meta\.glob(?:<\w+>)?\s*\(/g;
|
|
423
|
+
async function transformScanBuildStrip(code) {
|
|
424
|
+
const [imports] = esModuleLexer.parse(code);
|
|
425
|
+
let output = imports.map((e) => e.n && `import ${JSON.stringify(e.n)};\n`).filter(Boolean).join("");
|
|
426
|
+
if (importGlobRE.test(code)) {
|
|
427
|
+
const ast = await parseAstAsync(code);
|
|
428
|
+
walk(ast, { enter(node) {
|
|
429
|
+
if (node.type === "CallExpression" && node.callee.type === "MemberExpression" && node.callee.object.type === "MetaProperty" && node.callee.object.meta.type === "Identifier" && node.callee.object.meta.name === "import" && node.callee.object.property.type === "Identifier" && node.callee.object.property.name === "meta" && node.callee.property.type === "Identifier" && node.callee.property.name === "glob") {
|
|
430
|
+
const importMetaGlob = code.slice(node.start, node.end);
|
|
431
|
+
output += `console.log(${importMetaGlob});\n`;
|
|
432
|
+
}
|
|
433
|
+
} });
|
|
434
|
+
output += "";
|
|
435
|
+
}
|
|
436
|
+
return output;
|
|
437
|
+
}
|
|
438
|
+
|
|
420
439
|
//#endregion
|
|
421
440
|
//#region src/plugin.ts
|
|
422
441
|
let serverReferences = {};
|
|
@@ -446,6 +465,7 @@ function vitePluginRscMinimal(rscPluginOptions = {}) {
|
|
|
446
465
|
},
|
|
447
466
|
configResolved(config_) {
|
|
448
467
|
config = config_;
|
|
468
|
+
for (const e of Object.values(config.environments)) e.build.outDir = path.resolve(config.root, e.build.outDir);
|
|
449
469
|
},
|
|
450
470
|
configureServer(server_) {
|
|
451
471
|
server = server_;
|
|
@@ -945,10 +965,9 @@ function scanBuildStripPlugin() {
|
|
|
945
965
|
name: "rsc:scan-strip",
|
|
946
966
|
apply: "build",
|
|
947
967
|
enforce: "post",
|
|
948
|
-
transform(code, _id, _options) {
|
|
968
|
+
async transform(code, _id, _options) {
|
|
949
969
|
if (!isScanBuild) return;
|
|
950
|
-
const
|
|
951
|
-
const output = imports.map((e) => e.n && `import ${JSON.stringify(e.n)};\n`).filter(Boolean).join("");
|
|
970
|
+
const output = await transformScanBuildStrip(code);
|
|
952
971
|
return {
|
|
953
972
|
code: output,
|
|
954
973
|
map: { mappings: "" }
|
|
@@ -1396,6 +1415,9 @@ async function findSourceMapURL(server$1, filename, environmentName) {
|
|
|
1396
1415
|
};
|
|
1397
1416
|
}
|
|
1398
1417
|
function vitePluginRscCss(rscCssOptions) {
|
|
1418
|
+
function hasSpecialCssQuery(id) {
|
|
1419
|
+
return /[?&](url|inline|raw)(\b|=|&|$)/.test(id);
|
|
1420
|
+
}
|
|
1399
1421
|
function collectCss(environment, entryId) {
|
|
1400
1422
|
const visited = /* @__PURE__ */ new Set();
|
|
1401
1423
|
const cssIds = /* @__PURE__ */ new Set();
|
|
@@ -1405,8 +1427,10 @@ function vitePluginRscCss(rscCssOptions) {
|
|
|
1405
1427
|
visited.add(id);
|
|
1406
1428
|
const mod = environment.moduleGraph.getModuleById(id);
|
|
1407
1429
|
if (mod?.file) visitedFiles.add(mod.file);
|
|
1408
|
-
for (const next of mod?.importedModules ?? []) if (next.id) if (isCSSRequest(next.id))
|
|
1409
|
-
|
|
1430
|
+
for (const next of mod?.importedModules ?? []) if (next.id) if (isCSSRequest(next.id)) {
|
|
1431
|
+
if (hasSpecialCssQuery(next.id)) continue;
|
|
1432
|
+
cssIds.add(next.id);
|
|
1433
|
+
} else recurse(next.id);
|
|
1410
1434
|
}
|
|
1411
1435
|
recurse(entryId);
|
|
1412
1436
|
const hrefs = [...cssIds].map((id) => normalizeViteImportAnalysisUrl(environment, id));
|
package/dist/plugin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./dist-DiJnRA1C.js";
|
|
2
2
|
import "./plugin-CZbI4rhS.js";
|
|
3
|
-
import { __fix_cloudflare, findSourceMapURL, transformRscCssExport, vitePluginFindSourceMapURL, vitePluginRsc, vitePluginRscCss, vitePluginRscMinimal } from "./plugin-
|
|
3
|
+
import { __fix_cloudflare, findSourceMapURL, transformRscCssExport, vitePluginFindSourceMapURL, vitePluginRsc, vitePluginRscCss, vitePluginRscMinimal } from "./plugin-CzZCeIwj.js";
|
|
4
4
|
import "./encryption-utils-BDwwcMVT.js";
|
|
5
5
|
import "./rpc-tGuLT8PD.js";
|
|
6
6
|
import "./vite-utils-Vzd7cqfv.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitejs/plugin-rsc",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.21",
|
|
4
4
|
"description": "React Server Components (RSC) support for Vite.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vite",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"@playwright/test": "^1.54.2",
|
|
52
52
|
"@tsconfig/strictest": "^2.0.5",
|
|
53
53
|
"@types/estree": "^1.0.8",
|
|
54
|
-
"@types/node": "^22.17.
|
|
55
|
-
"@types/react": "^19.1.
|
|
54
|
+
"@types/node": "^22.17.2",
|
|
55
|
+
"@types/react": "^19.1.10",
|
|
56
56
|
"@types/react-dom": "^19.1.7",
|
|
57
57
|
"@vitejs/plugin-react": "workspace:*",
|
|
58
58
|
"react": "^19.1.1",
|
|
@@ -60,8 +60,7 @@
|
|
|
60
60
|
"react-server-dom-webpack": "^19.1.1",
|
|
61
61
|
"rsc-html-stream": "^0.0.7",
|
|
62
62
|
"tinyexec": "^1.0.1",
|
|
63
|
-
"tsdown": "^0.14.
|
|
64
|
-
"vite-plugin-inspect": "^11.3.2"
|
|
63
|
+
"tsdown": "^0.14.1"
|
|
65
64
|
},
|
|
66
65
|
"peerDependencies": {
|
|
67
66
|
"react": "*",
|