Skip to content

Commit

Permalink
[code-infra] Refactor eslint import/no-cycle rule (mui#42705)
Browse files Browse the repository at this point in the history
Signed-off-by: Lukas <llukas.tyla@gmail.com>
  • Loading branch information
LukasTy authored and joserodolfofreitas committed Jul 29, 2024
1 parent f0aec16 commit f60c46d
Show file tree
Hide file tree
Showing 12 changed files with 256 additions and 256 deletions.
5 changes: 4 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,10 @@ module.exports = {
],
},
],
'import/no-cycle': ['error', { ignoreExternal: true }],
// TODO: Consider setting back to `ignoreExternal: true` when the expected behavior is fixed:
// https://github.com/import-js/eslint-plugin-import/issues/2348#issuecomment-1587320057
// Reevaluate when https://github.com/import-js/eslint-plugin-import/pull/2998 is released.
'import/no-cycle': ['error', { ignoreExternal: false }],
},
},
{
Expand Down
3 changes: 1 addition & 2 deletions packages/api-docs-builder-core/baseUi/generateApiLinks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import kebabCase from 'lodash/kebabCase';
import { ReactApi as ComponentReactApi } from '@mui-internal/api-docs-builder/ApiBuilders/ComponentApiBuilder';
import { ReactApi as HookReactApi } from '@mui-internal/api-docs-builder/ApiBuilders/HookApiBuilder';
import { ComponentReactApi, HookReactApi } from '@mui-internal/api-docs-builder';

/**
* Generates the api links, in a format that would point to the appropriate API tab
Expand Down
113 changes: 18 additions & 95 deletions packages/api-docs-builder/ApiBuilders/ComponentApiBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ import kebabCase from 'lodash/kebabCase';
import remark from 'remark';
import remarkVisit from 'unist-util-visit';
import type { Link } from 'mdast';
import { defaultHandlers, parse as docgenParse, ReactDocgenApi } from 'react-docgen';
import { defaultHandlers, parse as docgenParse } 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 { toGitHubPath, writePrettifiedFile } from '../buildApiUtils';
import muiDefaultPropsHandler from '../utils/defaultPropsHandler';
import parseTest from '../utils/parseTest';
import generatePropTypeDescription, { getChained } from '../utils/generatePropTypeDescription';
Expand All @@ -23,90 +22,11 @@ import createDescribeableProp, {
} from '../utils/createDescribeableProp';
import generatePropDescription from '../utils/generatePropDescription';
import { TypeScriptProject } from '../utils/createTypeScriptProject';
import parseSlotsAndClasses, { Slot } from '../utils/parseSlotsAndClasses';
import parseSlotsAndClasses from '../utils/parseSlotsAndClasses';
import generateApiTranslations from '../utils/generateApiTranslation';
import { sortAlphabetical } from '../utils/sortObjects';

export type AdditionalPropsInfo = {
cssApi?: boolean;
sx?: boolean;
slotsApi?: boolean;
'joy-size'?: boolean;
'joy-color'?: boolean;
'joy-variant'?: boolean;
};

export type SeeMore = { description: string; link: { text: string; url: string } };

export interface ReactApi extends ReactDocgenApi {
demos: ReturnType<ComponentInfo['getDemos']>;
EOL: string;
filename: string;
apiPathname: string;
forwardsRefTo: string | undefined;
inheritance: ReturnType<ComponentInfo['getInheritance']>;
/**
* react component name
* @example 'Accordion'
*/
name: string;
muiName: string;
description: string;
spread: boolean | undefined;
/**
* If `true`, the component supports theme default props customization.
* If `null`, we couldn't infer this information.
* If `undefined`, it's not applicable in this context, for example Base UI components.
*/
themeDefaultProps: boolean | undefined | null;
/**
* result of path.readFileSync from the `filename` in utf-8
*/
src: string;
classes: ComponentClassDefinition[];
slots: Slot[];
propsTable: _.Dictionary<{
default: string | undefined;
required: boolean | undefined;
type: { name: string | undefined; description: string | undefined };
deprecated: true | undefined;
deprecationInfo: string | undefined;
signature: undefined | { type: string; describedArgs?: string[]; returned?: string };
additionalInfo?: AdditionalPropsInfo;
seeMoreLink?: SeeMore['link'];
}>;
/**
* Different ways to import components
*/
imports: string[];
translations: {
componentDescription: string;
deprecationInfo: string | undefined;
propDescriptions: {
[key: string]: {
description: string;
requiresRef?: boolean;
deprecated?: string;
typeDescriptions?: { [t: string]: string };
seeMoreText?: string;
};
};
classDescriptions: {
[key: string]: {
description: string;
conditions?: string;
nodeName?: string;
deprecationInfo?: string;
};
};
slotDescriptions?: { [key: string]: string };
};
/**
* The folder used to store the API translation.
*/
apiDocsTranslationFolder?: string;
deprecated: true | undefined;
}
import { AdditionalPropsInfo, ComponentReactApi } from '../types/ApiBuilder.types';
import { Slot, ComponentInfo } from '../types/utils.types';

const cssComponents = ['Box', 'Grid', 'Typography', 'Stack'];

Expand All @@ -118,7 +38,7 @@ const cssComponents = ['Box', 'Grid', 'Typography', 'Stack'];
* this method.
*/
export async function computeApiDescription(
api: { description: ReactApi['description'] },
api: { description: ComponentReactApi['description'] },
options: { host: string },
): Promise<string> {
const { host } = options;
Expand Down Expand Up @@ -151,7 +71,7 @@ export async function computeApiDescription(
* * - [Icon API](https://mui.com/api/icon/)
*/
async function annotateComponentDefinition(
api: ReactApi,
api: ComponentReactApi,
componentJsdoc: Annotation,
projectSettings: ProjectSettings,
) {
Expand Down Expand Up @@ -372,7 +292,7 @@ function extractClassCondition(description: string) {
const generateApiPage = async (
apiPagesDirectory: string,
importTranslationPagesDirectory: string,
reactApi: ReactApi,
reactApi: ComponentReactApi,
sortingStrategies?: SortingStrategiesType,
onlyJsonFile: boolean = false,
layoutConfigPath: string = '',
Expand Down Expand Up @@ -471,11 +391,11 @@ const generateApiPage = async (
};

const attachTranslations = (
reactApi: ReactApi,
reactApi: ComponentReactApi,
deprecationInfo: string | undefined,
settings?: CreateDescribeablePropSettings,
) => {
const translations: ReactApi['translations'] = {
const translations: ComponentReactApi['translations'] = {
componentDescription: reactApi.description,
deprecationInfo: deprecationInfo ? renderMarkdown(deprecationInfo) : undefined,
propDescriptions: {},
Expand Down Expand Up @@ -539,10 +459,13 @@ const attachTranslations = (
reactApi.translations = translations;
};

const attachPropsTable = (reactApi: ReactApi, settings?: CreateDescribeablePropSettings) => {
const attachPropsTable = (
reactApi: ComponentReactApi,
settings?: CreateDescribeablePropSettings,
) => {
const propErrors: Array<[propName: string, error: Error]> = [];
type Pair = [string, ReactApi['propsTable'][string]];
const componentProps: ReactApi['propsTable'] = _.fromPairs(
type Pair = [string, ComponentReactApi['propsTable'][string]];
const componentProps: ComponentReactApi['propsTable'] = _.fromPairs(
Object.entries(reactApi.props!).map(([propName, propDescriptor]): Pair => {
let prop: DescribeablePropDescriptor | null;
try {
Expand Down Expand Up @@ -599,7 +522,7 @@ const attachPropsTable = (reactApi: ReactApi, settings?: CreateDescribeablePropS
}
}

let signature: ReactApi['propsTable'][string]['signature'];
let signature: ComponentReactApi['propsTable'][string]['signature'];
if (signatureType !== undefined) {
signature = {
type: signatureType,
Expand Down Expand Up @@ -699,7 +622,7 @@ export default async function generateComponentApi(
}

const filename = componentInfo.filename;
let reactApi: ReactApi;
let reactApi: ComponentReactApi;

if (componentInfo.isSystemComponent) {
try {
Expand Down
93 changes: 14 additions & 79 deletions packages/api-docs-builder/ApiBuilders/HookApiBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { readFileSync, writeFileSync } from 'fs';
import path from 'path';
import * as ts from 'typescript';
import { Symbol, isPropertySignature } from 'typescript';
import * as astTypes from 'ast-types';
import * as _ from 'lodash';
import * as babel from '@babel/core';
import traverse from '@babel/traverse';
import { defaultHandlers, parse as docgenParse, ReactDocgenApi } from 'react-docgen';
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';
Expand All @@ -15,91 +15,26 @@ import { computeApiDescription } from './ComponentApiBuilder';
import {
getSymbolDescription,
getSymbolJSDocTags,
HookInfo,
stringifySymbol,
toGitHubPath,
writePrettifiedFile,
} from '../buildApiUtils';
import { TypeScriptProject } from '../utils/createTypeScriptProject';
import generateApiTranslations from '../utils/generateApiTranslation';

interface ParsedProperty {
name: string;
description: string;
tags: { [tagName: string]: ts.JSDocTagInfo };
required: boolean;
typeStr: string;
}
import { HookReactApi, ParsedProperty } from '../types/ApiBuilder.types';
import { HookInfo } from '../types/utils.types';

const parseProperty = async (
propertySymbol: ts.Symbol,
propertySymbol: Symbol,
project: TypeScriptProject,
): Promise<ParsedProperty> => ({
name: propertySymbol.name,
description: getSymbolDescription(propertySymbol, project),
tags: getSymbolJSDocTags(propertySymbol),
required: !propertySymbol.declarations?.find(ts.isPropertySignature)?.questionToken,
required: !propertySymbol.declarations?.find(isPropertySignature)?.questionToken,
typeStr: await stringifySymbol(propertySymbol, project),
});

export interface ReactApi extends ReactDocgenApi {
demos: ReturnType<HookInfo['getDemos']>;
EOL: string;
filename: string;
apiPathname: string;
parameters?: ParsedProperty[];
returnValue?: ParsedProperty[];
/**
* hook name
* @example 'useButton'
*/
name: string;
description: string;
/**
* Different ways to import components
*/
imports: string[];
/**
* result of path.readFileSync from the `filename` in utf-8
*/
src: string;
parametersTable: _.Dictionary<{
default: string | undefined;
required: boolean | undefined;
type: { name: string | undefined; description: string | undefined };
deprecated: true | undefined;
deprecationInfo: string | undefined;
}>;
returnValueTable: _.Dictionary<{
default: string | undefined;
required: boolean | undefined;
type: { name: string | undefined; description: string | undefined };
deprecated: true | undefined;
deprecationInfo: string | undefined;
}>;
translations: {
hookDescription: string;
deprecationInfo: string | undefined;
parametersDescriptions: {
[key: string]: {
description: string;
deprecated?: string;
};
};
returnValueDescriptions: {
[key: string]: {
description: string;
deprecated?: string;
};
};
};
/**
* The folder used to store the API translation.
*/
apiDocsTranslationFolder?: string;
deprecated: true | undefined;
}

/**
* Add demos & API comment block to type definitions, e.g.:
* /**
Expand All @@ -112,7 +47,7 @@ export interface ReactApi extends ReactDocgenApi {
* * - [useButton API](https://mui.com/base-ui/api/use-button/)
*/
async function annotateHookDefinition(
api: ReactApi,
api: HookReactApi,
hookJsdoc: Annotation,
projectSettings: ProjectSettings,
) {
Expand Down Expand Up @@ -308,12 +243,12 @@ async function annotateHookDefinition(
}

const attachTable = (
reactApi: ReactApi,
reactApi: HookReactApi,
params: ParsedProperty[],
tableName: 'parametersTable' | 'returnValueTable',
) => {
const propErrors: Array<[propName: string, error: Error]> = [];
const parameters: ReactApi[typeof tableName] = params
const parameters: HookReactApi[typeof tableName] = params
.map((p) => {
const { name: propName, ...propDescriptor } = p;
let prop: Omit<ParsedProperty, 'name'> | null;
Expand Down Expand Up @@ -355,7 +290,7 @@ const attachTable = (
},
};
})
.reduce((acc, curr) => ({ ...acc, ...curr }), {}) as unknown as ReactApi['parametersTable'];
.reduce((acc, curr) => ({ ...acc, ...curr }), {}) as unknown as HookReactApi['parametersTable'];
if (propErrors.length > 0) {
throw new Error(
`There were errors creating prop descriptions:\n${propErrors
Expand All @@ -376,8 +311,8 @@ const generateTranslationDescription = (description: string) => {
return renderMarkdown(description.replace(/\n@default.*$/, ''));
};

const attachTranslations = (reactApi: ReactApi, deprecationInfo: string | undefined) => {
const translations: ReactApi['translations'] = {
const attachTranslations = (reactApi: HookReactApi, deprecationInfo: string | undefined) => {
const translations: HookReactApi['translations'] = {
hookDescription: reactApi.description,
deprecationInfo: deprecationInfo ? renderMarkdown(deprecationInfo).trim() : undefined,
parametersDescriptions: {},
Expand Down Expand Up @@ -413,7 +348,7 @@ const attachTranslations = (reactApi: ReactApi, deprecationInfo: string | undefi
reactApi.translations = translations;
};

const generateApiJson = async (outputDirectory: string, reactApi: ReactApi) => {
const generateApiJson = async (outputDirectory: string, reactApi: HookReactApi) => {
/**
* Gather the metadata needed for the component's API page.
*/
Expand Down Expand Up @@ -543,7 +478,7 @@ export default async function generateHookApi(
return null;
}

const reactApi: ReactApi = docgenParse(
const reactApi: HookReactApi = docgenParse(
src,
(ast) => {
let node;
Expand Down
6 changes: 2 additions & 4 deletions packages/api-docs-builder/ProjectSettings.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { ComponentClassDefinition } from '@mui/internal-docs-utils';
import { ComponentInfo, HookInfo } from './buildApiUtils';
import { CreateTypeScriptProjectOptions } from './utils/createTypeScriptProject';
import { CreateDescribeablePropSettings } from './utils/createDescribeableProp';
import { ReactApi as ComponentReactApi } from './ApiBuilders/ComponentApiBuilder';
import { ReactApi as HookReactApi } from './ApiBuilders/HookApiBuilder';
import { Slot } from './utils/parseSlotsAndClasses';
import { ComponentReactApi, HookReactApi } from './types/ApiBuilder.types';
import { Slot, ComponentInfo, HookInfo } from './types/utils.types';

export type SortingStrategiesType = {
/**
Expand Down
Loading

0 comments on commit f60c46d

Please sign in to comment.