Skip to content

Commit b0e7a1f

Browse files
hi-ogawaclaude
andauthored
feat(rsc): add import.meta.viteRsc.import API (#1063)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent f9e79eb commit b0e7a1f

File tree

18 files changed

+1310
-37
lines changed

18 files changed

+1310
-37
lines changed

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ This monorepo contains multiple packages (see [README.md](README.md#packages) fo
1313

1414
- `packages/plugin-react/` - Main React plugin with Babel
1515
- `packages/plugin-react-swc/` - SWC-based React plugin
16-
- `packages/plugin-rsc/` - React Server Components ([AI guidance](packages/plugin-rsc/AGENTS.md))
16+
- `packages/plugin-rsc/` - React Server Components ([AI guidance](packages/plugin-rsc/AGENTS.md), [architecture](packages/plugin-rsc/docs/architecture.md))
1717
- `packages/plugin-react-oxc/` - Deprecated (merged with plugin-react)
1818

1919
### Essential Setup Commands

packages/plugin-rsc/AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ This document provides AI-agent-specific guidance for the React Server Component
44

55
- **[README.md](README.md)** - Plugin overview, concepts, and examples
66
- **[CONTRIBUTING.md](CONTRIBUTING.md)** - Development setup and testing guidelines
7+
- **[docs/architecture.md](docs/architecture.md)** - Build pipeline, data flow, and key components
8+
- **[docs/bundler-comparison.md](docs/bundler-comparison.md)** - How different bundlers approach RSC
79

810
## Quick Reference for AI Agents
911

packages/plugin-rsc/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,33 @@ ssrModule.renderHTML(...);
245245
export function renderHTML(...) {}
246246
```
247247

248+
#### `import.meta.viteRsc.import`
249+
250+
- Type: `<T>(specifier: string, options: { environment: string }) => Promise<T>`
251+
252+
A more ergonomic alternative to `loadModule`:
253+
254+
1. No manual `rollupOptions.input` config needed - entries are auto-discovered
255+
2. Specifier matches the path in `typeof import(...)` type annotations
256+
257+
**Comparison:**
258+
259+
```ts
260+
// Before (loadModule) - requires vite.config.ts:
261+
// environments.ssr.build.rollupOptions.input = { index: './entry.ssr.tsx' }
262+
import.meta.viteRsc.loadModule<typeof import('./entry.ssr.tsx')>('ssr', 'index')
263+
264+
// After (import) - no config needed, auto-discovered
265+
import.meta.viteRsc.import<typeof import('./entry.ssr.tsx')>(
266+
'./entry.ssr.tsx',
267+
{ environment: 'ssr' },
268+
)
269+
```
270+
271+
During development, this works the same as `loadModule`, using the `__VITE_ENVIRONMENT_RUNNER_IMPORT__` function to import modules in the target environment.
272+
273+
During production build, the plugin auto-discovers these imports and emits them as entries in the target environment. A manifest file (`__vite_rsc_env_imports_manifest.js`) is generated to map module specifiers to their output filenames.
274+
248275
### Available on `rsc` environment
249276

250277
#### `import.meta.viteRsc.loadCss`
@@ -616,6 +643,13 @@ Note that while there are official npm packages [`server-only`](https://www.npmj
616643
617644
This build-time validation is enabled by default and can be disabled by setting `validateImports: false` in the plugin options.
618645
646+
## Architecture Documentation
647+
648+
For developers interested in the internal architecture:
649+
650+
- **[docs/architecture.md](docs/architecture.md)** - Build pipeline, data flow, and key components
651+
- **[docs/bundler-comparison.md](docs/bundler-comparison.md)** - How different bundlers approach RSC
652+
619653
## Credits
620654
621655
This project builds on fundamental techniques and insights from pioneering Vite RSC implementations.
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# RSC Plugin Architecture
2+
3+
## Overview
4+
5+
The `@vitejs/plugin-rsc` implements React Server Components using Vite's multi-environment architecture. Each environment (rsc, ssr, client) has its own module graph, requiring a multi-pass build strategy.
6+
7+
## Build Pipeline
8+
9+
### With SSR (5-step)
10+
11+
```
12+
rsc (scan) → ssr (scan) → rsc (real) → client → ssr (real)
13+
```
14+
15+
| Step | Phase | Write to Disk | Purpose |
16+
| ---- | -------- | ------------- | ---------------------------------------------------------------------------- |
17+
| 1 | RSC scan | No | Discover `"use client"` boundaries → `clientReferenceMetaMap` |
18+
| 2 | SSR scan | No | Discover `"use server"` boundaries → `serverReferenceMetaMap` |
19+
| 3 | RSC real | Yes | Build server components, populate `renderedExports`, `serverChunk` |
20+
| 4 | Client | Yes | Build client bundle using reference metadata, generate `buildAssetsManifest` |
21+
| 5 | SSR real | Yes | Final SSR build with complete manifests |
22+
23+
### Without SSR (4-step)
24+
25+
```
26+
rsc (scan) → client (scan) → rsc (real) → client (real)
27+
```
28+
29+
## Why This Build Order?
30+
31+
1. **RSC scan first**: Must discover `"use client"` boundaries before client build knows what to bundle
32+
2. **SSR scan second**: Must discover `"use server"` boundaries for proxy generation in both client and SSR
33+
3. **RSC real third**: Generates proxy modules, determines which exports are actually used (`renderedExports`)
34+
4. **Client fourth**: Needs RSC's `renderedExports` to tree-shake unused client components
35+
5. **SSR last**: Needs complete client manifest for SSR hydration
36+
37+
### Critical Dependency: RSC → SSR Scan
38+
39+
The SSR scan **depends on RSC scan output**. This prevents parallelization:
40+
41+
1. SSR entry imports `@vitejs/plugin-rsc/ssr`
42+
2. `ssr.tsx` imports `virtual:vite-rsc/client-references`
43+
3. This virtual module reads `clientReferenceMetaMap` (populated during RSC scan)
44+
4. Client components may import `"use server"` files
45+
5. SSR scan processes those imports, populating `serverReferenceMetaMap`
46+
47+
## Data Flow
48+
49+
```
50+
┌─────────────────────────────────────────────────────────────┐
51+
│ RSC Scan Build │
52+
│ Writes: clientReferenceMetaMap (importId, exportNames) │
53+
│ Writes: serverReferenceMetaMap (for "use server" in RSC) │
54+
└──────────────────────────┬──────────────────────────────────┘
55+
56+
┌─────────────────────────────────────────────────────────────┐
57+
│ SSR Scan Build │
58+
│ Writes: serverReferenceMetaMap (for "use server" in SSR) │
59+
└──────────────────────────┬──────────────────────────────────┘
60+
61+
┌─────────────────────────────────────────────────────────────┐
62+
│ RSC Real Build │
63+
│ Reads: clientReferenceMetaMap │
64+
│ Mutates: renderedExports, serverChunk on each meta │
65+
│ Outputs: rscBundle │
66+
└──────────────────────────┬──────────────────────────────────┘
67+
68+
manager.stabilize()
69+
(sorts clientReferenceMetaMap)
70+
71+
┌─────────────────────────────────────────────────────────────┐
72+
│ Client Build │
73+
│ Reads: clientReferenceMetaMap (with renderedExports) │
74+
│ Uses: clientReferenceGroups for chunking │
75+
│ Outputs: buildAssetsManifest, copies RSC assets │
76+
└──────────────────────────┬──────────────────────────────────┘
77+
78+
┌─────────────────────────────────────────────────────────────┐
79+
│ SSR Real Build │
80+
│ Reads: serverReferenceMetaMap │
81+
│ Final output with assets manifest │
82+
└─────────────────────────────────────────────────────────────┘
83+
```
84+
85+
## Key Components
86+
87+
### RscPluginManager
88+
89+
Central state manager shared across all build phases:
90+
91+
```typescript
92+
class RscPluginManager {
93+
server: ViteDevServer
94+
config: ResolvedConfig
95+
rscBundle: Rollup.OutputBundle
96+
buildAssetsManifest: AssetsManifest | undefined
97+
isScanBuild: boolean = false
98+
99+
// Reference tracking
100+
clientReferenceMetaMap: Record<string, ClientReferenceMeta> = {}
101+
clientReferenceGroups: Record<string, ClientReferenceMeta[]> = {}
102+
serverReferenceMetaMap: Record<string, ServerReferenceMeta> = {}
103+
serverResourcesMetaMap: Record<string, { key: string }> = {}
104+
}
105+
```
106+
107+
### Client Reference Discovery
108+
109+
When RSC transform encounters `"use client"`:
110+
111+
1. Parse exports from the module
112+
2. Generate a unique `referenceKey` (hash of module ID)
113+
3. Store in `clientReferenceMetaMap`:
114+
- `importId`: Module ID for importing
115+
- `referenceKey`: Unique identifier
116+
- `exportNames`: List of exports
117+
- `renderedExports`: Exports actually used (populated during real build)
118+
- `serverChunk`: Which RSC chunk imports this (for grouping)
119+
120+
### Server Reference Discovery
121+
122+
When transform encounters `"use server"`:
123+
124+
1. Parse exported functions
125+
2. Generate reference IDs
126+
3. Store in `serverReferenceMetaMap`
127+
4. Generate proxy module that calls server via RPC
128+
129+
### Virtual Modules
130+
131+
Key virtual modules used in the build:
132+
133+
| Virtual Module | Purpose |
134+
| ------------------------------------------------- | ----------------------------------------------- |
135+
| `virtual:vite-rsc/client-references` | Entry point importing all client components |
136+
| `virtual:vite-rsc/client-references/group/{name}` | Grouped client components for code splitting |
137+
| `virtual:vite-rsc/assets-manifest` | Client asset manifest for SSR |
138+
| `virtual:vite-rsc/rpc-client` | Dev-mode RPC client for cross-environment calls |
139+
140+
### Cross-Environment Module Loading
141+
142+
`import.meta.viteRsc.loadModule(environment, entryName)` enables loading modules from other environments:
143+
144+
**Dev mode:**
145+
146+
```typescript
147+
globalThis.__VITE_ENVIRONMENT_RUNNER_IMPORT__(environmentName, resolvedId)
148+
```
149+
150+
**Build mode:**
151+
152+
- Emits marker during transform
153+
- `renderChunk` resolves to relative import path between output directories
154+
155+
## Key Code Locations
156+
157+
| Component | Location |
158+
| ----------------------------- | -------------------------- |
159+
| Manager definition | `src/plugin.ts:112-148` |
160+
| Build orchestration | `src/plugin.ts:343-429` |
161+
| clientReferenceMetaMap writes | `src/plugin.ts:1386` |
162+
| serverReferenceMetaMap writes | `src/plugin.ts:1817, 1862` |
163+
| Scan strip plugin | `src/plugins/scan.ts` |
164+
| Cross-env module loading | `src/plugin.ts:824-916` |
165+
166+
## Virtual Module Resolution
167+
168+
Virtual modules with `\0` prefix need special handling:
169+
170+
1. Vite convention: `\0` prefix marks virtual modules
171+
2. When used as import specifiers, `\0` must be stripped
172+
3. CSS requests get `?direct` query added by Vite
173+
4. The `resolved-id-proxy` plugin handles query stripping
174+
175+
See `src/plugins/resolved-id-proxy.ts` for implementation.

0 commit comments

Comments
 (0)