@mui/internal-docs-infra
Version:
MUI Infra - internal documentation creation tools.
127 lines (122 loc) • 5.52 kB
JavaScript
import * as React from 'react';
import { buildChunkRenderInputs } from "./buildChunkRenderInputs.mjs";
import { resolveChunkRender } from "./resolveChunkRender.mjs";
import { ChunkServerLoader } from "./ChunkServerLoader.mjs";
import { CoordinatedLazyClient } from "./CoordinatedLazyClient.mjs";
import { jsx as _jsx } from "react/jsx-runtime";
function RenderNull() {
return null;
}
/**
* Build a self-loading {@link CoordinatedLazy} component. The returned component
* is **isomorphic**: per render it evaluates {@link buildChunkRenderInputs} and
* routes via `resolveChunkRender`, so one component covers build, server,
* and client loading, and server or client rendering:
*
* - **content** (preloaded/controlled) - renders `ChunkContent` directly, so
* build-precomputed data lands in the server HTML. `ChunkContent` may be a
* server OR client component here.
* - **server-loader / server-initial** - renders the server `ChunkServerLoader`
* under a Suspense boundary (server `Loader`/`InitialLoader` or a server-side
* `data`-mode load), so content loads and renders on the server and streams
* in. Requires a server (RSC) render context; supports server-component content.
* - **client modes** (async/initial/null) - delegates to the `'use client'`
* {@link CoordinatedLazyClient}, which loads on the client and swaps the
* fallback to content. `ChunkContent` here must be a client component.
*
* The client-mode branch hands the (function-bearing) `config` to a `'use client'`
* component, so a client-loaded chunk must render inside a client subtree - call
* `createCoordinatedLazy` from a client module, or wrap it in a client provider
* (e.g. `abstractCreateStream`'s `ClientProvider`). Server-loaded and
* preloaded/precomputed chunks have no such constraint - they render entirely on
* the server path.
*
* The user's generic props `T` flow through to both components; `data` (type
* `P`) is the loaded value (or the initial value while loading). Use it
* standalone for any deferred piece (a demo, a chart, a code frame); `useStream`
* renders a streamed list of them.
*/
export function createCoordinatedLazy(config) {
const ChunkContent = config.ChunkContent;
const ChunkLoading = config.ChunkLoading ?? RenderNull;
function CoordinatedLazyContent(props) {
const decision = resolveChunkRender(buildChunkRenderInputs(config, props));
const userProps = props.userProps ?? {};
// Render the content directly so it is part of the server HTML (no framework
// swap):
// - `content` (loaded/precomputed/controlled): settled, not loading.
// - `content-initial`: the initial paint is in hand and there is no full
// loader, so render the content from the initial (still `loading`). The
// content owns any further client-side load + swap itself.
if (decision.mode === 'content' || decision.mode === 'content-initial') {
// Spreading the generic `T` alongside fixed fields needs an assertion; the
// shape matches `ChunkContentProps<T, P>`.
const contentProps = {
...userProps,
data: props.preloaded,
loading: decision.loading
};
return /*#__PURE__*/_jsx(ChunkContent, {
...contentProps
});
}
// Server load (server `Loader`/`InitialLoader` or a server-side `data` load).
// `ChunkServerLoader` is a plain async component; this branch is taken only
// when a server loader is configured.
if (decision.mode === 'server-loader' || decision.mode === 'server-initial') {
const serverLoader = /*#__PURE__*/_jsx(ChunkServerLoader, {
config: config,
props: props,
initial: decision.mode === 'server-initial'
});
// `awaitServerLoad` blocks the server render on the loader (no Suspense) so
// its content lands in the initial HTML; otherwise stream a fallback.
if (props.awaitServerLoad) {
return serverLoader;
}
const loadingProps = {
...userProps,
data: props.preloaded,
loading: true
};
return /*#__PURE__*/_jsx(React.Suspense, {
fallback: /*#__PURE__*/_jsx(ChunkLoading, {
...loadingProps
}),
children: serverLoader
});
}
// Client-driven mode (attempt-initial-client, or a forceClient opt-out).
// When the content manages its own client load + swap, render it directly
// (loading) rather than wrapping it in the framework's load+swap, so it is
// not double-swapped.
if (config.contentManagesSwap) {
const contentProps = {
...userProps,
data: props.preloaded,
loading: true
};
return /*#__PURE__*/_jsx(ChunkContent, {
...contentProps
});
}
// Otherwise load on the client and swap via the framework. Strip the
// server-only loader functions (`source`/`Loader`/`InitialLoader`) so they can
// never be serialized into the 'use client' `CoordinatedLazyClient` - the RSC
// "Functions cannot be passed to Client Components" guard, enforced by its
// `ClientChunkConfig` param. A client-loaded chunk gets its source from a
// surrounding `ChunkProvider` instead.
const {
source,
Loader,
InitialLoader,
...clientConfig
} = config;
return /*#__PURE__*/_jsx(CoordinatedLazyClient, {
config: clientConfig,
props: props
});
}
CoordinatedLazyContent.displayName = 'CoordinatedLazyContent';
return CoordinatedLazyContent;
}