Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[docs-infra] Allow JSDoc tags #42337

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"hookDescription": "Stabilizes the ListContext value for the MenuItem component, so it doesn't change when sibling items update.\n\n@param id The id of the MenuItem. If undefined, it will be generated with useId.\n@returns The stable ListContext value and the id of the MenuItem.",
"hookDescription": "Stabilizes the ListContext value for the MenuItem component, so it doesn't change when sibling items update.",
"parametersDescriptions": {},
"returnValueDescriptions": {}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"hookDescription": "Stabilizes the ListContext value for the Option component, so it doesn't change when sibling Options update.\n\n@param value The value of the Option.\n@returns The stable ListContext value.",
"hookDescription": "Stabilizes the ListContext value for the Option component, so it doesn't change when sibling Options update.",
"parametersDescriptions": {},
"returnValueDescriptions": {}
}
21 changes: 18 additions & 3 deletions packages/api-docs-builder/ApiBuilders/ComponentApiBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type { Link } from 'mdast';
import { defaultHandlers, parse as docgenParse, ReactDocgenApi } from 'react-docgen';
import { renderMarkdown } from '@mui/internal-markdown';
import { ComponentClassDefinition } from '@mui/internal-docs-utils';
import { parse as parseDoctrine, Annotation } from 'doctrine';
import { ProjectSettings, SortingStrategiesType } from '../ProjectSettings';
import { ComponentInfo, toGitHubPath, writePrettifiedFile } from '../buildApiUtils';
import muiDefaultPropsHandler from '../utils/defaultPropsHandler';
Expand Down Expand Up @@ -147,7 +148,7 @@ export async function computeApiDescription(
* *
* * - [Icon API](https://mui.com/api/icon/)
*/
async function annotateComponentDefinition(api: ReactApi) {
async function annotateComponentDefinition(api: ReactApi, componentJsdoc: Annotation) {
const HOST = 'https://mui.com';

const typesFilename = api.filename.replace(/\.js$/, '.d.ts');
Expand Down Expand Up @@ -318,6 +319,14 @@ async function annotateComponentDefinition(api: ReactApi) {
markdownLines.push(`- inherits ${inheritanceAPILink}`);
}

if (componentJsdoc.tags.length > 0) {
markdownLines.push('');
}

componentJsdoc.tags.forEach((tag) => {
markdownLines.push(`@${tag.title}${tag.name ? ` ${tag.name} -` : ''} ${tag.description}`);
});

const jsdoc = `/**\n${markdownLines
.map((line) => (line.length > 0 ? ` * ${line}` : ` *`))
.join('\n')}\n */`;
Expand Down Expand Up @@ -738,13 +747,19 @@ export default async function generateComponentApi(
reactApi.props = {};
}

const { getComponentImports = defaultGetComponentImports } = projectSettings;
const componentJsdoc = parseDoctrine(reactApi.description);

// We override `reactApi.description` with `componentJsdoc.description` because
// the former can include JSDoc tags that we don't want to render in the docs.
reactApi.description = componentJsdoc.description;

// Ignore what we might have generated in `annotateComponentDefinition`
const annotatedDescriptionMatch = reactApi.description.match(/(Demos|API):\r?\n\r?\n/);
if (annotatedDescriptionMatch !== null) {
reactApi.description = reactApi.description.slice(0, annotatedDescriptionMatch.index).trim();
}

const { getComponentImports = defaultGetComponentImports } = projectSettings;
reactApi.filename = filename;
reactApi.name = componentInfo.name;
reactApi.imports = getComponentImports(componentInfo.name, filename);
Expand Down Expand Up @@ -825,7 +840,7 @@ export default async function generateComponentApi(
: !skipAnnotatingComponentDefinition
) {
// Add comment about demo & api links (including inherited component) to the component file
await annotateComponentDefinition(reactApi);
await annotateComponentDefinition(reactApi, componentJsdoc);
}
}

Expand Down
18 changes: 16 additions & 2 deletions packages/api-docs-builder/ApiBuilders/HookApiBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import traverse from '@babel/traverse';
import { defaultHandlers, parse as docgenParse, ReactDocgenApi } from 'react-docgen';
import kebabCase from 'lodash/kebabCase';
import upperFirst from 'lodash/upperFirst';
import { parse as parseDoctrine, Annotation } from 'doctrine';
import { renderMarkdown } from '@mui/internal-markdown';
import { ProjectSettings } from '../ProjectSettings';
import { computeApiDescription } from './ComponentApiBuilder';
Expand Down Expand Up @@ -108,7 +109,7 @@ export interface ReactApi extends ReactDocgenApi {
* *
* * - [useButton API](https://mui.com/base-ui/api/use-button/)
*/
async function annotateHookDefinition(api: ReactApi) {
async function annotateHookDefinition(api: ReactApi, hookJsdoc: Annotation) {
const HOST = 'https://mui.com';

const typesFilename = api.filename.replace(/\.js$/, '.d.ts');
Expand Down Expand Up @@ -285,6 +286,14 @@ async function annotateHookDefinition(api: ReactApi) {
})`,
);

if (hookJsdoc.tags.length > 0) {
markdownLines.push('');
}

hookJsdoc.tags.forEach((tag) => {
markdownLines.push(`@${tag.title}${tag.name ? ` ${tag.name} -` : ''} ${tag.description}`);
});

const jsdoc = `/**\n${markdownLines
.map((line) => (line.length > 0 ? ` * ${line}` : ` *`))
.join('\n')}\n */`;
Expand Down Expand Up @@ -546,6 +555,11 @@ export default async function generateHookApi(

const parameters = await extractInfoFromType(`${upperFirst(name)}Parameters`, project);
const returnValue = await extractInfoFromType(`${upperFirst(name)}ReturnValue`, project);
const hookJsdoc = parseDoctrine(reactApi.description);

// We override `reactApi.description` with `hookJsdoc.description` because
// the former can include JSDoc tags that we don't want to render in the docs.
reactApi.description = hookJsdoc.description;

// Ignore what we might have generated in `annotateHookDefinition`
const annotatedDescriptionMatch = reactApi.description.match(/(Demos|API):\r?\n\r?\n/);
Expand Down Expand Up @@ -590,7 +604,7 @@ export default async function generateHookApi(
await generateApiJson(apiPagesDirectory, reactApi);

// Add comment about demo & api links to the component hook file
await annotateHookDefinition(reactApi);
await annotateHookDefinition(reactApi, hookJsdoc);
}

return reactApi;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import { ListContext, ListContextValue, ListItemState } from '../useList';
/**
* Stabilizes the ListContext value for the MenuItem component, so it doesn't change when sibling items update.
*
* @param id The id of the MenuItem. If undefined, it will be generated with useId.
* @returns The stable ListContext value and the id of the MenuItem.
*
* Demos:
*
* - [Menu](https://mui.com/base-ui/react-menu/#hooks)
*
* API:
*
* - [useMenuItemContextStabilizer API](https://mui.com/base-ui/react-menu/hooks-api/#use-menu-item-context-stabilizer)
*
* @param id - The id of the MenuItem. If undefined, it will be generated with useId.
* @returns The stable ListContext value and the id of the MenuItem.
*/
export function useMenuItemContextStabilizer(id: string | undefined) {
const listContext = React.useContext(ListContext as React.Context<ListContextValue<string>>);
Expand Down
6 changes: 3 additions & 3 deletions packages/mui-base/src/useOption/useOptionContextStabilizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import { ListContext, ListContextValue } from '../useList';
/**
* Stabilizes the ListContext value for the Option component, so it doesn't change when sibling Options update.
*
* @param value The value of the Option.
* @returns The stable ListContext value.
*
* Demos:
*
* - [Select](https://mui.com/base-ui/react-select/#hooks)
*
* API:
*
* - [useOptionContextStabilizer API](https://mui.com/base-ui/react-select/hooks-api/#use-option-context-stabilizer)
*
* @param value - The value of the Option.
* @returns The stable ListContext value.
*/
export function useOptionContextStabilizer<OptionValue>(value: OptionValue) {
const listContext = React.useContext(ListContext as React.Context<ListContextValue<OptionValue>>);
Expand Down
2 changes: 2 additions & 0 deletions packages/mui-material/src/Hidden/Hidden.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ export interface HiddenProps {
* API:
*
* - [Hidden API](https://mui.com/material-ui/api/hidden/)
*
* @deprecated The Hidden component was deprecated in Material UI v5. To learn more, see [the Hidden section](/material-ui/migration/v5-component-changes/#hidden) of the migration docs.
*/
declare const Hidden: React.JSXElementConstructor<HiddenProps>;

Expand Down
2 changes: 2 additions & 0 deletions packages/mui-material/src/Hidden/Hidden.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import HiddenCss from './HiddenCss';

/**
* Responsively hides children based on the selected implementation.
*
* @deprecated The Hidden component was deprecated in Material UI v5. To learn more, see [the Hidden section](/material-ui/migration/v5-component-changes/#hidden) of the migration docs.
*/
function Hidden(props) {
const {
Expand Down
Loading