@mui/internal-docs-infra
Version:
MUI Infra - internal documentation creation tools.
109 lines (104 loc) • 4.59 kB
JavaScript
import { formatProperties, formatParameters, parseMarkdownToHast, applyDescriptionReplacements } from "./format.mjs";
import { formatType } from "./formatType.mjs";
import { isFunctionType, isAnonymousObjectType, isObjectType } from "./typeGuards.mjs";
import { rewriteTypeStringsDeep } from "./rewriteTypes.mjs";
export async function formatHookData(hook, typeNameMap, rewriteContext, options = {}) {
const {
descriptionReplacements,
formatting,
externalTypes
} = options;
const {
exportNames
} = rewriteContext;
const descriptionText = hook.documentation?.description ? applyDescriptionReplacements(hook.documentation.description, descriptionReplacements) : undefined;
const description = descriptionText ? await parseMarkdownToHast(descriptionText) : undefined;
// Handle hook overloads: pick the signature with the most parameters,
// then mark parameters as optional if they don't appear in all signatures.
const callSignatures = hook.type.callSignatures;
const signature = callSignatures.reduce((longest, current) => current.parameters.length > longest.parameters.length ? current : longest);
const parameters = signature.parameters;
// Determine which parameters are optional by checking if they exist in all overloads
const minParamCount = Math.min(...callSignatures.map(sig => sig.parameters.length));
const optionalFromIndex = minParamCount;
let formattedParameters;
let formattedProperties;
if (parameters.length === 1 && isObjectType(parameters[0].type) && isAnonymousObjectType(parameters[0].type)) {
formattedProperties = await formatProperties(parameters[0].type.properties, {
exportNames,
typeNameMap,
formatting,
externalTypes,
descriptionReplacements
});
} else {
formattedParameters = await formatParameters(parameters, {
exportNames,
typeNameMap,
formatting,
externalTypes,
descriptionReplacements
});
// Mark parameters as optional if they don't appear in all overloads
parameters.forEach((param, index) => {
if (index >= optionalFromIndex) {
const entry = formattedParameters.find(p => p.name === param.name);
if (entry) {
entry.optional = true;
}
}
});
}
let formattedReturnValue;
let returnValueText;
// Only expand anonymous object types into a property table.
// Named types (like class instances `DialogHandle<Payload>`) are kept as type references.
const returnType = signature.returnValueType;
const shouldExpandReturnType = isObjectType(returnType) && isAnonymousObjectType(returnType) && returnType.properties && returnType.properties.length > 0;
if (shouldExpandReturnType) {
formattedReturnValue = await formatProperties(returnType.properties, {
exportNames,
typeNameMap,
formatting,
externalTypes,
descriptionReplacements
});
} else {
// Format type as plain text - highlighting is deferred to loadServerTypes
// Only expand anonymous objects (no type name) — named types should be shown as references.
const shouldExpand = isObjectType(returnType) && isAnonymousObjectType(returnType);
returnValueText = formatType(signature.returnValueType, {
expandObjects: shouldExpand,
exportNames,
typeNameMap,
externalTypesCollector: externalTypes
});
formattedReturnValue = returnValueText;
}
// Get return value description from @returns tag
const returnsTag = hook.documentation?.tags?.find(tag => tag.name === 'returns');
const returnValueDescriptionText = returnsTag?.value;
const returnValueDescription = returnValueDescriptionText ? await parseMarkdownToHast(returnValueDescriptionText) : undefined;
const raw = {
name: hook.name,
description,
descriptionText,
...(formattedParameters && {
parameters: formattedParameters
}),
...(formattedProperties && {
expandedProperties: formattedProperties
}),
returnValue: formattedReturnValue,
returnValueText,
returnValueDescription,
returnValueDescriptionText
};
// Post-process type strings to align naming across re-exports
return rewriteTypeStringsDeep(raw, rewriteContext);
}
export function isPublicHook(exportNode) {
const isPublic = exportNode.documentation?.visibility !== 'private' && exportNode.documentation?.visibility !== 'internal';
const hasIgnoreTag = exportNode.documentation?.tags?.some(tag => tag.name === 'ignore');
return isFunctionType(exportNode.type) && exportNode.name.startsWith('use') && !hasIgnoreTag && isPublic;
}