Skip to content

Commit

Permalink
[core] Move API docs generator to an internal package
Browse files Browse the repository at this point in the history
  • Loading branch information
michaldudak committed Nov 23, 2022
1 parent c7a589b commit f6f4e72
Show file tree
Hide file tree
Showing 24 changed files with 159 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import remarkVisit from 'unist-util-visit';
import { Link } from 'mdast';
import { defaultHandlers, parse as docgenParse, ReactDocgenApi } from 'react-docgen';
import { unstable_generateUtilityClass as generateUtilityClass } from '@mui/utils';
// @ts-ignore
import { renderInline as renderMarkdownInline } from '@mui/markdown';
import { getUnstyledFilename } from '@mui-internal/docs-utilities';
import * as ttp from 'typescript-to-proptypes';

import muiDefaultPropsHandler from '../utils/defaultPropsHandler';
Expand All @@ -23,7 +25,6 @@ import createDescribeableProp, {
} from '../utils/createDescribeableProp';
import generatePropDescription from '../utils/generatePropDescription';
import parseStyles, { Styles } from '../utils/parseStyles';
import { getUnstyledFilename } from '../../helpers';
import { ComponentInfo } from '../buildApiUtils';

const DEFAULT_PRETTIER_CONFIG_PATH = path.join(process.cwd(), 'prettier.config.js');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const getAllFiles = (dirPath: string, arrayOfFiles: string[] = []) => {

function findApiPages(relativeFolder: string) {
let pages: Array<{ pathname: string }> = [];
let filePaths = [];
let filePaths: string[] = [];
try {
filePaths = getAllFiles(path.join(process.cwd(), relativeFolder));
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import fs from 'fs';
import path from 'path';
import kebabCase from 'lodash/kebabCase';
// @ts-ignore
import { getHeaders, getTitle } from '@mui/markdown';
import { getLineFeed } from '@mui-internal/docs-utilities';
import { replaceComponentLinks } from './utils/replaceUrl';
import findPagesMarkdownNew from './utils/findPagesMarkdown';
import { getLineFeed } from '../helpers';

const systemComponents = fs
.readdirSync(path.resolve('packages', 'mui-system', 'src'))
Expand Down
28 changes: 28 additions & 0 deletions docs/packages/apiDocsBuilder/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "@mui-internal/api-docs-builder",
"version": "1.0.0",
"private": "true",
"main": "./buildApi.ts",
"scripts": {
"typescript": "tsc -p tsconfig.json"
},
"dependencies": {
"@babel/core": "^7.20.2",
"@mui-internal/docs-utilities": "*",
"@mui/markdown": "*",
"ast-types": "^0.14.2",
"doctrine": "^3.0.0",
"lodash": "^4.17.21",
"react-docgen": "^5.4.3",
"recast": "^0.21.5",
"typescript-to-proptypes": "*",
"yargs": "^17.6.2"
},
"devDependencies": {
"@types/mocha": "^10.0.0",
"@types/node": "^18.11.9",
"chai": "^4.3.7",
"sinon": "^14.0.2",
"typescript": "^4.9.3"
}
}
22 changes: 22 additions & 0 deletions docs/packages/apiDocsBuilder/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"compilerOptions": {
"allowJs": true,
"isolatedModules": true,
"noEmit": true,
"noUnusedLocals": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"esModuleInterop": true,
"types": ["node", "mocha"],
"target": "ES2020",
"module": "CommonJS",
"moduleResolution": "node",
"strict": true,
"baseUrl": "./",
"paths": {
"react-docgen": ["../../types/react-docgen.d.ts"]
}
},
"include": ["./**/*.ts", "./**/*.js"],
"exclude": ["node_modules"]
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// @ts-check
const astTypes = require('ast-types');
const { parse: parseDoctrine } = require('doctrine');
const { utils: docgenUtils } = require('react-docgen');
import astTypes from 'ast-types';
import { parse as parseDoctrine, Annotation } from 'doctrine';
import { utils as docgenUtils, NodePath, Documentation, Importer, Handler } from 'react-docgen';

const { getPropertyName, isReactForwardRefCall, printValue, resolveToValue } = docgenUtils;

Expand All @@ -15,22 +14,18 @@ const { namedTypes: types } = astTypes;
* @param {import('react-docgen').Importer} importer
* @returns {{ value: string; computed: boolean } | null}
*/
function getDefaultValue(propertyPath, importer) {
function getDefaultValue(propertyPath: NodePath, importer: Importer) {
if (!types.AssignmentPattern.check(propertyPath.get('value').node)) {
return null;
}
/**
* @type import('react-docgen').NodePath
*/
let path = propertyPath.get('value', 'right');

let path: NodePath = propertyPath.get('value', 'right');
let node = path.node;

/**
* @type {string|undefined}
*/
let defaultValue;
let defaultValue: string | undefined;
if (types.Literal.check(path.node)) {
// @ts-expect-error TODO upstream fix
// TODO: fix the type error
// @ts-ignore
defaultValue = node.raw;
} else {
if (types.AssignmentPattern.check(path.node)) {
Expand Down Expand Up @@ -68,11 +63,7 @@ function getDefaultValue(propertyPath, importer) {
return null;
}

/**
* @param {import('doctrine').Annotation} jsdoc
* @return {{ value: string } | undefined}
*/
function getJsdocDefaultValue(jsdoc) {
function getJsdocDefaultValue(jsdoc: Annotation): { value: string } | undefined {
const defaultTag = jsdoc.tags.find((tag) => tag.title === 'default');
if (defaultTag === undefined) {
return undefined;
Expand All @@ -86,25 +77,26 @@ function getJsdocDefaultValue(jsdoc) {
* @param {import('react-docgen').Importer} importer
* @returns {void}
*/
function getDefaultValuesFromProps(properties, documentation, importer) {
function getDefaultValuesFromProps(
properties: NodePath,
documentation: Documentation,
importer: Importer,
) {
const { props: documentedProps } = documentation.toObject();
/**
* @type Record<string, import('react-docgen').NodePath>
*/
const implementedProps = {};
const implementedProps: Record<string, NodePath> = {};
properties
.filter(
/**
* @param {import('react-docgen').NodePath} propertyPath
*/
(propertyPath) => types.Property.check(propertyPath.node),
(propertyPath: NodePath) => types.Property.check(propertyPath.node),
undefined,
)
.forEach(
/**
* @param {import('react-docgen').NodePath} propertyPath
*/
(propertyPath) => {
(propertyPath: NodePath) => {
const propName = getPropertyName(propertyPath);
if (propName) {
implementedProps[propName] = propertyPath;
Expand Down Expand Up @@ -142,35 +134,23 @@ function getDefaultValuesFromProps(properties, documentation, importer) {
});
}

/**
* @param {import('react-docgen').NodePath} componentDefinition
* @param {import('react-docgen').Importer} importer
* @returns import('react-docgen').NodePath
*/
function getRenderBody(componentDefinition, importer) {
function getRenderBody(componentDefinition: NodePath, importer: Importer): NodePath {
const value = resolveToValue(componentDefinition, importer);
if (isReactForwardRefCall(value, importer)) {
return value.get('arguments', 0, 'body', 'body');
}
return value.get('body', 'body');
}

/**
* @param {import('react-docgen').NodePath} functionBody
* @returns import('react-docgen').NodePath | undefined
*/
function getPropsPath(functionBody) {
/**
* @type import('react-docgen').NodePath | undefined
*/
let propsPath;
function getPropsPath(functionBody: NodePath): NodePath | undefined {
let propsPath: NodePath | undefined;
// visitVariableDeclarator, can't use visit body.node since it looses scope information
functionBody
.filter(
/**
* @param {import('react-docgen').NodePath} path
*/
(path) => {
(path: NodePath) => {
return types.VariableDeclaration.check(path.node);
},
undefined,
Expand All @@ -179,7 +159,7 @@ function getPropsPath(functionBody) {
/**
* @param {import('react-docgen').NodePath} path
*/
(path) => {
(path: NodePath) => {
const declaratorPath = path.get('declarations', 0);
// find `const {} = props`
// but not `const ownerState = props`
Expand All @@ -195,15 +175,12 @@ function getPropsPath(functionBody) {
return propsPath;
}

/**
* @type {import('react-docgen').Handler}
*/
const defaultPropsHandler = (documentation, componentDefinition, importer) => {
const defaultPropsHandler: Handler = (documentation, componentDefinition, importer) => {
const renderBody = getRenderBody(componentDefinition, importer);
const props = getPropsPath(renderBody);
if (props !== undefined) {
getDefaultValuesFromProps(props.get('properties'), documentation, importer);
}
};

module.exports = defaultPropsHandler;
export default defaultPropsHandler;
19 changes: 7 additions & 12 deletions docs/scripts/helpers.js → docs/packages/docsUtilities/index.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
const os = require('os');
import { EOL } from 'os';

/**
* @param {string} source
*/
function getLineFeed(source) {
function getLineFeed(source: string) {
const match = source.match(/\r?\n/);
return match === null ? os.EOL : match[0];
return match === null ? EOL : match[0];
}

const fixBabelIssuesRegExp = /(?<=(\/>)|,)(\r?\n){2}/g;
/**
* @param {string} source
*/
function fixBabelGeneratorIssues(source) {
function fixBabelGeneratorIssues(source: string) {
return source.replace(fixBabelIssuesRegExp, '\n');
}

/**
* @param {string} source
* @param {string} target
*/
function fixLineEndings(source, target) {
function fixLineEndings(source: string, target: string) {
return target.replace(/\r?\n/g, getLineFeed(source));
}

/**
* Converts styled or regular component d.ts file to unstyled d.ts
* @param {string} filename - the file of the styled or regular mui component
*/
function getUnstyledFilename(filename, definitionFile = false) {
function getUnstyledFilename(filename: string, definitionFile = false) {
if (filename.indexOf('Unstyled') > -1) {
return filename;
}
Expand Down Expand Up @@ -73,9 +73,4 @@ function getUnstyledFilename(filename, definitionFile = false) {
return definitionFile ? `${unstyledFile}.d.ts` : `${unstyledFile}.js`;
}

module.exports = {
getLineFeed,
fixBabelGeneratorIssues,
fixLineEndings,
getUnstyledFilename,
};
export { getLineFeed, fixBabelGeneratorIssues, fixLineEndings, getUnstyledFilename };
6 changes: 6 additions & 0 deletions docs/packages/docsUtilities/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "@mui-internal/docs-utilities",
"version": "1.0.0",
"private": "true",
"main": "index.ts"
}
9 changes: 1 addition & 8 deletions docs/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
{
"extends": "../tsconfig.json",
"include": [
"next-env.d.ts",
"types",
"src",
"pages",
"data",
"scripts/apiDocsBuilder/utils/generatePropDescription.ts"
],
"include": ["next-env.d.ts", "types", "src", "pages", "data"],
"compilerOptions": {
"allowJs": true,
"isolatedModules": true,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"release:publish:dry-run": "lerna publish from-package --dist-tag latest --contents build --registry=\"http://localhost:4873/\"",
"release:tag": "node scripts/releaseTag.mjs",
"docs:api": "rimraf ./docs/pages/**/api-docs ./docs/pages/**/api && yarn docs:api:build",
"docs:api:build": "cross-env BABEL_ENV=development __NEXT_EXPORT_TRAILING_SLASH=true babel-node --extensions \".tsx,.ts,.js\" ./docs/scripts/apiDocsBuilder/buildApi.ts",
"docs:api:build": "ts-node ./docs/packages/apiDocsBuilder/buildApi.ts",
"docs:build": "yarn workspace docs build",
"docs:build-sw": "yarn workspace docs build-sw",
"docs:build-color-preview": "babel-node scripts/buildColorTypes",
Expand Down
2 changes: 2 additions & 0 deletions packages/typescript-to-proptypes/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"url": "https://github.com/mui/material-ui.git",
"directory": "packages/typescript-to-proptypes"
},
"type": "module",
"main": "src/index.ts",
"author": "merceyz <merceyz@users.noreply.github.com>",
"license": "MIT",
"keywords": [
Expand Down
6 changes: 3 additions & 3 deletions scripts/generateProptypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
fixBabelGeneratorIssues,
fixLineEndings,
getUnstyledFilename,
} from '../docs/scripts/helpers';
} from '@mui-internal/docs-utilities';

const useExternalPropsFromInputBase = [
'autoComplete',
Expand Down Expand Up @@ -356,7 +356,7 @@ async function run(argv: HandlerArgv) {
const sourceFile = tsFile.includes('.d.ts') ? tsFile.replace('.d.ts', '.js') : tsFile;
try {
await generateProptypes(program, sourceFile, tsFile);
} catch (error) {
} catch (error: any) {
error.message = `${tsFile}: ${error.message}`;
throw error;
}
Expand All @@ -377,7 +377,7 @@ async function run(argv: HandlerArgv) {
}

yargs
.command({
.command<HandlerArgv>({
command: '$0',
describe: 'Generates Component.propTypes from TypeScript declarations',
builder: (command) => {
Expand Down
Loading

0 comments on commit f6f4e72

Please sign in to comment.