vite-plugin-react-server
Version:
Vite plugin for React Server Components (RSC)
1,142 lines • 43.8 kB
TypeScript
import type { Readable } from "node:stream";
import type { Worker, Serializable as WorkerSerializable } from "node:worker_threads";
import type React from "react";
import type { NormalizedOutputOptions, OutputBundle, PreRenderedAsset, PreRenderedChunk } from "rollup";
import type { PassThrough, Transform } from "stream";
import type { AliasOptions, BuildOptions, Connect, Logger, Manifest, Plugin, ResolveOptions, UserConfig, ViteDevServer } from "vite";
import type { serializeResolvedConfig, serializeResolvedUserConfig } from "./helpers/serializeUserOptions.js";
import type { AllowedDirectives, Program } from "react-server-loader/directives";
import type { LoaderConfig, TransformOptions } from "./loader/types.js";
import type { RenderMetrics, StreamMetrics, WorkerStartupMetrics, ModuleResolutionMetrics } from "./metrics/types.js";
import type { HtmlWorkerOutputMessage } from "./worker/html/types.js";
import type { RscChunkOutputMessage } from "./worker/rsc/types.js";
import type { WorkerMessage } from "./worker/types.js";
import type { Strategy } from "./orchestrator/createPluginOrchestrator.server.js";
import type { RenderToPipeableStreamOptions as ClientRenderToPipeableStreamOptions } from "react-dom/server";
import type { RenderToPipeableStreamOptions as ServerRenderToPipeableStreamOptions } from "react-server-dom-esm/server.node";
export type OnEvent<Interface extends ViteReactServerComponentsPlugin = ViteReactServerComponentsPlugin> = (event: PluginEvent<Interface>) => void;
export type OnMetrics = (metrics: RenderMetrics<"rsc-full" | "rsc-headless" | "html"> | WorkerStartupMetrics | ModuleResolutionMetrics) => void;
export type MessageHandler<T extends HtmlWorkerOutputMessage | RscChunkOutputMessage = HtmlWorkerOutputMessage | RscChunkOutputMessage> = (message: T) => void | Promise<unknown>;
export type CreateInputNormalizerProps = {
root: string;
preserveModulesRoot?: string | undefined;
removeExtension?: boolean | RegExp | string | ((path: string) => boolean);
moduleBasePath: string | undefined;
moduleBaseURL: string | undefined;
};
export type Serializable = WorkerSerializable | Serializable[] | SerializableRecord;
export type SerializableRecord = {
[key: string]: Serializable;
};
export type HmrState = {
timestamp: number;
invalidated: boolean;
routes: string[];
};
export type RenderPageResult = {
type: "skip";
reason?: unknown;
html: {
abort: (reason?: unknown) => void;
pipe: <Writable extends NodeJS.WritableStream>(destination: Writable) => Writable;
};
rsc: {
abort: (reason?: unknown) => void;
pipe: <Writable extends NodeJS.WritableStream>(destination: Writable) => Writable;
};
metrics: {
rscFull: RenderMetrics & {
type: "rsc-full";
};
rscHeadless: RenderMetrics & {
type: "rsc-headless";
};
html: RenderMetrics & {
type: "html";
};
};
} | {
type: "error";
error: unknown;
metrics: {
rscFull: RenderMetrics & {
type: "rsc-full";
};
rscHeadless: RenderMetrics & {
type: "rsc-headless";
};
html: RenderMetrics & {
type: "html";
};
};
} | {
type: "success";
html: {
abort: (reason?: unknown) => void;
pipe: <Writable extends NodeJS.WritableStream>(destination: Writable) => Writable;
};
rsc: {
abort: (reason?: unknown) => void;
pipe: <Writable extends NodeJS.WritableStream>(destination: Writable) => Writable;
};
metrics: {
rscFull: RenderMetrics & {
type: "rsc-full";
};
rscHeadless: RenderMetrics & {
type: "rsc-headless";
};
html: RenderMetrics & {
type: "html";
};
};
};
export type AutoDiscoveredFiles = ResolvedBuildPages & {
workerPaths: Record<string, string>;
serverEntry: Record<string, string> | null;
clientEntry: Record<string, string>;
clientInputs: Record<string, string>;
staticInputs: Record<string, string>;
serverInputs: Record<string, string>;
serverActions: Record<string, string>;
};
export type NormalizerInput = unknown;
export type InputNormalizer = (input: NormalizerInput) => [string, string];
export type HtmlContent = {
raw: string;
transformed?: string;
assets?: string[];
};
export type PartialPageData = {
route: string;
html?: {
raw: string;
transformed?: string;
assets?: string[];
};
rsc?: {
modules: unknown[];
content: string;
};
};
export type InputNormalizerWorker = (input: NormalizerInput) => Promise<[string, string]>;
export type ResolvedUserConfig = Required<Pick<UserConfig, "root" | "mode" | "build" | "resolve">> & Omit<UserConfig, "root" | "mode" | "build" | "resolve"> & {
resolve: ResolveOptions;
} & {
build: NonNullable<Required<Pick<BuildOptions, "target" | "outDir" | "assetsDir" | "ssr" | "ssrEmitAssets" | "ssrManifest" | "manifest" | "rollupOptions" | "modulePreload">>> & Omit<BuildOptions, "target" | "outDir" | "assetsDir" | "ssr" | "ssrEmitAssets" | "ssrManifest" | "manifest">;
};
export type SerializedUserConfig<T extends ResolvedUserConfig = ResolvedUserConfig> = ReturnType<typeof serializeResolvedUserConfig<T>>;
export type SerializedUserOptions = {
[k in keyof ResolvedUserOptions]: Extract<ResolvedUserOptions[k], Serializable | Record<string, Serializable>> extends infer X ? X extends never ? undefined : X : undefined;
};
export type SerializedResolvedConfig = ReturnType<typeof serializeResolvedConfig>;
export type StreamPluginOptionsClient = {
outDir?: string;
build?: BuildConfig;
assetsDir?: string;
projectRoot?: string;
moduleBase?: string;
moduleBasePath?: string;
moduleBaseURL?: string;
clientComponents?: AliasOptions;
cssFiles?: AliasOptions;
};
export type StringKeys = "moduleBase" | "moduleBasePath" | "moduleBaseURL" | "moduleRootPath" | "projectRoot" | "pageExportName" | "propsExportName" | "htmlWorkerPath" | "rscWorkerPath" | "loaderPath" | "reactLoaderPath" | "cssLoaderPath" | "envLoaderPath" | "clientEntry" | "serverEntry" | "publicOrigin";
export type NumberKeys = "rscTimeout" | "htmlTimeout" | "htmlWorkerStartupTimeout" | "rscWorkerStartupTimeout" | "fileWriteTimeout" | "workerShutdownTimeout";
export type BooleanKeys = "verbose";
export type NestedConfigKeys = "build" | "autoDiscover" | "loader" | "css" | "pipeableStreamOptions" | "serverPipeableStreamOptions" | "clientPipeableStreamOptions";
export type EventKeys = "onMetrics" | "onEvent";
export type NormalizerKeys = "normalizer" | "moduleID";
export type ComponentKeys = "Html" | "Root";
export type SourceURLKeys = "Page" | "props";
export type OptKey = StringKeys | NumberKeys | BooleanKeys | EventKeys | NormalizerKeys | ComponentKeys | SourceURLKeys;
export type AutoDiscoverConfig = {
clientEntry: string;
serverEntry: string;
cssEntry: string;
jsonEntry: string;
htmlEntry: string;
modulePattern: RegExp;
serverPattern: RegExp;
clientPattern: RegExp;
pagePattern: RegExp;
propsPattern: RegExp;
cssPattern: RegExp;
jsonPattern: RegExp;
htmlPattern: RegExp;
cssModulePattern: RegExp;
vendorPattern: RegExp;
nodePattern: RegExp;
dotPattern: RegExp;
virtualPattern: RegExp;
rscPattern: RegExp;
};
export type GenericModuleLoader = (id: string) => Promise<Record<string, unknown>>;
export type BuildModuleLoader<Opt extends Pick<ResolvedUserOptions, "pageExportName" | "propsExportName"> = Pick<ResolvedUserOptions, "pageExportName" | "propsExportName">, T extends PagePropOpt = PagePropOpt> = <STR extends string, ID extends string = STR extends `${infer I extends string}${string}${string}` ? I : never, Special extends "?" | "#" | "" = STR extends `${string}${infer X extends "?" | "#"}${string}` ? X : "", Extra extends string = STR extends `${string}${string}${infer Y extends string}` ? Y : "">(moduleId: STR) => Promise<STR extends `${ID}${Special}${Extra}` ? `${Special}${Extra}` extends "?inline" ? {
default: string;
} : Extra extends Opt["pageExportName"] ? {
[k in Extra]: (props: T) => unknown;
} : Extra extends Opt["propsExportName"] ? {
[k in Extra]: T;
} : Extra extends string ? Special extends "#" ? {
[k in Extra]: unknown;
} : Record<string, unknown> : Record<string, unknown> : Record<string, unknown>>;
export type InterfaceAwareBuildModuleLoader<Interface extends ViteReactServerComponentsPlugin = DefaultInterface, Opt extends Pick<ResolvedUserOptions, "pageExportName" | "propsExportName"> = Pick<ResolvedUserOptions, "pageExportName" | "propsExportName">> = BuildModuleLoader<Opt, Interface["PageProps"]>;
export type StreamError = {
code?: string;
} & Error;
export type PanicThreshold = "none" | "critical_errors" | "all_errors";
export type ResolvedUserOptions = {
projectRoot: string;
moduleBase: string;
moduleBasePath: string;
moduleBaseURL: string;
moduleRootPath: string;
publicOrigin: string;
pageExportName: string;
propsExportName: string;
htmlExportName: string;
rootExportName: string;
htmlWorkerPath: string;
rscWorkerPath: string;
loaderPath: string;
reactLoaderPath?: string;
cssLoaderPath?: string;
envLoaderPath?: string;
verbose: boolean;
rscTimeout: number;
htmlTimeout: number;
htmlWorkerStartupTimeout: number;
rscWorkerStartupTimeout: number;
fileWriteTimeout: number;
workerShutdownTimeout: number;
panicThreshold: PanicThreshold;
availableEnvironments?: string[];
strategy?: Strategy;
onEvent?: OnEvent<ViteReactServerComponentsPlugin> | undefined;
props?: StreamPluginOptions["props"];
clientEntry?: string;
serverEntry?: string;
/**
* Packages whose internal `"use client"` per-file directives should be
* honored by the RSC pipeline (Chakra UI, MUI, Mantine, react-aria, …).
* When set, the matching node_modules paths bypass the transformer's
* node_modules early-return, get added to `optimizeDeps.exclude` so esbuild
* doesn't strip directives, and get added to `resolve.noExternal` so they
* land in the server bundle where the transform actually runs.
*/
clientPackages?: readonly string[];
Page: StreamPluginOptions["Page"];
Html: StreamPluginOptions["Html"];
Root: StreamPluginOptions["Root"];
normalizer: InputNormalizer;
moduleID: ((id: string, sourceContent?: string, isClientByDirective?: boolean) => string) | undefined;
onMetrics: OnMetrics | undefined;
pipeableStreamOptions: any;
serverPipeableStreamOptions?: any;
clientPipeableStreamOptions?: any;
autoDiscover: Required<AutoDiscoverConfig>;
loader?: Required<LoaderConfig> | undefined;
build: Required<BuildConfig>;
dev: Required<DevConfig>;
css: RootOptions<boolean>;
components?: StreamPluginOptions["components"];
};
export type DirectiveOptions = Pick<TransformOptions, "verbose" | "panicThreshold" | "loader"> & {
loader: Pick<Required<NonNullable<TransformOptions["loader"]>>, "isServerFunctionCode" | "isClientComponentCode" | "getDirectiveType" | "parse"> & {
allowedDirectives: AllowedDirectives;
};
logger?: Logger;
};
export type RootOptions<InlineCSS extends InlineCssOpt = InlineCssOpt> = {
inlineCss?: InlineCSS;
inlineThreshold?: number;
inlinePatterns?: RegExp[];
linkPatterns?: RegExp[];
};
export type CssContent<InlineCSS extends InlineCssOpt = InlineCssOpt> = InlineCSS extends true ? StyleCssProps : InlineCSS extends false ? LinkCssProps : InlineCSS extends undefined | boolean ? StyleCssProps | LinkCssProps : never;
/**
* Boxed component type for the Root
*/
export type CssComponentType<InlineCSS extends InlineCssOpt = InlineCssOpt, R = any> = (props: {
cssFiles: Map<string, CssContent<InlineCSS>> | CssContent[];
}) => R;
export type FileWriteEvent = {
type: "file.write";
data: {
path: string;
fileType: "html" | "rsc";
route: string;
stream: Readable;
onComplete: () => Promise<void>;
};
};
export type FileWriteDoneEvent = {
type: "file.write.done";
data: {
route: string;
fileType: "html" | "rsc";
content: string;
chunks: number;
path: string;
fileName: string;
baseDir: string;
routePath: string;
};
};
export type RouteErrorEvent = {
type: "route.error";
data: {
route: string;
error?: unknown | null;
errorInfo?: {
componentStack?: string | null;
digest?: string | null;
};
reason?: unknown;
isPanic?: boolean;
panicThreshold?: PanicThreshold;
};
};
export type RouteShellErrorEvent = {
type: "route.shellError";
data: {
route: string;
error: unknown;
};
};
export type RoutePostponeEvent = {
type: "route.postpone";
data: {
route: string;
reason?: unknown;
};
};
export type RouteShellReadyEvent = {
type: "route.shellReady";
data: {
route: string;
};
};
export type RouteAllReadyEvent = {
type: "route.allReady";
data: {
route: string;
};
};
export type PropsLoadEvent = {
type: "props.load";
data: {
route: string;
propsPath: string;
props: unknown;
};
};
export type CssProcessEvent<Interface extends ViteReactServerComponentsPlugin = DefaultInterface> = {
type: "css.process";
data: InterfaceAwareCssContent<Interface>;
};
export type BuildStartEvent = {
type: "build.start";
data: {
pages: string[];
files: AutoDiscoveredFiles;
};
};
export type BuildWriteBundleEventServer = {
type: "build.writeBundle.server";
data: {
pages: string[];
options: NormalizedOutputOptions;
bundle: OutputBundle;
};
};
export type BuildWriteBundleEventClient = {
type: "build.writeBundle.client";
data: {
pages: string[];
options: NormalizedOutputOptions;
bundle: OutputBundle;
};
};
export type BuildWriteBundleEventStatic = {
type: "build.writeBundle.static";
data: {
pages: string[];
options: NormalizedOutputOptions;
bundle: OutputBundle;
};
};
export type BuildEventStaticSiteGenerationStart = {
type: "build.ssg.start";
data: {
pages: string[];
options: NormalizedOutputOptions;
bundle: OutputBundle;
};
};
export type BuildEventStaticSiteGenerationEnd = {
type: "build.ssg.end";
data: {
pages: string[];
options: NormalizedOutputOptions;
bundle: OutputBundle;
};
};
export type BuildWriteBundleEvent = BuildWriteBundleEventServer | BuildWriteBundleEventClient | BuildEventStaticSiteGenerationStart | BuildEventStaticSiteGenerationEnd | BuildWriteBundleEventStatic;
export type PluginEvent<Interface extends ViteReactServerComponentsPlugin = ViteReactServerComponentsPlugin> = FileWriteEvent | FileWriteDoneEvent | RouteErrorEvent | RouteShellErrorEvent | RoutePostponeEvent | PropsLoadEvent | CssProcessEvent<Interface> | BuildStartEvent | BuildWriteBundleEvent | RouteShellReadyEvent | RouteAllReadyEvent;
export type PluginEventType = PluginEvent["type"];
export interface StreamPluginOptions<Interface extends ViteReactServerComponentsPlugin = DefaultInterface> {
projectRoot?: string;
moduleBase: string;
moduleBasePath?: string;
moduleBaseURL?: string;
moduleRootPath?: string;
publicOrigin?: string;
clientEntry?: string;
serverEntry?: string;
loader?: {
importServerPath?: string;
importClientPath?: string;
registerClientReferenceName?: string;
registerServerReferenceName?: string;
serverDirective?: RegExpOpt;
clientDirective?: RegExpOpt;
directivePattern?: RegExpOpt;
isServerFunctionCode?: (code: string, moduleId?: string) => boolean;
isClientComponentCode?: (code: string, moduleId?: string) => boolean;
isClientComponentByCode?: (code: string) => boolean;
isClientComponentByName?: (moduleId: string) => boolean;
moduleID?: (moduleId: string) => string;
allowedDirectives?: string[] | AllowedDirectives;
mode?: "development" | "production" | "test";
getDirectiveType?: (directive: string, moduleId?: string) => "client" | "server" | undefined;
parse?: (source: string) => Promise<{
ast: Program;
code: string;
map?: {
url: string;
start: number;
end: number;
lines: number;
} | null;
}>;
};
autoDiscover?: {
clientEntry: string;
serverEntry: string;
cssEntry: string;
jsonEntry: string;
htmlEntry: string;
/**
* Pattern to match CSS files. If not provided, uses default pattern.
* Provide either a string pattern or RegExp.
* @example ```ts
* autoDiscover: {
* // String pattern
* cssPattern: ".css$",
* // Or RegExp pattern
* cssPattern: /\.css$/
* }
*/
cssPattern?: RegExpOpt;
/**
* Pattern to match CSS module files. If not provided, uses default pattern.
* Provide either a string pattern or RegExp.
* @example ```ts
* autoDiscover: {
* // String pattern
* cssModulePattern: ".css\\.js$",
* // Or RegExp pattern
* cssModulePattern: /\.css\.js$/
* }
*/
cssModulePattern?: RegExpOpt;
/**
* Pattern to match client component files. If not provided, uses default pattern.
* Provide either a string pattern or RegExp.
* @example ```ts
* autoDiscover: {
* // String pattern
* clientPattern: ".client\\.(js|ts|jsx|tsx)$",
* // Or RegExp pattern
* clientPattern: /\.client\.(js|ts|jsx|tsx)$/
* }
*/
clientPattern?: RegExpOpt;
/**
* Pattern to match server function files. If not provided, uses default pattern.
* Provide either a string pattern or RegExp.
* @example ```ts
* autoDiscover: {
* // String pattern
* serverPattern: ".server\\.(js|ts|jsx|tsx)$",
* // Or RegExp pattern
* serverPattern: /\.server\.(js|ts|jsx|tsx)$/
* }
*/
serverPattern?: RegExpOpt;
/**
* Pattern to match HTML files. If not provided, uses default pattern.
* Provide either a string pattern or RegExp.
* @example ```ts
* autoDiscover: {
* // String pattern
* htmlPattern: ".html$",
* // Or RegExp pattern
* htmlPattern: /\.html$/
* }
*/
htmlPattern?: RegExpOpt;
/**
* Pattern to match JSON files. If not provided, uses default pattern.
* Provide either a string pattern or RegExp.
* @example ```ts
* autoDiscover: {
* // String pattern
* jsonPattern: ".json$",
* // Or RegExp pattern
* jsonPattern: /\.json$/
* }
*/
jsonPattern?: RegExpOpt;
/**
* Pattern to match module files. If not provided, uses default pattern.
* Provide either a string pattern or RegExp.
* @example ```ts
* autoDiscover: {
* // String pattern
* modulePattern: "\\.(js|ts|jsx|tsx)$",
* // Or RegExp pattern
* modulePattern: /\.(js|ts|jsx|tsx)$/
* }
*/
modulePattern?: RegExpOpt;
/**
* Pattern to match RSC files. If not provided, uses default pattern.
* Provide either a string pattern or RegExp.
* @example ```ts
* autoDiscover: {
* // String pattern
* rscPattern: ".rsc$",
* // Or RegExp pattern
* rscPattern: /\.rsc$/
* }
*/
rscPattern?: RegExpOpt;
/**
* Pattern to match page files. If not provided, uses default pattern.
* Provide either a string pattern or RegExp.
* @example ```ts
* autoDiscover: {
* // String pattern
* pagePattern: "[Pp]age\\.(js|ts|jsx|tsx)$",
* // Or RegExp pattern
* pagePattern: /[Pp]age\.(js|ts|jsx|tsx)$/
* }
*/
pagePattern?: RegExpOpt;
/**
* Pattern to match props files. If not provided, uses default pattern.
* Provide either a string pattern or RegExp.
* @example ```ts
* autoDiscover: {
* // String pattern
* propsPattern: "[Pp]rops\\.(js|ts|jsx|tsx)$",
* // Or RegExp pattern
* propsPattern: /[Pp]rops\.(js|ts|jsx|tsx)$/
* }
*/
propsPattern?: RegExpOpt;
/**
* Pattern to match dot files. No interpolation support.
* @example ```ts
* autoDiscover: {
* dotFiles: /^\.[^/]+$/
* }
*/
dotPattern?: RegExpOpt;
/**
* Pattern to match Node.js native modules. No interpolation support.
* @example ```ts
* autoDiscover: {
* nodePattern: /.node/
* }
*/
nodePattern?: RegExpOpt;
/**
* Pattern to match vendor files. No interpolation support.
* @example ```ts
* autoDiscover: {
* vendorPattern: /node_modules|_virtual/
* }
*/
vendorPattern?: RegExpOpt;
/**
* Pattern to match virtual files. No interpolation support.
* @example ```ts
* autoDiscover: {
* virtualPattern: /^virtual:/
* }
*/
virtualPattern?: RegExpOpt;
} | undefined;
Page?: UrlOpt;
props?: UrlOpt;
htmlWorkerPath?: string;
rscWorkerPath?: string;
loaderPath?: string;
reactLoaderPath?: string;
cssLoaderPath?: string;
envLoaderPath?: string;
pageExportName?: Interface["PageExportName"];
propsExportName?: Interface["PropsExportName"];
htmlExportName?: Interface["HtmlExportName"];
rootExportName?: Interface["RootExportName"];
Html?: UrlOpt;
Root?: UrlOpt;
components?: {
Html?: HtmlComponentType<Interface["PageProps"], Interface["As"], Interface["InlineCSS"], Interface["ReactType"]>;
Root?: RootComponentType<Interface["PageProps"], Interface["As"], Interface["InlineCSS"], Interface["ReactType"]>;
Page?: PageComponentType<Interface["PageProps"], Interface["ReactType"]>;
};
build?: BuildConfig;
dev?: DevConfig;
css?: RootOptions<Interface["InlineCSS"]>;
pipeableStreamOptions?: any;
serverPipeableStreamOptions?: any;
clientPipeableStreamOptions?: any;
onMetrics?: OnMetrics;
onEvent?: OnEvent<Interface>;
normalizer?: InputNormalizer;
moduleID?: (id: string, sourceContent?: string, isClientByDirective?: boolean) => string;
verbose?: boolean;
rscTimeout?: number;
htmlTimeout?: number;
htmlWorkerStartupTimeout?: number;
rscWorkerStartupTimeout?: number;
fileWriteTimeout?: number;
workerShutdownTimeout?: number;
panicThreshold?: PanicThreshold;
}
export type MultiPageHandlerOptions<Opt extends ResolvedUserOptions = ResolvedUserOptions> = Omit<CreateHandlerOptions<Opt>, "pagePath" | "route" | "cssFiles" | "propsPath" | "rootPath" | "htmlPath" | "pageProps" | "PageComponent" | "RootComponent" | "HtmlComponent">;
export type CreateHandlerOptions<Opt extends Record<string, unknown> = ResolvedUserOptions, Interface extends ViteReactServerComponentsPlugin = DefaultInterface, R = Interface["ReactType"]> = Pick<Opt, "autoDiscover" | "css" | "pageExportName" | "propsExportName" | "rootExportName" | "htmlExportName" | "moduleBase" | "moduleRootPath" | "moduleBasePath" | "moduleBaseURL" | "publicOrigin" | "onEvent" | "onMetrics" | "projectRoot" | "normalizer" | "moduleID" | "verbose" | "components" | "panicThreshold" | "projectRoot" | "rscTimeout" | "htmlTimeout" | "fileWriteTimeout" | "workerShutdownTimeout" | "rscWorkerPath" | "htmlWorkerPath"> & {
id?: string;
/** ID of headless stream to reuse for efficiency */
reuseHeadlessStreamId?: string;
/** Storage for headless stream reuse Map<id, { PageComponent: any, errored: boolean }> */
headlessStreamElements?: Map<string, {
PageComponent: any;
errored: boolean;
}>;
signal?: AbortSignal;
logger: Logger;
loader: BuildModuleLoader | GenericModuleLoader;
pagePath: string;
propsPath?: string;
rootPath?: string;
htmlPath?: string;
pageProps?: Interface["PageProps"];
PageComponent?: PageComponentType<Interface["PageProps"], R> | typeof React.Fragment;
RootComponent?: RootComponentType<Interface["PageProps"], Interface["As"], Interface["InlineCSS"], R> | typeof React.Fragment;
HtmlComponent?: HtmlComponentType<Interface["PageProps"], Interface["As"], Interface["InlineCSS"], R> | typeof React.Fragment;
route: string;
url?: string;
as?: Interface["As"];
manifest: Manifest;
staticManifest?: Manifest;
serverManifest?: Manifest;
clientManifest?: Manifest;
worker?: Worker;
rscWorker?: Worker;
htmlWorker?: Worker;
server?: ViteDevServer;
importedCss?: Set<string>;
cssFiles: Map<string, InterfaceAwareCssContent<Interface>>;
globalCss: Map<string, InterfaceAwareCssContent<Interface>>;
serverPipeableStreamOptions?: ServerRenderToPipeableStreamOptions;
clientPipeableStreamOptions?: ClientRenderToPipeableStreamOptions;
rscStream?: import("node:stream").Readable;
htmlStream?: import("node:stream").Readable;
metrics?: import("./metrics/types.js").StreamMetrics;
build: Pick<ResolvedUserOptions["build"], "outDir" | "pages" | "server" | "static" | "client" | "rscOutputPath" | "htmlOutputPath" | "assetsDir" | "preserveModulesRoot">;
dev: Pick<ResolvedUserOptions["dev"], "useHtmlWorker" | "useRscWorker">;
children?: React.ReactNode;
};
export type ResolvePageOptions = {
pagePath: string;
pageExportName: string;
url: string;
};
export type ResolvePropsOptions = {
propsPath: string;
propsExportName: string;
url: string;
};
export type StreamResult = {
type: "success";
stream: PassThrough;
assets?: {
css?: string[];
};
} | {
type: "error";
error: unknown;
} | {
type: "skip";
};
export type RouteConfig = {
path: string;
pattern?: {
page?: string;
props?: string;
};
paths?: {
page: string;
props: string;
};
};
export type BuildOutput = {
dir?: string;
rsc?: string;
ext?: string;
};
export type BuildConfig = {
useRscWorker?: boolean;
useHtmlWorker?: boolean;
pages?: string[] | (() => Promise<string[]> | string[]) | Promise<string[]>;
assetsDir?: string;
client?: string;
server?: string;
static?: string;
api?: string;
outDir?: string;
hash?: string | {
format?: "vite" | "hex" | "custom";
length?: number;
characters?: string;
prefix?: string;
suffix?: string;
};
preserveModulesRoot?: boolean;
rscOutputPath?: string;
htmlOutputPath?: string;
entryFile?: (n: PreRenderedChunk, ssr: boolean, sourceContent?: string) => string;
chunkFile?: (n: PreRenderedChunk, ssr: boolean, sourceContent?: string) => string;
assetFile?: (n: PreRenderedAsset, ssr: boolean) => string;
extensionMap?: Record<string, string>;
moduleExtension?: string;
jsExtension?: string;
cssExtension?: string;
htmlExtension?: string;
jsonExtension?: string;
rscExtension?: string;
cssModuleExtension?: string;
nodeExtension?: string;
/**
* Controls how pages are rendered during static generation.
*
* - `"parallel"` (default): Renders pages in concurrent batches for faster builds.
* Use `batchSize` to control concurrency (default: 8).
* - `"sequential"`: Renders pages one at a time. Slower but uses less memory
* and produces deterministic output order.
*/
renderMode?: "parallel" | "sequential";
/**
* Number of pages to render concurrently when `renderMode` is `"parallel"`.
* Higher values use more memory but build faster.
* @default 8
*/
batchSize?: number;
};
export type DevConfig = {
useHtmlWorker?: boolean | undefined;
useRscWorker?: boolean | undefined;
};
export type RequestHandler = Connect.NextHandleFunction;
export type RscServerModule = {
/**
* Get RSC data for a route
* @param path - Route path (e.g. "/", "/about")
* @returns Page component and props
*/
getRscData: (path: string) => Promise<{
/** Page component to render */
Page: React.ComponentType;
/** Props to pass to the page */
props: unknown;
}>;
};
export type RegisterComponentMessage = {
type: "REGISTER_COMPONENT";
id: string;
code: string;
};
export type RscBuildResult = string[];
export type ReactStreamPluginMeta = {
timing: BuildTiming;
};
export type BuildTiming = {
start: number;
configResolved?: number;
buildStart?: number;
buildEnd?: number;
renderStart?: number;
renderEnd?: number;
closeBundle?: number;
render?: number;
total?: number;
};
export type ResolvedBuildPages = {
propsMap: Map<string, string>;
pageMap: Map<string, string>;
rootMap: Map<string, string>;
htmlMap: Map<string, string>;
/**
* ## routeMap
*
* Maps props & page paths to routes
*
* @example
* const routeMap = new Map<string, string[]>();
* routeMap.set("src/page/home/page.tsx", ["/", "/home"]);
*/
routeMap: Map<string, string[]>;
/**
* ## urlMap
*
* Maps urls to props & page paths
*
* @example
* ```ts
* const urlMap = new Map<string, { props?: string; page: string }>();
* urlMap.set("/", { props: "/props", page: "/page" });
* ```
*/
urlMap: Map<string, {
props?: string;
page: string;
root?: string;
html?: string;
}>;
errors: unknown[];
};
export type ModuleId = string & {
readonly __brand: unique symbol;
};
export type PagePath = string & {
readonly __brand: unique symbol;
};
export interface DeserializedRegExp {
source: string;
flags: string;
__isRegExp: boolean;
}
export type RegExpOpt = RegExp | string | DeserializedRegExp;
export type UrlOpt = string | ((url: string) => string) | ((url: string) => Promise<string>);
export type PageName = "Page";
export type PropsName = "props";
export type HtmlName = "Html";
export type RootName = "Root";
export type HtmlProps<PageProps extends ViteReactServerComponentsPlugin["PageProps"] = DefaultInterface["PageProps"], InlineCSS extends ViteReactServerComponentsPlugin["InlineCSS"] = DefaultInterface["InlineCSS"], As extends ViteReactServerComponentsPlugin["As"] = DefaultInterface["As"], R = DefaultInterface["ReactType"]> = {
pageProps?: PageProps;
Page: PageComponentType<PageProps, R>;
route: string;
url: string;
projectRoot: string;
moduleBase: string;
moduleBaseURL: string;
moduleBasePath: string;
moduleRootPath: string;
cssFiles: Map<string, CssContent<InlineCSS>>;
manifest: Manifest;
Root: RootComponentType<PageProps, As, InlineCSS, R> | typeof React.Fragment;
globalCss: Map<string, CssContent<InlineCSS>>;
as?: As;
};
export type PageAsset = {
type: "css" | "js";
path: string;
parentUrl: string;
};
type BaseCssProps = {
as: string;
id: string;
};
export type LinkCssProps = BaseCssProps & {
as: "link";
type?: never;
children?: never;
id: string;
href: string;
rel: "stylesheet";
precedence?: string;
};
export type StyleCssProps = BaseCssProps & {
as: "style";
type: "text/css";
children?: React.ReactNode;
precedence?: never;
rel?: never;
href?: never;
};
export type CssProps<InlineCSS extends InlineCssOpt = InlineCssOpt> = {
cssFiles: Map<string, CssContent<InlineCSS>>;
};
export type HtmlRenderState = {
id: string;
rscStream: PassThrough;
htmlStream: PassThrough;
progressStream: PassThrough;
errorTransform: Transform;
htmlChunks: string[];
pipeableStreamOptions: any;
streamState: StreamMetrics;
};
export type RenderPagesResult = {
type: "error" | "success" | "skip";
error?: unknown;
route?: string;
completedRoutes: Set<string>;
failedRoutes: Map<string, unknown>;
results: Map<string, RenderPageResult>;
};
export type HandlerAssets<InlineCSS extends InlineCssOpt = InlineCssOpt> = {
css: CssContent<InlineCSS>[];
js: string[];
bootstrapModules: string[];
};
export type CreateHandlerResult<InlineCSS extends InlineCssOpt = InlineCssOpt> = {
type: "success";
controller: AbortController;
stream: PassThrough;
assets: {
css: CssContent<InlineCSS>[];
js: string[];
bootstrapModules: string[];
};
route: string;
metrics: StreamMetrics;
} | {
type: "error";
error: unknown;
} | {
type: "skip";
};
export type LoaderContext = {
format?: string;
importAttributes?: Record<string, string>;
conditions?: string[];
env?: {
targetEnvironment?: "client" | "server" | "browser";
};
url: string;
userOptions?: SerializedUserOptions;
};
declare global {
interface ImportMeta {
cssModules?: Record<string, Record<string, string>>;
}
}
export type VendorInteropConfig = {
react: string;
reactDOMServer: string;
};
export type RSCClientBrowserInteropConfig = {
createFromFetch: string;
createFromReadableStream: string;
createServerReference: string;
createTemporaryReferenceSet: string;
encodeReply: string;
registerServerReference: string;
};
export type RSCClientNodeInteropConfig = {
createFromNodeStream: string;
createServerReference: string;
registerServerReference: string;
};
export type RSCServerInteropConfig = {
createTemporaryReferenceSet: string;
decodeAction: string;
decodeFormState: string;
decodeReply: string;
decodeReplyFromBusboy: string;
registerClientReference: string;
registerServerReference: string;
renderToPipeableStream: string;
unstable_prerenderToNodeStream: string;
};
export type RSCInteropConfig = {
client: {
browser: {
production: string;
development: string;
test: string;
exports: RSCClientBrowserInteropConfig;
};
node: {
production: string;
development: string;
test: string;
exports: RSCClientNodeInteropConfig;
};
};
server: {
production: string;
development: string;
test: string;
exports: RSCServerInteropConfig;
};
};
export type FlightTarget = "default" | "webpack" | "nextjs" | "react-server-dom-esm" | "react-server-dom-webpack" | "react-server-dom-parcel";
export type FlightConfig = {
rsc: RSCInteropConfig;
vendor: VendorInteropConfig;
};
export type VitePluginMainFn = <Opt extends StreamPluginOptions<any> = StreamPluginOptions<any>>(options: Opt, strategy?: Strategy) => Plugin<Opt>[];
export type VitePluginMainAsyncFn = <Opt extends StreamPluginOptions<any> = StreamPluginOptions<any>>(options: Opt) => Promise<Plugin<Opt>[]>;
export type VitePluginFn = <Opt extends StreamPluginOptions = StreamPluginOptions>(options: Opt) => Plugin;
export type ComponentResolutionStrategy = "serializable-path" | "direct-import" | "worker-internal" | "external-reference";
export type SerializableComponentPath = string & {
readonly __brand: "SerializableComponentPath";
};
export type ComponentResolutionConfig = {
strategy: ComponentResolutionStrategy;
path?: SerializableComponentPath;
moduleId?: string;
exportName?: string;
};
export type ClientEnvironmentOptions = {
PageComponent?: never;
RootComponent?: never;
HtmlComponent?: never;
pageComponentConfig?: ComponentResolutionConfig;
rootComponentConfig?: ComponentResolutionConfig;
htmlComponentConfig?: ComponentResolutionConfig;
pagePath?: string;
rootPath?: string;
htmlPath?: string;
};
export type ServerEnvironmentOptions = {
PageComponent?: CreateHandlerOptions["PageComponent"];
RootComponent?: CreateHandlerOptions["RootComponent"];
HtmlComponent?: CreateHandlerOptions["HtmlComponent"];
pageComponentConfig?: ComponentResolutionConfig;
rootComponentConfig?: ComponentResolutionConfig;
htmlComponentConfig?: ComponentResolutionConfig;
pagePath?: string;
rootPath?: string;
htmlPath?: string;
};
export type ReactStreamCommonOptions<Env extends "client" | "server" = "client" | "server", Handles extends keyof CreateHandlerOptions = never> = Omit<CreateHandlerOptions, Handles | "PageComponent" | "RootComponent" | "HtmlComponent" | "pagePath" | "rootPath" | "htmlPath"> & Partial<Pick<CreateHandlerOptions, Handles>> & (Env extends "client" ? ClientEnvironmentOptions : ServerEnvironmentOptions);
export type ReactStreamHandlerFn<Env extends "client" | "server", Handles extends keyof CreateHandlerOptions, ReturnType> = (options: ReactStreamCommonOptions<Env, Handles>) => ReturnType;
export type RscWorkerMessage = {
pageComponentConfig?: ComponentResolutionConfig;
rootComponentConfig?: ComponentResolutionConfig;
htmlComponentConfig?: ComponentResolutionConfig;
pagePath?: string;
rootPath?: string;
htmlPath?: string;
route: string;
url: string;
pageProps?: Record<string, unknown>;
cssFiles: Array<[string, unknown]>;
manifest: Record<string, unknown>;
PageComponent?: never;
RootComponent?: never;
HtmlComponent?: never;
};
export type RscRenderOpt = WorkerMessage & {
type: "INIT";
} & {
dataPort: MessagePort;
controlPort: MessagePort;
options: Omit<CreateHandlerOptions<ResolvedUserOptions>, "onEvent" | "onMetrics" | "loader" | "build" | "autoDiscover" | "normalizer" | "moduleID" | "PageComponent" | "RootComponent" | "HtmlComponent" | "url" | "logger" | "cssFiles"> & {
url?: string;
pageComponentConfig?: ComponentResolutionConfig;
rootComponentConfig?: ComponentResolutionConfig;
htmlComponentConfig?: ComponentResolutionConfig;
cssFiles: Array<[string, unknown]>;
build: Omit<CreateHandlerOptions<ResolvedUserOptions>["build"], "entryFileNames" | "chunkFileNames" | "assetFileNames" | "pages"> & {
pages: string[];
};
};
};
export type PluginComponentReference = {
type: "plugin-component";
componentName: "Html" | "Root" | "Css";
modulePath: string;
exportName: string;
};
export type ResolvedComponent<T = unknown> = {
type: "success";
component: T;
source: "direct" | "resolved" | "plugin" | "worker-internal";
} | {
type: "error";
error: Error;
source: "direct" | "resolved" | "plugin" | "worker-internal";
};
export type WorkerComponentLoader = {
loadComponent: (config: ComponentResolutionConfig) => Promise<ResolvedComponent>;
loadPluginComponent: (reference: PluginComponentReference) => Promise<ResolvedComponent>;
hasInternalComponent: (componentName: string) => boolean;
};
export type { RenderMetrics, StreamMetrics, WorkerStartupMetrics, ModuleResolutionMetrics, };
export type InferReactType<R = React.ReactNode> = R;
export type CustomInterface<R = React.ReactNode> = {
ReactType: R;
PageProps: any;
As: any;
PropsExportName: string;
PageExportName: string;
RootExportName: string;
HtmlExportName: string;
InlineCSS: undefined | boolean;
};
declare global {
interface ViteReactServerComponentsPlugin {
PageProps: any;
As: any;
InlineCSS: undefined | boolean;
ReactType: InferReactType;
PropsExportName: string;
PageExportName: string;
RootExportName: string;
HtmlExportName: string;
}
}
export interface DefaultInterface<T extends React.ComponentProps<any> = React.ComponentProps<any>, As extends React.JSXElementConstructor<any> | keyof React.JSX.IntrinsicElements = React.JSXElementConstructor<any> | keyof React.JSX.IntrinsicElements> extends ViteReactServerComponentsPlugin {
ReactType: InferReactType;
PageProps: T;
As: As;
PropsExportName: PropsName;
PageExportName: PageName;
RootExportName: RootName;
HtmlExportName: HtmlName;
}
export type PagePropOpt = DefaultInterface["PageProps"];
export type AsOpt = DefaultInterface["As"];
export type InlineCssOpt = DefaultInterface["InlineCSS"];
export type PageComponentType<PageProps extends ViteReactServerComponentsPlugin["PageProps"] = DefaultInterface["PageProps"], R = DefaultInterface["ReactType"]> = (props: PageProps) => R;
export type RootComponentType<PageProps extends ViteReactServerComponentsPlugin["PageProps"] = DefaultInterface["PageProps"], As extends ViteReactServerComponentsPlugin["As"] = DefaultInterface["As"], InlineCSS extends ViteReactServerComponentsPlugin["InlineCSS"] = DefaultInterface["InlineCSS"], R = DefaultInterface["ReactType"]> = (props: RootProps<PageProps, InlineCSS, As, R>) => R;
export type RootProps<PageProps extends ViteReactServerComponentsPlugin["PageProps"] = DefaultInterface["PageProps"], InlineCSS extends ViteReactServerComponentsPlugin["InlineCSS"] = DefaultInterface["InlineCSS"], As extends ViteReactServerComponentsPlugin["As"] = DefaultInterface["As"], R = DefaultInterface["ReactType"]> = {
as: As;
cssFiles?: Map<string, CssContent<InlineCSS>>;
pageProps?: Omit<PageProps, "children">;
Page: PageComponentType<Omit<PageProps, "children">, R>;
id?: string;
};
/**
* Boxed component type for the Html component
*/
export type HtmlComponentType<T extends ViteReactServerComponentsPlugin["PageProps"] = DefaultInterface["PageProps"], As extends ViteReactServerComponentsPlugin["As"] = DefaultInterface["As"], InlineCSS extends ViteReactServerComponentsPlugin["InlineCSS"] = DefaultInterface["InlineCSS"], R = DefaultInterface["ReactType"]> = (props: HtmlProps<T, InlineCSS, As, R>) => R;
export type InterfaceAwareCssContent<Interface extends ViteReactServerComponentsPlugin> = CssContent<Interface["InlineCSS"]>;
export type InterfaceAwareRootOptions<Interface extends ViteReactServerComponentsPlugin> = RootOptions<Interface["InlineCSS"]>;
export type InterfaceAwareCssComponentType<Interface extends ViteReactServerComponentsPlugin, R = Interface["ReactType"]> = CssComponentType<Interface["InlineCSS"], R>;
export type InterfaceAwareCssProps<Interface extends ViteReactServerComponentsPlugin> = CssProps<Interface["InlineCSS"]>;
export type InterfaceAwareHandlerAssets<Interface extends ViteReactServerComponentsPlugin> = HandlerAssets<Interface["InlineCSS"]>;
export type InterfaceAwareCreateHandlerResult<Interface extends ViteReactServerComponentsPlugin> = CreateHandlerResult<Interface["InlineCSS"]>;
export type VitePluginReactClientFn = <Opt extends StreamPluginOptions = StreamPluginOptions>(options: Opt) => Plugin[];
export type VitePluginReactServerFn = <Opt extends StreamPluginOptions = StreamPluginOptions>(options: Opt) => Plugin[];
//# sourceMappingURL=types.d.ts.map