Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/clean-csr-hydration-runtime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@modern-js/runtime': patch
---

fix(runtime): exclude the SSR hydration runtime from CSR bundles
5 changes: 5 additions & 0 deletions .changeset/clean-rsc-web-runtime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@modern-js/runtime': patch
---

fix(runtime): exclude the RSC client runtime from web bundles when `server.rsc` is disabled
6 changes: 6 additions & 0 deletions packages/runtime/plugin-runtime/src/cli/ssr/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ const ssrBuilderPlugin = (
? JSON.stringify('node')
: JSON.stringify('browser'),
'process.env.MODERN_SSR_ENV': JSON.stringify(ssrEnv),
'process.env.MODERN_ENABLE_HYDRATION': JSON.stringify(
isUseSSRBundle(userConfig),
),
'process.env.MODERN_ENABLE_RSC': JSON.stringify(
Boolean(userConfig.server?.rsc),
),
},
},
output: {
Expand Down
13 changes: 11 additions & 2 deletions packages/runtime/plugin-runtime/src/core/browser/hydrate.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { loadableReady } from '@loadable/component';
import { normalizePathname } from '@modern-js/runtime-utils/url';
import { SSR_HYDRATION_ID_PREFIX } from '@modern-js/utils/universal/constants';
import type React from 'react';
import type { Root } from 'react-dom/client';
import { type Root, hydrateRoot as hydrateReactRoot } from 'react-dom/client';
import { RenderLevel } from '../constants';
import type { TRuntimeContext } from '../context/runtime';
import { wrapRuntimeContextProvider } from '../react/wrapper';
import { WithCallback } from './withCallback';

export async function hydrateWithReact(
App: React.ReactElement,
rootElement: HTMLElement,
) {
return hydrateReactRoot(rootElement, App, {
identifierPrefix: SSR_HYDRATION_ID_PREFIX,
});
}

export function hydrateRoot(
App: React.ReactElement,
context: TRuntimeContext,
Expand Down
28 changes: 9 additions & 19 deletions packages/runtime/plugin-runtime/src/core/browser/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { SSR_HYDRATION_ID_PREFIX } from '@modern-js/utils/universal/constants';
import cookieTool from 'cookie';
import type React from 'react';
// aliased because this file already has Modern's own `hydrateRoot` from './hydrate'
import { createRoot, hydrateRoot as hydrateReactRoot } from 'react-dom/client';
import { createRoot } from 'react-dom/client';
import { getGlobalInternalRuntimeContext } from '../context';
import { type TRuntimeContext, getInitialContext } from '../context/runtime';
import { wrapRuntimeContextProvider } from '../react/wrapper';
import type { SSRContainer } from '../types';
import { hydrateRoot } from './hydrate';
import { hydrateRoot, hydrateWithReact } from './hydrate';

export { hydrateWithReact };

const getQuery = () =>
window.location.search
Expand Down Expand Up @@ -100,12 +100,12 @@ export async function render(
return renderWithReact(App, rootElement);
}

async function ModernHydrate(App: React.ReactElement) {
return hydrateWithReact(App, rootElement);
}
// we should hydrateRoot only when SSR or SSG is enabled
if (process.env.MODERN_ENABLE_HYDRATION && window._SSR_DATA) {
async function ModernHydrate(App: React.ReactElement) {
return hydrateWithReact(App, rootElement);
}

// we should hydateRoot only when ssr
if (window._SSR_DATA) {
return hydrateRoot(App, context, ModernRender, ModernHydrate);
}
return ModernRender(wrapRuntimeContextProvider(App, context));
Expand All @@ -123,13 +123,3 @@ export async function renderWithReact(
root.render(App);
return root;
}

export async function hydrateWithReact(
App: React.ReactElement,
rootElement: HTMLElement,
) {
const root = hydrateReactRoot(rootElement, App, {
identifierPrefix: SSR_HYDRATION_ID_PREFIX,
});
return root;
}
28 changes: 17 additions & 11 deletions packages/runtime/plugin-runtime/src/router/runtime/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,12 @@ export const routerPlugin = (

const RouterWrapper = (props: any) => {
const routerResult = useRouterCreation(
{
...props,
rscPayload: props?.rscPayload,
},
process.env.MODERN_ENABLE_RSC
? {
...props,
rscPayload: props?.rscPayload,
}
: props,
{
api: api as any,
createRoutes,
Expand All @@ -166,7 +168,6 @@ export const routerPlugin = (
);

// Only cache router instance, routes are always from routerResult
// rscPayload is stable after first render, so we only create router once
const router = useMemo(() => {
if (cachedRouter) {
return cachedRouter;
Expand Down Expand Up @@ -244,9 +245,16 @@ function useRouterCreation(props: any, options: UseRouterCreationOptions) {
: baseUrl;

const { unstable_getBlockNavState: getBlockNavState } = runtimeContext;
const rscPayload = props?.rscPayload ? safeUse(props.rscPayload) : null;
// Keep the define expression inline at every RSC branch. Rspack can then
// remove the RSC imports in both production and development compilations.
const rscPayload =
process.env.MODERN_ENABLE_RSC && props?.rscPayload
? safeUse(props.rscPayload)
: null;

let hydrationData = window._ROUTER_DATA || rscPayload;
let hydrationData = process.env.MODERN_ENABLE_RSC
? window._ROUTER_DATA || rscPayload
: window._ROUTER_DATA;

return useMemo(() => {
if (hydrationData?.errors) {
Expand All @@ -256,10 +264,8 @@ function useRouterCreation(props: any, options: UseRouterCreationOptions) {
};
}

const isRscClient = getGlobalIsRscClient();

let routes: RouteObject[] | null = null;
if (isRscClient) {
if (process.env.MODERN_ENABLE_RSC && getGlobalIsRscClient()) {
routes = createRoutes
? createRoutes()
: createRouteObjectsFromConfig({
Expand All @@ -282,7 +288,7 @@ function useRouterCreation(props: any, options: UseRouterCreationOptions) {

const hooks = api.getHooks();

if (rscPayload) {
if (process.env.MODERN_ENABLE_RSC && rscPayload) {
try {
const router = createClientRouterFromPayload(
rscPayload,
Expand Down
Loading