vite-plugin-react-server
Version:
Vite plugin for React Server Components (RSC)
110 lines (108 loc) • 2.69 kB
text/typescript
import * as React from "react";
// @ts-ignore
import { renderToPipeableStream } from "react-server-dom-esm/server.node";
import type { PipeableStreamOptions } from "../worker/types.js";
import type { Logger } from "vite";
import type {
CreateHandlerOptions,
CssCollectorProps,
CssContent,
InlineCssCollectorProps,
} from "../types.js";
export function createRscStream<InlineCSS extends boolean = true>({
Html,
Page,
props,
loader = (id) => import(id).then((m) => m.default),
moduleRootPath,
moduleBasePath,
moduleBaseURL,
logger,
cssFiles = [],
route,
url,
pipableStreamOptions,
htmlProps,
inlineCss = true as InlineCSS,
CssCollector,
root,
}: {
Html: CreateHandlerOptions["Html"];
Page: React.ComponentType<any>;
loader: (id: string) => Promise<any>;
props: any;
moduleBase: string;
moduleRootPath: string;
moduleBasePath: string;
moduleBaseURL: string;
logger: Logger;
route: string;
url: string;
pipableStreamOptions?: PipeableStreamOptions;
htmlProps?: any;
root: string;
inlineCss?: InlineCSS;
cssFiles?: (string | CssContent)[];
} & (InlineCSS extends true
? {
CssCollector: React.FC<InlineCssCollectorProps>;
}
: {
CssCollector: React.FC<CssCollectorProps>;
})) {
const htmlIsFragment = Html == React.Fragment;
if (!htmlIsFragment) {
if (!htmlProps) {
htmlProps = {};
}
if (!("moduleBaseURL" in htmlProps)) {
htmlProps["moduleBaseURL"] = moduleBaseURL;
}
if (!("moduleBasePath" in htmlProps)) {
htmlProps["moduleBasePath"] = moduleBasePath;
}
if (!("moduleRootPath" in htmlProps)) {
htmlProps["moduleRootPath"] = moduleRootPath;
}
if (!("url" in htmlProps)) {
htmlProps["url"] = url;
}
if (!("route" in htmlProps)) {
htmlProps["route"] = route;
}
if (!("pageProps" in htmlProps)) {
htmlProps["pageProps"] = props;
}
}
const withCss = React.createElement(
CssCollector as any,
(inlineCss === true
? {
cssFiles: cssFiles,
route,
moduleBaseURL,
moduleBasePath,
moduleRootPath,
root,
loader,
}
: { cssFiles: cssFiles, route, moduleBaseURL }) as any,
React.createElement(Page, props)
);
// Otherwise wrap with Html component
const content = htmlIsFragment
? withCss
: React.createElement(Html, htmlProps, withCss);
try {
return renderToPipeableStream(
content,
moduleBasePath,
pipableStreamOptions
);
} catch (error) {
logger.error(`Failed to create stream for ${route}.`, {
error: error as Error,
});
return null;
}
}