Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion packages/plugin-rsc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,9 @@ The plugin provides an additional helper for multi environment interaction.

This allows importing `ssr` environment module specified by `environments.ssr.build.rollupOptions.input[entryName]` inside `rsc` environment and vice versa.

During development, by default, this API assumes both `rsc` and `ssr` environments execute under the main Vite process. When enabling `rsc({ loadModuleDevProxy: true })` plugin option, the loaded module is implemented as a proxy with `fetch`-based RPC to call in node environment on the main Vite process, which for example, allows `rsc` environment inside cloudflare workers to access `ssr` environment on the main Vite process. This proxy mechanism uses [turbo-stream](https://github.com/jacob-ebey/turbo-stream) for serializing data types beyond JSON, with custom encoders/decoders to additionally support `Request` and `Response` instances.
During development, by default, this API assumes both `rsc` and `ssr` environments execute under the main Vite process as `RunnableDevEnvironment`. Internally, `loadModule` uses the global `__VITE_ENVIRONMENT_RUNNER_IMPORT__` function to import modules in the target environment (see [`__VITE_ENVIRONMENT_RUNNER_IMPORT__`](#__vite_environment_runner_import__) below).

When enabling `rsc({ loadModuleDevProxy: true })` plugin option, the loaded module is implemented as a proxy with `fetch`-based RPC to call in node environment on the main Vite process, which for example, allows `rsc` environment inside cloudflare workers to access `ssr` environment on the main Vite process. This proxy mechanism uses [turbo-stream](https://github.com/jacob-ebey/turbo-stream) for serializing data types beyond JSON, with custom encoders/decoders to additionally support `Request` and `Response` instances.

During production build, this API will be rewritten into a static import of the specified entry of other environment build and the modules are executed inside the same runtime.

Expand Down Expand Up @@ -327,6 +329,35 @@ import.meta.hot.on('rsc:update', async () => {
})
```

### Global API

#### `__VITE_ENVIRONMENT_RUNNER_IMPORT__`

- Type: `(environmentName: string, id: string) => Promise<any>`

This global function provides a standardized way to import a module in a given environment during development. It is used internally by `import.meta.viteRsc.loadModule` to execute modules in the target environment.

By default, the plugin sets this global to import via the environment's module runner:

```js
globalThis.__VITE_ENVIRONMENT_RUNNER_IMPORT__ = async (environmentName, id) => {
return server.environments[environmentName].runner.import(id)
}
```

**Custom Environment Integration:**

Frameworks with custom environment setups (e.g., environments running in separate workers or with custom module loading) can override this global to provide their own module import logic.

```js
// Custom logic to import module between multiple environments inside worker
globalThis.__VITE_ENVIRONMENT_RUNNER_IMPORT__ = async (environmentName, id) => {
return myWorkerRunners[environmentname].import(id)
}
```

This allows `import.meta.viteRsc.loadModule` to work seamlessly with different runtime configurations without requiring changes to user code.

## Plugin API

### `@vitejs/plugin-rsc`
Expand Down
30 changes: 25 additions & 5 deletions packages/plugin-rsc/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,13 @@ export function vitePluginRscMinimal(
]
}

declare global {
function __VITE_ENVIRONMENT_RUNNER_IMPORT__(
environmentName: string,
id: string,
): Promise<any>
}

export default function vitePluginRsc(
rscPluginOptions: RscPluginOptions = {},
): Plugin[] {
Expand Down Expand Up @@ -516,7 +523,23 @@ export default function vitePluginRsc(
},
},
configureServer(server) {
;(globalThis as any).__viteRscDevServer = server
globalThis.__VITE_ENVIRONMENT_RUNNER_IMPORT__ = async function (
environmentName,
id,
) {
const environment = server.environments[environmentName]
if (!environment) {
throw new Error(
`[vite-rsc] unknown environment '${environmentName}'`,
)
}
if (!vite.isRunnableDevEnvironment(environment)) {
throw new Error(
`[vite-rsc] environment '${environmentName}' is not runnable`,
)
}
return environment.runner.import(id)
}

// intercept client hmr to propagate client boundary invalidation to server environment
const oldSend = server.environments.client.hot.send
Expand Down Expand Up @@ -768,10 +791,7 @@ export default function vitePluginRsc(
const source = getEntrySource(environment.config, entryName)
const resolved = await environment.pluginContainer.resolveId(source)
assert(resolved, `[vite-rsc] failed to resolve entry '${source}'`)
replacement =
`globalThis.__viteRscDevServer.environments[${JSON.stringify(
environmentName,
)}]` + `.runner.import(${JSON.stringify(resolved.id)})`
replacement = `globalThis.__VITE_ENVIRONMENT_RUNNER_IMPORT__(${JSON.stringify(environmentName)}, ${JSON.stringify(resolved.id)})`
} else {
replacement = JSON.stringify(
`__vite_rsc_load_module:${this.environment.name}:${environmentName}:${entryName}`,
Expand Down
Loading