@mui/internal-docs-infra
Version:
MUI Infra - internal documentation creation tools.
168 lines (163 loc) • 7.08 kB
JavaScript
import * as React from 'react';
import { CodeHighlighter } from "../CodeHighlighter/index.mjs";
import { applyUrlPrefixToCode, applyUrlPrefixToGlobalsCode, replaceUrlPrefix } from "../pipeline/loaderUtils/applyUrlPrefix.mjs";
import { createDemoDataWithVariants } from "../createDemoData/index.mjs";
import { resolveDemoFlag } from "./resolveDemoFlag.mjs";
/**
* Render-time display controls accepted on a generated demo component and in
* `createDemo(..., meta)`. Each pair cascades instance → meta → factory, with
* the "off" flag overriding the "on" flag at its layer.
*/
import { jsx as _jsx } from "react/jsx-runtime";
export function abstractCreateDemo(options, url, variants, meta) {
const demoData = createDemoDataWithVariants(url, variants, meta);
const variantType = options.variantTypes && options.variantTypes[Object.keys(variants).sort().join(':')];
const globalCode = [];
if (options.demoGlobalData) {
options.demoGlobalData.forEach(data => {
globalCode.push(data.precompute || data.url);
});
}
// Apply urlPrefix once at factory build time so the rewritten values are
// captured by the closures below (and shared across renders) instead of
// being recomputed on every render inside `DemoComponent`.
//
// The top-level `url` is only rewritten when the variant is fully loaded
// (i.e. a `precompute` is present). Without a precompute, this `url` is
// forwarded to `loadSource` at runtime, which expects the original
// `file://` URL it can read from disk — rewriting here would turn it into
// a hosted `https://` URL and cause `loadSource` to fail. In that case
// the `urlPrefix` prop on `<CodeHighlighter>` (forwarded into
// `loadIsomorphicCodeVariant`) takes care of rewriting the loaded variant after the
// file is read.
const urlPrefix = resolveUrlPrefix(options.projectDir, options.projectUrl);
const resolvedUrl = urlPrefix && demoData.precompute ? replaceUrlPrefix(demoData.url, urlPrefix) ?? demoData.url : demoData.url;
const resolvedPrecompute = urlPrefix && demoData.precompute ? applyUrlPrefixToCode(demoData.precompute, urlPrefix) : demoData.precompute;
const resolvedGlobalCode = urlPrefix && globalCode.length > 0 ? applyUrlPrefixToGlobalsCode(globalCode, urlPrefix) : globalCode;
function DemoComponent(props) {
const renderedComponents = Object.entries(demoData.components).reduce((acc, [key, Component]) => {
acc[key] = /*#__PURE__*/React.createElement(Component);
return acc;
}, {});
// Pull the render-time display controls off the demo props so they don't
// leak into the demo component's own props, then resolve their effective
// values (instance → meta → factory). Each pair is an on/off cascade.
const {
collapseToEmpty: instanceCollapseToEmpty,
showCollapsedFocus: instanceShowCollapsedFocus,
initialExpanded: instanceInitialExpanded,
initialCollapsed: instanceInitialCollapsed,
...restProps
} = props;
const collapseToEmpty = resolveDemoFlag([{
on: instanceCollapseToEmpty,
off: instanceShowCollapsedFocus
}, {
on: meta?.collapseToEmpty,
off: meta?.showCollapsedFocus
}], options.collapseToEmpty);
const initialExpanded = resolveDemoFlag([{
on: instanceInitialExpanded,
off: instanceInitialCollapsed
}, {
on: meta?.initialExpanded,
off: meta?.initialCollapsed
}], options.initialExpanded);
const highlighter = /*#__PURE__*/_jsx(CodeHighlighter, {
url: resolvedUrl,
name: demoData.name,
slug: demoData.slug,
variantType: meta?.variantType || variantType,
precompute: resolvedPrecompute,
globalsCode: resolvedGlobalCode,
components: renderedComponents,
contentProps: restProps,
collapseToEmpty: collapseToEmpty,
initialExpanded: initialExpanded,
Content: options.DemoContent,
ContentLoading: options.DemoContentLoading,
loadCodeMeta: options.loadCodeMeta,
loadVariantMeta: options.loadVariantMeta,
loadSource: options.loadSource,
sourceParser: options.sourceParser,
sourceEnhancers: options.sourceEnhancers,
urlPrefix: urlPrefix,
highlightAfter: meta?.highlightAfter || options.highlightAfter,
enhanceAfter: meta?.enhanceAfter || options.enhanceAfter,
editActivation: meta?.editActivation || options.editActivation,
controlled: options.controlled,
fallbackUsesExtraFiles: options.fallbackUsesExtraFiles,
fallbackUsesAllVariants: options.fallbackUsesAllVariants
});
// Use client provider if available
const ClientProvider = meta?.ClientProvider;
const rendered = ClientProvider ? /*#__PURE__*/_jsx(ClientProvider, {
children: highlighter
}) : highlighter;
// Tag every demo's rendered root with the `demo` class so tooling (e2e
// tests, screenshots) can target a demo in isolation from page chrome,
// without each standalone demo `page.tsx` having to add the wrapper.
return /*#__PURE__*/_jsx("div", {
className: "demo",
children: rendered
});
}
function Title() {
if (options.DemoTitle) {
return /*#__PURE__*/_jsx(options.DemoTitle, {
slug: demoData.slug,
children: demoData.name
});
}
return /*#__PURE__*/_jsx("h3", {
id: demoData.slug,
children: demoData.name
});
}
DemoComponent.Title = Title;
if (process.env.NODE_ENV !== 'production') {
DemoComponent.displayName = demoData.displayName;
DemoComponent.Title.displayName = `${demoData.displayName}Title`;
}
return DemoComponent;
}
function resolveUrlPrefix(projectDir, projectUrl) {
if (!projectDir || !projectUrl) {
return undefined;
}
const from = projectDir.endsWith('/') ? projectDir : `${projectDir}/`;
const to = projectUrl.endsWith('/') ? projectUrl : `${projectUrl}/`;
return {
from,
to
};
}
export function createDemoFactory(options) {
/**
* Creates a demo component for displaying code examples with syntax highlighting.
* @param url Depends on `import.meta.url` to determine the source file location.
* @param component The component to be rendered in the demo.
* @param meta Additional meta for the demo.
*/
const createDemo = (url, component, meta) => {
return abstractCreateDemo(options, url, {
Default: component
},
// precomputed code will use the 'Default' key
meta);
};
return createDemo;
}
export function createDemoWithVariantsFactory(options) {
/**
* Creates a demo component for displaying code examples with syntax highlighting.
* A variant is a different implementation style of the same component.
* @param url Depends on `import.meta.url` to determine the source file location.
* @param variants The variants of the component to be rendered in the demo.
* @param meta Additional meta for the demo.
*/
const createDemoWithVariants = (url, variants, meta) => {
return abstractCreateDemo(options, url, variants, meta);
};
return createDemoWithVariants;
}