Skip to content

Commit

Permalink
[api-docs-builder] Allow custom annotations (#44707)
Browse files Browse the repository at this point in the history
  • Loading branch information
vladmoroz authored Dec 10, 2024
1 parent 348cba6 commit 817470c
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 40 deletions.
58 changes: 38 additions & 20 deletions packages/api-docs-builder/ApiBuilders/ComponentApiBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { visit as remarkVisit } from 'unist-util-visit';
import type { Link } from 'mdast';
import { defaultHandlers, parse as docgenParse } from 'react-docgen';
import { parse as parseDoctrine, Annotation } from 'doctrine';
import escapeRegExp from 'lodash/escapeRegExp';
import { renderCodeTags, renderMarkdown } from '../buildApi';
import { ProjectSettings, SortingStrategiesType } from '../ProjectSettings';
import { toGitHubPath, writePrettifiedFile } from '../buildApiUtils';
Expand Down Expand Up @@ -229,26 +230,36 @@ async function annotateComponentDefinition(
if (markdownLines[markdownLines.length - 1] !== '') {
markdownLines.push('');
}
markdownLines.push(
'Demos:',
'',
...api.demos.map((demo) => {
return `- [${demo.demoPageTitle}](${
demo.demoPathname.startsWith('http') ? demo.demoPathname : `${HOST}${demo.demoPathname}`
})`;
}),
'',
);

markdownLines.push(
'API:',
'',
`- [${api.name} API](${
api.apiPathname.startsWith('http') ? api.apiPathname : `${HOST}${api.apiPathname}`
})`,
);
if (api.inheritance) {
markdownLines.push(`- inherits ${inheritanceAPILink}`);
if (api.customAnnotation) {
markdownLines.push(
...api.customAnnotation
.split('\n')
.map((line) => line.trim())
.filter(Boolean),
);
} else {
markdownLines.push(
'Demos:',
'',
...api.demos.map((demo) => {
return `- [${demo.demoPageTitle}](${
demo.demoPathname.startsWith('http') ? demo.demoPathname : `${HOST}${demo.demoPathname}`
})`;
}),
'',
);

markdownLines.push(
'API:',
'',
`- [${api.name} API](${
api.apiPathname.startsWith('http') ? api.apiPathname : `${HOST}${api.apiPathname}`
})`,
);
if (api.inheritance) {
markdownLines.push(`- inherits ${inheritanceAPILink}`);
}
}

if (componentJsdoc.tags.length > 0) {
Expand Down Expand Up @@ -764,7 +775,13 @@ export default async function generateComponentApi(
reactApi.description = componentJsdoc.description;

// Ignore what we might have generated in `annotateComponentDefinition`
const annotatedDescriptionMatch = reactApi.description.match(/(Demos|API):\r?\n\r?\n/);
let annotationBoundary: RegExp = /(Demos|API):\r?\n\r?\n/;
if (componentInfo.customAnnotation) {
annotationBoundary = new RegExp(
escapeRegExp(componentInfo.customAnnotation.trim().split('\n')[0].trim()),
);
}
const annotatedDescriptionMatch = reactApi.description.match(new RegExp(annotationBoundary));
if (annotatedDescriptionMatch !== null) {
reactApi.description = reactApi.description.slice(0, annotatedDescriptionMatch.index).trim();
}
Expand All @@ -778,6 +795,7 @@ export default async function generateComponentApi(
reactApi.slots = [];
reactApi.classes = [];
reactApi.demos = componentInfo.getDemos();
reactApi.customAnnotation = componentInfo.customAnnotation;
reactApi.inheritance = null;
if (reactApi.demos.length === 0) {
throw new Error(
Expand Down
64 changes: 44 additions & 20 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 { defaultHandlers, parse as docgenParse } from 'react-docgen';
import kebabCase from 'lodash/kebabCase';
import upperFirst from 'lodash/upperFirst';
import { parse as parseDoctrine, Annotation } from 'doctrine';
import escapeRegExp from 'lodash/escapeRegExp';
import { escapeEntities, renderMarkdown } from '../buildApi';
import { ProjectSettings } from '../ProjectSettings';
import { computeApiDescription } from './ComponentApiBuilder';
Expand Down Expand Up @@ -184,32 +185,42 @@ async function annotateHookDefinition(
}

const markdownLines = (await computeApiDescription(api, { host: HOST })).split('\n');

// Ensure a newline between manual and generated description.
if (markdownLines[markdownLines.length - 1] !== '') {
markdownLines.push('');
}

if (api.demos && api.demos.length > 0) {
if (api.customAnnotation) {
markdownLines.push(
'Demos:',
'',
...api.demos.map((item) => {
return `- [${item.demoPageTitle}](${
item.demoPathname.startsWith('http') ? item.demoPathname : `${HOST}${item.demoPathname}`
})`;
}),
...api.customAnnotation
.split('\n')
.map((line) => line.trim())
.filter(Boolean),
);
} else {
if (api.demos && api.demos.length > 0) {
markdownLines.push(
'Demos:',
'',
...api.demos.map((item) => {
return `- [${item.demoPageTitle}](${
item.demoPathname.startsWith('http') ? item.demoPathname : `${HOST}${item.demoPathname}`
})`;
}),
'',
);
}

markdownLines.push(
'API:',
'',
`- [${api.name} API](${
api.apiPathname.startsWith('http') ? api.apiPathname : `${HOST}${api.apiPathname}`
})`,
);
}

markdownLines.push(
'API:',
'',
`- [${api.name} API](${
api.apiPathname.startsWith('http') ? api.apiPathname : `${HOST}${api.apiPathname}`
})`,
);

if (hookJsdoc.tags.length > 0) {
markdownLines.push('');
}
Expand Down Expand Up @@ -410,8 +421,16 @@ export default async function generateHookApi(
project: TypeScriptProject,
projectSettings: ProjectSettings,
) {
const { filename, name, apiPathname, apiPagesDirectory, getDemos, readFile, skipApiGeneration } =
hooksInfo;
const {
filename,
name,
apiPathname,
apiPagesDirectory,
getDemos,
readFile,
skipApiGeneration,
customAnnotation,
} = hooksInfo;

const { shouldSkip, EOL, src } = readFile();

Expand Down Expand Up @@ -445,8 +464,12 @@ export default async function generateHookApi(
// 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/);
// Ignore what we might have generated in `annotateComponentDefinition`
let annotationBoundary: RegExp = /(Demos|API):\r?\n\r?\n/;
if (customAnnotation) {
annotationBoundary = new RegExp(escapeRegExp(customAnnotation.trim().split('\n')[0].trim()));
}
const annotatedDescriptionMatch = reactApi.description.match(new RegExp(annotationBoundary));
if (annotatedDescriptionMatch !== null) {
reactApi.description = reactApi.description.slice(0, annotatedDescriptionMatch.index).trim();
}
Expand All @@ -458,6 +481,7 @@ export default async function generateHookApi(
reactApi.apiPathname = apiPathname;
reactApi.EOL = EOL;
reactApi.demos = getDemos();
reactApi.customAnnotation = customAnnotation;
if (reactApi.demos.length === 0) {
// TODO: Enable this error once all public hooks are documented
// throw new Error(
Expand Down
1 change: 1 addition & 0 deletions packages/api-docs-builder/types/ApiBuilder.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ interface CommonReactApi extends ReactDocgenApi {
*/
apiDocsTranslationFolder?: string;
deprecated: true | undefined;
customAnnotation?: string;
}

export interface PropsTableItem {
Expand Down
8 changes: 8 additions & 0 deletions packages/api-docs-builder/types/utils.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ export type ComponentInfo = {
* If `true`, the component's name match one of the MUI System components.
*/
isSystemComponent?: boolean;
/**
* If provided, this annotation will be used instead of the auto-generated demo & API links
*/
customAnnotation?: string;
};

export type HookInfo = {
Expand All @@ -74,4 +78,8 @@ export type HookInfo = {
getDemos: ComponentInfo['getDemos'];
apiPagesDirectory: string;
skipApiGeneration?: boolean;
/**
* If provided, this annotation will be used instead of the auto-generated demo & API links
*/
customAnnotation?: string;
};

0 comments on commit 817470c

Please sign in to comment.