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] Add a rawDescriptions option #44390

Merged
merged 4 commits into from
Nov 13, 2024
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
4 changes: 2 additions & 2 deletions packages/api-docs-builder/ApiBuilders/ComponentApiBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { remark } from 'remark';
import { visit as remarkVisit } from 'unist-util-visit';
import type { Link } from 'mdast';
import { defaultHandlers, parse as docgenParse } from 'react-docgen';
import { renderMarkdown } from '@mui/internal-markdown';
import { parse as parseDoctrine, Annotation } from 'doctrine';
import { renderCodeTags, renderMarkdown } from '../buildApi';
import { ProjectSettings, SortingStrategiesType } from '../ProjectSettings';
import { toGitHubPath, writePrettifiedFile } from '../buildApiUtils';
import muiDefaultPropsHandler from '../utils/defaultPropsHandler';
Expand Down Expand Up @@ -279,7 +279,7 @@ function extractClassCondition(description: string) {
description.replace(stylesRegex, '$1{{nodeName}}$5{{conditions}}.'),
),
nodeName: renderMarkdown(conditions[3]),
conditions: renderMarkdown(conditions[6].replace(/`(.*?)`/g, '<code>$1</code>')),
conditions: renderMarkdown(renderCodeTags(conditions[6])),
};
}

Expand Down
9 changes: 2 additions & 7 deletions packages/api-docs-builder/ApiBuilders/HookApiBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,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 { renderMarkdown } from '@mui/internal-markdown';
import { escapeEntities, renderMarkdown } from '../buildApi';
import { ProjectSettings } from '../ProjectSettings';
import { computeApiDescription } from './ComponentApiBuilder';
import {
Expand Down Expand Up @@ -268,12 +268,7 @@ const attachTable = (
const requiredProp = prop.required;

const deprecation = (propDescriptor.description || '').match(/@deprecated(\s+(?<info>.*))?/);
const typeDescription = (propDescriptor.typeStr ?? '')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
const typeDescription = escapeEntities(propDescriptor.typeStr ?? '');
return {
[propName]: {
type: {
Expand Down
30 changes: 28 additions & 2 deletions packages/api-docs-builder/buildApi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { mkdirSync } from 'fs';
import path from 'path';
import * as fse from 'fs-extra';
import { renderMarkdown as _renderMarkdown } from '@mui/internal-markdown';
import findComponents from './utils/findComponents';
import findHooks from './utils/findHooks';
import { writePrettifiedFile } from './buildApiUtils';
Expand All @@ -13,6 +14,8 @@ import {
} from './utils/createTypeScriptProject';
import { ProjectSettings } from './ProjectSettings';
import { ComponentReactApi } from './types/ApiBuilder.types';
import _escapeCell from './utils/escapeCell';
import _escapeEntities from './utils/escapeEntities';

async function removeOutdatedApiDocsTranslations(
components: readonly ComponentReactApi[],
Expand Down Expand Up @@ -64,7 +67,14 @@ async function removeOutdatedApiDocsTranslations(
);
}

export async function buildApi(projectsSettings: ProjectSettings[], grep: RegExp | null = null) {
let rawDescriptionsCurrent = false;

export async function buildApi(
projectsSettings: ProjectSettings[],
grep: RegExp | null = null,
rawDescriptions = false,
) {
rawDescriptionsCurrent = rawDescriptions;
const allTypeScriptProjects = projectsSettings
.flatMap((setting) => setting.typeScriptProjects)
.reduce(
Expand Down Expand Up @@ -118,7 +128,6 @@ async function buildSingleProject(
if (manifestDir) {
mkdirSync(manifestDir, { recursive: true });
}

const apiBuilds = tsProjects.flatMap((project) => {
const projectComponents = findComponents(path.join(project.rootPath, 'src')).filter(
(component) => {
Expand Down Expand Up @@ -202,3 +211,20 @@ async function buildSingleProject(
await projectSettings.onCompleted?.();
return builds;
}

export function renderMarkdown(markdown: string) {
return rawDescriptionsCurrent ? markdown : _renderMarkdown(markdown);
}
export function renderCodeTags(value: string) {
return rawDescriptionsCurrent ? value : value.replace(/`(.*?)`/g, '<code>$1</code>');
}
export function escapeEntities(value: string) {
return rawDescriptionsCurrent ? value : _escapeEntities(value);
}
export function escapeCell(value: string) {
return rawDescriptionsCurrent ? value : _escapeCell(value);
}
export function joinUnionTypes(value: string[]) {
// Use unopinionated formatting for raw descriptions
return rawDescriptionsCurrent ? value.join(' | ') : value.join('<br>&#124;&nbsp;');
}
8 changes: 8 additions & 0 deletions packages/api-docs-builder/utils/escapeEntities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default function escapeEntities(value: string): string {
return value
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
2 changes: 1 addition & 1 deletion packages/api-docs-builder/utils/generatePropDescription.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import * as doctrine from 'doctrine';
import * as recast from 'recast';
import { PropTypeDescriptor } from 'react-docgen';
import { escapeCell } from '../buildApi';
import {
isElementTypeAcceptingRefProp,
isElementAcceptingRefProp,
} from './generatePropTypeDescription';
import { DescribeablePropDescriptor } from './createDescribeableProp';
import escapeCell from './escapeCell';
import { SeeMore } from '../types/utils.types';

function resolveType(type: NonNullable<doctrine.Tag['type']>): string {
Expand Down
26 changes: 10 additions & 16 deletions packages/api-docs-builder/utils/generatePropTypeDescription.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as recast from 'recast';
import { parse as docgenParse, PropTypeDescriptor } from 'react-docgen';
import escapeCell from './escapeCell';
import { escapeCell, escapeEntities, joinUnionTypes } from '../buildApi';

function getDeprecatedInfo(type: PropTypeDescriptor) {
const marker = /deprecatedPropType\((\r*\n)*\s*PropTypes\./g;
Expand Down Expand Up @@ -113,26 +113,20 @@ export default function generatePropTypeDescription(type: PropTypeDescriptor): s
.join(', ')} }`;

case 'union':
return (
type.value
.map((type2) => {
return generatePropTypeDescription(type2);
})
// Display one value per line as it's better for visibility.
.join('<br>&#124;&nbsp;')
return joinUnionTypes(
type.value.map((type2) => {
return generatePropTypeDescription(type2) ?? '';
}),
);
case 'enum':
return (
type.value
.map((type2) => {
return escapeCell(type2.value);
})
// Display one value per line as it's better for visibility.
.join('<br>&#124;&nbsp;')
return joinUnionTypes(
type.value.map((type2) => {
return escapeCell(type2.value);
}),
);

case 'arrayOf': {
return `Array&lt;${generatePropTypeDescription(type.value)}&gt;`;
return `Array${escapeEntities('<')}${generatePropTypeDescription(type.value)}${escapeEntities('>')}`;
}

case 'instanceOf': {
Expand Down
2 changes: 1 addition & 1 deletion packages/api-docs-builder/utils/parseSlotsAndClasses.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as ts from 'typescript';
import { ComponentClassDefinition } from '@mui-internal/api-docs-builder';
import { renderMarkdown } from '@mui/internal-markdown';
import { renderMarkdown } from '../buildApi';
import { getSymbolDescription, getSymbolJSDocTags } from '../buildApiUtils';
import { TypeScriptProject } from './createTypeScriptProject';
import { getPropsFromComponentNode } from './getPropsFromComponentNode';
Expand Down
21 changes: 14 additions & 7 deletions scripts/buidApiDocs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,30 @@ const projectSettings: ProjectSettings[] = [
muiSystemProjectSettings,
];

type CommandOptions = { grep?: string };
type CommandOptions = { grep?: string; rawDescriptions?: boolean };

async function run(argv: ArgumentsCamelCase<CommandOptions>) {
const grep = argv.grep == null ? null : new RegExp(argv.grep);
return buildApi(projectSettings, grep);
const rawDescriptions = argv.rawDescriptions === true;
return buildApi(projectSettings, grep, rawDescriptions);
}

yargs(process.argv.slice(2))
.command({
command: '$0',
describe: 'Generates API documentation for the MUI packages.',
builder: (command) => {
return command.option('grep', {
description:
'Only generate files for component filenames matching the pattern. The string is treated as a RegExp.',
type: 'string',
});
return command
.option('grep', {
description:
'Only generate files for component filenames matching the pattern. The string is treated as a RegExp.',
type: 'string',
})
.option('rawDescriptions', {
description: 'Whether to output raw JSDoc descriptions or process them as markdown.',
type: 'boolean',
default: false,
});
},
handler: run,
})
Expand Down