UNPKG

@mui/internal-docs-infra

Version:

MUI Infra - internal documentation creation tools.

168 lines (163 loc) 7.08 kB
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; }