diff --git a/packages/plugin-rsc/src/react/rsc.node.ts b/packages/plugin-rsc/src/react/rsc.node.ts new file mode 100644 index 000000000..3ba5ebba7 --- /dev/null +++ b/packages/plugin-rsc/src/react/rsc.node.ts @@ -0,0 +1,25 @@ +// @ts-ignore +import * as ReactServerNode from '@vitejs/plugin-rsc/vendor/react-server-dom/server.node' +import { createClientManifest } from '../core/rsc' +import type { PipeableStream, RenderToPipeableStreamOptions } from '../types' + +export * from './rsc' + +export function renderToPipeableStream( + data: T, + options?: RenderToPipeableStreamOptions, + extraOptions?: { + /** + * @internal + */ + onClientReference?: (metadata: { id: string; name: string }) => void + }, +): PipeableStream { + return ReactServerNode.renderToPipeableStream( + data, + createClientManifest({ + onClientReference: extraOptions?.onClientReference, + }), + options, + ) +} diff --git a/packages/plugin-rsc/src/react/ssr.node.ts b/packages/plugin-rsc/src/react/ssr.node.ts new file mode 100644 index 000000000..90e71abe6 --- /dev/null +++ b/packages/plugin-rsc/src/react/ssr.node.ts @@ -0,0 +1,16 @@ +// @ts-ignore +import * as ReactClientNode from '@vitejs/plugin-rsc/vendor/react-server-dom/client.node' +import { createServerConsumerManifest } from '../core/ssr' +import type { CreateFromNodeStreamOptions } from '../types' + +export * from './ssr' + +export function createFromNodeStream( + stream: import('node:stream').Readable, + options: CreateFromNodeStreamOptions = {}, +): Promise { + return ReactClientNode.createFromNodeStream(stream, { + serverConsumerManifest: createServerConsumerManifest(), + ...options, + }) +} diff --git a/packages/plugin-rsc/src/rsc.node.tsx b/packages/plugin-rsc/src/rsc.node.tsx new file mode 100644 index 000000000..4e81ed601 --- /dev/null +++ b/packages/plugin-rsc/src/rsc.node.tsx @@ -0,0 +1,35 @@ +import assetsManifest from 'virtual:vite-rsc/assets-manifest' +import type { ResolvedAssetDeps } from './plugin' +import { renderToPipeableStream as originalRenderToPipeableStream } from './react/rsc.node' +import type { PipeableStream } from './types' + +export * from './rsc' + +export function renderToPipeableStream( + data: T, + options?: object, + extraOptions?: { + /** + * @experimental + */ + onClientReference?: (metadata: { + id: string + name: string + deps: ResolvedAssetDeps + }) => void + }, +): PipeableStream { + return originalRenderToPipeableStream(data, options, { + onClientReference(metadata) { + const deps = assetsManifest.clientReferenceDeps[metadata.id] ?? { + js: [], + css: [], + } + extraOptions?.onClientReference?.({ + id: metadata.id, + name: metadata.name, + deps, + }) + }, + }) +} diff --git a/packages/plugin-rsc/src/ssr.node.tsx b/packages/plugin-rsc/src/ssr.node.tsx new file mode 100644 index 000000000..d4a1dfe29 --- /dev/null +++ b/packages/plugin-rsc/src/ssr.node.tsx @@ -0,0 +1,3 @@ +export * from './ssr' + +export { createFromNodeStream } from './react/ssr.node' diff --git a/packages/plugin-rsc/src/types/index.ts b/packages/plugin-rsc/src/types/index.ts index bb827a7b4..a723d7539 100644 --- a/packages/plugin-rsc/src/types/index.ts +++ b/packages/plugin-rsc/src/types/index.ts @@ -93,6 +93,15 @@ type DebugChannel = { writable?: WritableStream } +export interface PipeableStream { + pipe(destination: T): T + abort(reason?: unknown): void +} + +export type RenderToPipeableStreamOptions = RenderToReadableStreamOptions + +export type CreateFromNodeStreamOptions = CreateFromReadableStreamEdgeOptions + // TODO: for now keep them unknown // export type ServerTemporaryReferenceSet = WeakMap // export type ClientTemporaryReferenceSet = Map diff --git a/packages/plugin-rsc/tsdown.config.ts b/packages/plugin-rsc/tsdown.config.ts index d226dd01d..f51b16f80 100644 --- a/packages/plugin-rsc/tsdown.config.ts +++ b/packages/plugin-rsc/tsdown.config.ts @@ -7,14 +7,18 @@ export default defineConfig({ 'src/plugin.ts', 'src/browser.ts', 'src/ssr.tsx', + 'src/ssr.node.tsx', 'src/rsc.tsx', + 'src/rsc.node.tsx', 'src/core/browser.ts', 'src/core/ssr.ts', 'src/core/rsc.ts', 'src/core/plugin.ts', 'src/react/browser.ts', 'src/react/ssr.ts', + 'src/react/ssr.node.ts', 'src/react/rsc.ts', + 'src/react/rsc.node.ts', 'src/transforms/index.ts', 'src/plugins/cjs.ts', 'src/utils/rpc.ts',