diff --git a/packages/plugin-rsc/README.md b/packages/plugin-rsc/README.md index 0628f2ad5..f7e6af5d0 100644 --- a/packages/plugin-rsc/README.md +++ b/packages/plugin-rsc/README.md @@ -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. @@ -327,6 +329,35 @@ import.meta.hot.on('rsc:update', async () => { }) ``` +### Global API + +#### `__VITE_ENVIRONMENT_RUNNER_IMPORT__` + +- Type: `(environmentName: string, id: string) => Promise` + +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` diff --git a/packages/plugin-rsc/src/plugin.ts b/packages/plugin-rsc/src/plugin.ts index dc46a90f6..c3ec2baac 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -301,6 +301,13 @@ export function vitePluginRscMinimal( ] } +declare global { + function __VITE_ENVIRONMENT_RUNNER_IMPORT__( + environmentName: string, + id: string, + ): Promise +} + export default function vitePluginRsc( rscPluginOptions: RscPluginOptions = {}, ): Plugin[] { @@ -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 @@ -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}`,