@mui/internal-docs-infra
Version:
MUI Infra - internal documentation creation tools.
293 lines (275 loc) • 13.1 kB
JavaScript
import * as React from 'react';
import enhanceCodeInline from "../pipeline/enhanceCodeInline/index.mjs";
import enhanceCodeTypes from "../pipeline/enhanceCodeTypes/index.mjs";
import { typeToJsx, additionalTypesToJsx } from "./typesToJsx.mjs";
import { jsx as _jsx } from "react/jsx-runtime";
/**
* Default enhancers applied when no enhancers are specified.
* Note: enhanceCodeTypes is added dynamically when anchorMap is available.
*/
const DEFAULT_ENHANCERS = [enhanceCodeInline];
/**
* Default inline enhancers applied to shortType and default fields.
* These are simpler than full enhancers since inline fields don't need
* block-level processing like export links.
*/
const DEFAULT_ENHANCERS_INLINE = [enhanceCodeInline];
/**
* Export data structure containing a main type and its related additional types.
* Used in the precompute field for structured type data.
*/
export function abstractCreateTypes(options, url, meta, exportName) {
if (!url.startsWith('file:')) {
throw new Error('abstractCreateTypes() requires the `url` parameter to be a file URL. Use `import.meta.url` to get the current file URL.');
}
if (!meta || !meta.precompute) {
throw new Error('abstractCreateTypes() must be called within a `types.ts` file');
}
const singleComponentName = meta.precompute.singleComponentName;
// Merge components from factory options and meta, with meta taking priority
const components = {
...options.components,
...meta.components
};
// Resolve named component slots (meta overrides options)
const TypePre = meta.TypePre ?? options.TypePre;
const DetailedTypePre = meta.DetailedTypePre ?? options.DetailedTypePre;
const ShortTypeCode = meta.ShortTypeCode ?? options.ShortTypeCode;
const DefaultCode = meta.DefaultCode ?? options.DefaultCode;
const RawTypePre = meta.RawTypePre ?? options.RawTypePre;
const highlightAt = meta.highlightAt ?? options.highlightAt;
// Enhancers from meta completely override options.enhancers if set
// Use DEFAULT_ENHANCERS if neither meta nor options specify enhancers
// Then append enhanceCodeTypes if anchorMap is available
let enhancers = meta.enhancers ?? options.enhancers ?? DEFAULT_ENHANCERS;
if (meta.precompute.anchorMap && (Object.keys(meta.precompute.anchorMap.js ?? {}).length > 0 || Object.keys(meta.precompute.anchorMap.css ?? {}).length > 0)) {
const typeRefComponent = meta.typeRefComponent ?? options.typeRefComponent;
const typePropRefComponent = meta.typePropRefComponent ?? options.typePropRefComponent;
const typeParamRefComponent = meta.typeParamRefComponent ?? options.typeParamRefComponent;
const linkProps = meta.linkProps ?? options.linkProps;
const linkParams = meta.linkParams ?? options.linkParams;
const linkScope = meta.linkScope ?? options.linkScope;
const moduleLinkMap = meta.moduleLinkMap ?? options.moduleLinkMap;
const defaultImportSlug = meta.defaultImportSlug ?? options.defaultImportSlug;
const exportLinksOptions = {
linkMap: meta.precompute.anchorMap
};
if (typeRefComponent) {
exportLinksOptions.typeRefComponent = typeRefComponent;
}
if (typePropRefComponent) {
exportLinksOptions.typePropRefComponent = typePropRefComponent;
}
if (typeParamRefComponent) {
exportLinksOptions.typeParamRefComponent = typeParamRefComponent;
}
if (linkProps) {
exportLinksOptions.linkProps = linkProps;
}
if (linkParams) {
exportLinksOptions.linkParams = linkParams;
}
if (linkScope) {
exportLinksOptions.linkScope = linkScope;
}
if (moduleLinkMap) {
exportLinksOptions.moduleLinkMap = moduleLinkMap;
}
if (defaultImportSlug) {
exportLinksOptions.defaultImportSlug = defaultImportSlug;
}
enhancers = [...enhancers, [enhanceCodeTypes, exportLinksOptions]];
}
// Inline enhancers for shortType and default fields
const enhancersInline = meta.enhancersInline ?? options.enhancersInline ?? DEFAULT_ENHANCERS_INLINE;
// Extract precompute reference to avoid null checks inside component
const precompute = meta.precompute;
// Determine target export name outside component - it's static
let targetExportName = exportName || singleComponentName || Object.keys(precompute.exports)[0];
// Handle default imports in single-component mode: when a component is imported
// via default import, singleComponentName is the local binding name (e.g., 'loadPrecomputedTypes')
// but the API extractor uses 'default' as the export key.
// Only apply in single-component mode to avoid mapping every key to 'default' in multiple mode.
if (!exportName && !(targetExportName in precompute.exports) && 'default' in precompute.exports) {
targetExportName = 'default';
}
// For single component mode (createTypes), include global additional types
// For multiple component mode (createMultipleTypes), they go to the separate AdditionalTypes component
// Exception: if the export doesn't exist (e.g., namespace import on types-only module),
// use the variant-only additional types for that specific variant
const isMultipleMode = Boolean(exportName);
const exportExists = targetExportName in precompute.exports;
// For namespace imports on types-only modules, use the pre-separated
// variantOnlyAdditionalTypes instead of filtering from the shared pool
const filteredAdditionalTypes = !exportExists && precompute.variantOnlyAdditionalTypes?.[targetExportName] ? precompute.variantOnlyAdditionalTypes[targetExportName] : precompute.additionalTypes;
function TypesComponent(props) {
// Memoize the conversion from HAST to JSX - only for the single export we need
const {
type,
additionalTypes
} = React.useMemo(() => typeToJsx(precompute.exports[targetExportName], filteredAdditionalTypes, {
components,
TypePre,
DetailedTypePre,
ShortTypeCode,
DefaultCode,
RawTypePre,
enhancers,
enhancersInline,
highlightAt
},
// Include additionalTypes for:
// 1. Single component mode (createTypes)
// 2. Multiple mode when export doesn't exist (namespace import on types-only module)
!isMultipleMode || !exportExists), []);
return /*#__PURE__*/_jsx(options.TypesTable, {
...props,
type: type,
additionalTypes: additionalTypes,
multiple: isMultipleMode
});
}
if (process.env.NODE_ENV !== 'production') {
TypesComponent.displayName = meta?.displayName || `${meta?.name?.replace(/ /g, '') || ''}${singleComponentName || exportName || ''}Types`;
}
return TypesComponent;
}
export function createTypesFactory(options) {
/**
* Creates a types table component for displaying TypeScript type information.
* @param url Depends on `import.meta.url` to determine the source file location.
* @param typeDef The type definition object to extract types from.
* @param [meta] Additional meta for the types table.
*/
const createTypes = (url, typeDef, meta) => {
return abstractCreateTypes(options, url, meta);
};
return createTypes;
}
export function createMultipleTypesFactory(options) {
/**
* Creates multiple types table components for displaying TypeScript type information.
* Each key in the typeDef object will have a corresponding component in `types`.
* Also returns an `AdditionalTypes` component for top-level non-namespaced types.
* @param url Depends on `import.meta.url` to determine the source file location.
* @param typeDef The type definition object with multiple exports to extract types from.
* @param [meta] Additional meta for the types tables.
*/
const createMultipleTypes = (url, typeDef, meta) => {
const types = {};
// When precompute data is available, use its exports keys instead of typeDef keys.
// This allows the webpack loader to replace the typeDef with a plain object,
// avoiding the need to import actual component modules at runtime.
// Also include keys from variantTypeNames for namespace imports on types-only modules.
let keys;
if (meta?.precompute) {
const exportKeys = Object.keys(meta.precompute.exports);
// Add variant names that have types but no export (namespace imports on types-only modules)
const variantKeys = meta.precompute.variantTypeNames ? Object.keys(meta.precompute.variantTypeNames).filter(k => !exportKeys.includes(k) && meta.precompute.variantTypeNames[k].length > 0) : [];
keys = [...exportKeys, ...variantKeys];
} else {
keys = Object.keys(typeDef);
}
keys.forEach(key => {
types[key] = abstractCreateTypes(options, url, meta, String(key));
});
// Create AdditionalTypes component for top-level non-namespaced types
const AdditionalTypes = createAdditionalTypesComponent(options, url, meta);
return {
types,
AdditionalTypes
};
};
return createMultipleTypes;
}
function createAdditionalTypesComponent(options, url, meta) {
if (!url.startsWith('file:')) {
throw new Error('createAdditionalTypesComponent() requires the `url` parameter to be a file URL. Use `import.meta.url` to get the current file URL.');
}
if (!meta || !meta.precompute) {
throw new Error('createAdditionalTypesComponent() must be called within a `types.ts` file');
}
// Merge components from factory options and meta, with meta taking priority
const components = {
...options.components,
...meta.components
};
// Resolve named component slots (meta overrides options)
const TypePre = meta.TypePre ?? options.TypePre;
const DetailedTypePre = meta.DetailedTypePre ?? options.DetailedTypePre;
const ShortTypeCode = meta.ShortTypeCode ?? options.ShortTypeCode;
const DefaultCode = meta.DefaultCode ?? options.DefaultCode;
const RawTypePre = meta.RawTypePre ?? options.RawTypePre;
const highlightAt = meta.highlightAt ?? options.highlightAt;
// Enhancers from meta completely override options.enhancers if set
// Use DEFAULT_ENHANCERS if neither meta nor options specify enhancers
// Then append enhanceCodeTypes if anchorMap is available
let enhancers = meta.enhancers ?? options.enhancers ?? DEFAULT_ENHANCERS;
if (meta.precompute.anchorMap && (Object.keys(meta.precompute.anchorMap.js ?? {}).length > 0 || Object.keys(meta.precompute.anchorMap.css ?? {}).length > 0)) {
const typeRefComponent = meta.typeRefComponent ?? options.typeRefComponent;
const typePropRefComponent = meta.typePropRefComponent ?? options.typePropRefComponent;
const typeParamRefComponent = meta.typeParamRefComponent ?? options.typeParamRefComponent;
const linkProps = meta.linkProps ?? options.linkProps;
const linkParams = meta.linkParams ?? options.linkParams;
const linkScope = meta.linkScope ?? options.linkScope;
const moduleLinkMap = meta.moduleLinkMap ?? options.moduleLinkMap;
const defaultImportSlug = meta.defaultImportSlug ?? options.defaultImportSlug;
const exportLinksOptions = {
linkMap: meta.precompute.anchorMap
};
if (typeRefComponent) {
exportLinksOptions.typeRefComponent = typeRefComponent;
}
if (typePropRefComponent) {
exportLinksOptions.typePropRefComponent = typePropRefComponent;
}
if (typeParamRefComponent) {
exportLinksOptions.typeParamRefComponent = typeParamRefComponent;
}
if (linkProps) {
exportLinksOptions.linkProps = linkProps;
}
if (linkParams) {
exportLinksOptions.linkParams = linkParams;
}
if (linkScope) {
exportLinksOptions.linkScope = linkScope;
}
if (moduleLinkMap) {
exportLinksOptions.moduleLinkMap = moduleLinkMap;
}
if (defaultImportSlug) {
exportLinksOptions.defaultImportSlug = defaultImportSlug;
}
enhancers = [...enhancers, [enhanceCodeTypes, exportLinksOptions]];
}
// Inline enhancers for shortType and default fields
const enhancersInline = meta.enhancersInline ?? options.enhancersInline ?? DEFAULT_ENHANCERS_INLINE;
const precompute = meta.precompute;
// Include the "Default" variant-only types since they represent the catch-all
// flat/common types that belong in the Additional Types section.
const allAdditionalTypes = precompute.variantOnlyAdditionalTypes?.Default ? [...precompute.additionalTypes, ...precompute.variantOnlyAdditionalTypes.Default] : precompute.additionalTypes;
function AdditionalTypesComponent(props) {
const additionalTypes = React.useMemo(() => additionalTypesToJsx(allAdditionalTypes, {
components,
TypePre,
DetailedTypePre,
ShortTypeCode,
DefaultCode,
RawTypePre,
enhancers,
enhancersInline,
highlightAt
}), []);
return /*#__PURE__*/_jsx(options.TypesTable, {
...props,
type: undefined,
additionalTypes: additionalTypes,
multiple: true
});
}
if (process.env.NODE_ENV !== 'production') {
AdditionalTypesComponent.displayName = `${meta?.name?.replace(/ /g, '') || ''}AdditionalTypes`;
}
return AdditionalTypesComponent;
}