vite-plugin-react-server
Version:
Vite plugin for React Server Components (RSC)
91 lines (82 loc) • 3.12 kB
text/typescript
import type { CreateFromNodeStreamFn } from "./createFromNodeStream.types.js";
import { React, ReactDOMClient } from "../vendor/vendor.client.js";
import { assertNonReactServer } from "../config/getCondition.js";
assertNonReactServer();
/**
* Client version of createNodeStream.
*
* Strategy: Convert RSC stream to React elements using ReactDOMClient.createFromNodeStream.
* This is the same approach used by the HTML worker for proper CSS handling and RSC processing.
*/
export const createFromNodeStream: CreateFromNodeStreamFn<"client"> =
function _createFromNodeStreamClient(options) {
const { rscStream, logger, verbose = false } = options;
let { moduleRootPath, moduleBasePath, moduleBaseURL } = options;
if (options.children) {
if (verbose) {
logger?.info(
`[createNodeStream.client] Options already have children, skipping conversion`
);
}
return {
type: "client" as const,
children: options.children as React.ReactElement,
};
}
if (!rscStream) {
throw new Error(
"[createNodeStream.client] no rscStream nor children provided"
);
}
if (verbose) {
logger?.info(
`[createNodeStream.client] Converting RSC stream to React elements, moduleRootPath: ${moduleRootPath}, moduleBasePath: ${moduleBasePath}, moduleBaseURL: ${moduleBaseURL} (type: ${typeof moduleBaseURL})`
);
}
// Ensure moduleBaseURL is a string and not empty
// React Server DOM needs a valid base URL to resolve modules in the RSC stream
if (typeof moduleBaseURL !== "string" || !moduleBaseURL) {
if (verbose && logger) {
logger.warn(
`[createNodeStream.client] moduleBaseURL is not a valid string: ${JSON.stringify(
moduleBaseURL
)} (type: ${typeof moduleBaseURL}), defaulting to "/"`
);
}
moduleBaseURL = "/";
}
if (!moduleRootPath) {
moduleRootPath = "";
} else if (!moduleRootPath.endsWith("/")) {
moduleRootPath = `${moduleRootPath}/`;
}
if (!moduleBasePath) {
moduleBasePath = "";
} else if (!moduleBasePath.endsWith("/")) {
moduleBasePath = `${moduleBasePath}/`;
}
if (verbose) {
logger?.info(
`[createNodeStream.client] Using ReactDOMClient.createFromNodeStream from react-server-dom-esm/client.node`
);
logger?.info(
`[createNodeStream.client] rscStream type: ${typeof rscStream}, readable: ${
rscStream.readable
}, destroyed: ${rscStream.destroyed}`
);
}
// Convert RSC stream to React elements using ReactDOMClient.createFromNodeStream
// This is the same approach used by the HTML worker
// IMPORTANT: ReactDOMClient.createFromNodeStream returns a Promise that needs to be awaited
return {
type: "client" as const,
children: React.createElement(() => {
const promise = ReactDOMClient.createFromNodeStream(
rscStream,
moduleRootPath,
moduleBaseURL
);
return React.use(promise);
}),
};
};