@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 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-less RSC experience**: The plugin implements [RSC conventions](https://react.dev/reference/rsc/server-components) and provides low level `react-server-dom` runtime API without framework-specific abstractions.
8
- - **CSS support**: CSS is automatically code-split both at client and server components and they are injected upon rendering.
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
- - **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)).
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
- - [`./examples/starter`](./examples/starter)
23
- - This example provides an in-depth overview of API with inline comments to explain how they function within RSC-powered React application.
24
- - [`./examples/react-router`](./examples/react-router)
25
- - This 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.
26
- - [`./examples/basic`](./examples/basic)
27
- - This is mainly used for e2e testing and include various advanced RSC usages (e.g. `"use cache"` example).
28
- - [`./examples/ssg`](./examples/ssg)
29
- - Static site generation (SSG) example with MDX and client components for interactivity.
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 * as ReactServer from '@vitejs/plugin-rsc/rsc' // re-export of react-server-dom/server.edge and client.edge
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
- // serialization React VDOM to RSC stream
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 = ReactServer.renderToReadableStream(root)
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 * as ReactClient from '@vitejs/plugin-rsc/ssr' // re-export of react-server-dom/client.edge
174
- import * as ReactDOMServer from 'react-dom/server.edge'
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 ReactClient.createFromReadableStream(rscStream)
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 = ReactDOMServer.renderToReadableStream(root, {
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 * as ReactClient from "@vitejs/plugin-rsc/browser"; // re-export of react-server-dom/client.browser
197
- import * as ReactDOMClient from "react-dom/client";
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 + ".rsc);
202
- const root = await ReactClient.createFromReadableStream(rscResponse.body);
204
+ const rscResponse = await fetch(window.location.href + '.rsc')
205
+ const root = await createFromReadableStream(rscResponse.body)
203
206
 
204
207
  // hydration (traditional CSR)
205
- ReactDOMClient.hydrateRoot(document, root);
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
- ### available on `rsc` or `ssr` environment
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
- ### available on `rsc` environment
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
- Where specifying `loadCss(<id>)`, it will collect css through the server module resolved by `<id>`.
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.css?vite-rsc-css-export=Page
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
- ### available on `ssr` environment
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
- ### available on `client` environment
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 * as ReactClient from '@vitejs/plugin-rsc/browser'
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 ReactClient.createFromFetch(
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-DYOxE_xg.js";
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 [imports] = esModuleLexer.parse(code);
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)) cssIds.add(next.id);
1409
- else recurse(next.id);
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-DYOxE_xg.js";
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.20",
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.1",
55
- "@types/react": "^19.1.9",
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.0",
64
- "vite-plugin-inspect": "^11.3.2"
63
+ "tsdown": "^0.14.1"
65
64
  },
66
65
  "peerDependencies": {
67
66
  "react": "*",