1import React, { useContext } from 'react'; 2 3import DocumentationPageContext from '~/components/DocumentationPageContext'; 4import { P } from '~/components/base/paragraph'; 5import { GeneratedData } from '~/components/plugins/api/APIDataTypes'; 6import APISectionClasses from '~/components/plugins/api/APISectionClasses'; 7import APISectionComponents from '~/components/plugins/api/APISectionComponents'; 8import APISectionConstants from '~/components/plugins/api/APISectionConstants'; 9import APISectionEnums from '~/components/plugins/api/APISectionEnums'; 10import APISectionInterfaces from '~/components/plugins/api/APISectionInterfaces'; 11import APISectionMethods from '~/components/plugins/api/APISectionMethods'; 12import APISectionProps from '~/components/plugins/api/APISectionProps'; 13import APISectionTypes from '~/components/plugins/api/APISectionTypes'; 14import { TypeDocKind } from '~/components/plugins/api/APISectionUtils'; 15 16const LATEST_VERSION = `v${require('~/package.json').version}`; 17 18type Props = { 19 packageName: string; 20 apiName?: string; 21 forceVersion?: string; 22}; 23 24const filterDataByKind = ( 25 entries: GeneratedData[] = [], 26 kind: TypeDocKind | TypeDocKind[], 27 additionalCondition: (entry: GeneratedData) => boolean = () => true 28) => 29 entries.filter( 30 (entry: GeneratedData) => 31 (Array.isArray(kind) ? kind.includes(entry.kind) : entry.kind === kind) && 32 additionalCondition(entry) 33 ); 34 35const isHook = ({ name }: GeneratedData) => 36 name.startsWith('use') && 37 // note(simek): hardcode this exception until the method will be renamed 38 name !== 'useSystemBrightnessAsync'; 39 40const isListener = ({ name }: GeneratedData) => 41 name.endsWith('Listener') || name.endsWith('Listeners'); 42 43const isProp = ({ name }: GeneratedData) => name.includes('Props') && name !== 'ErrorRecoveryProps'; 44 45const isComponent = ({ type, extendedTypes }: GeneratedData) => 46 type?.name === 'React.FC' || 47 (extendedTypes && extendedTypes.length ? extendedTypes[0].name === 'Component' : false); 48 49const isConstant = ({ flags, name, type }: GeneratedData) => 50 (flags?.isConst || false) && name !== 'default' && type?.name !== 'React.FC'; 51 52const renderAPI = ( 53 packageName: string, 54 version: string = 'unversioned', 55 apiName?: string, 56 isTestMode: boolean = false 57): JSX.Element => { 58 try { 59 // note(simek): When the path prefix is interpolated Next or Webpack fails to locate the file 60 const { children: data } = isTestMode 61 ? require(`../../public/static/data/${version}/${packageName}.json`) 62 : require(`~/public/static/data/${version}/${packageName}.json`); 63 64 const methods = filterDataByKind( 65 data, 66 TypeDocKind.Function, 67 entry => !isListener(entry) && !isHook(entry) 68 ); 69 const hooks = filterDataByKind(data, TypeDocKind.Function, isHook); 70 const eventSubscriptions = filterDataByKind(data, TypeDocKind.Function, isListener); 71 72 const types = filterDataByKind( 73 data, 74 TypeDocKind.TypeAlias, 75 entry => 76 !isProp(entry) && 77 !!( 78 entry.type.declaration || 79 entry.type.types || 80 entry.type.type || 81 entry.type.typeArguments 82 ) 83 ); 84 85 const props = filterDataByKind( 86 data, 87 TypeDocKind.TypeAlias, 88 entry => isProp(entry) && !!(entry.type.types || entry.type.declaration?.children) 89 ); 90 const defaultProps = filterDataByKind( 91 data 92 .filter((entry: GeneratedData) => entry.kind === TypeDocKind.Class) 93 .map((entry: GeneratedData) => entry.children) 94 .flat(), 95 TypeDocKind.Property, 96 entry => entry.name === 'defaultProps' 97 )[0]; 98 99 const enums = filterDataByKind(data, [TypeDocKind.Enum, TypeDocKind.LegacyEnum]); 100 const interfaces = filterDataByKind(data, TypeDocKind.Interface); 101 const constants = filterDataByKind(data, TypeDocKind.Variable, entry => isConstant(entry)); 102 103 const components = filterDataByKind(data, [TypeDocKind.Variable, TypeDocKind.Class], entry => 104 isComponent(entry) 105 ); 106 const componentsPropNames = components.map(component => `${component.name}Props`); 107 const componentsProps = filterDataByKind(props, TypeDocKind.TypeAlias, entry => 108 componentsPropNames.includes(entry.name) 109 ); 110 111 const classes = filterDataByKind(data, TypeDocKind.Class, entry => !isComponent(entry)); 112 113 return ( 114 <> 115 <APISectionComponents data={components} componentsProps={componentsProps} /> 116 <APISectionClasses data={classes} /> 117 <APISectionConstants data={constants} apiName={apiName} /> 118 <APISectionMethods data={hooks} header="Hooks" /> 119 <APISectionMethods data={methods} apiName={apiName} /> 120 <APISectionMethods 121 data={eventSubscriptions} 122 apiName={apiName} 123 header="Event Subscriptions" 124 /> 125 {props && !componentsProps.length ? ( 126 <APISectionProps data={props} defaultProps={defaultProps} /> 127 ) : null} 128 <APISectionTypes data={types} /> 129 <APISectionInterfaces data={interfaces} /> 130 <APISectionEnums data={enums} /> 131 </> 132 ); 133 } catch (error) { 134 return <P>No API data file found, sorry!</P>; 135 } 136}; 137 138const APISection = ({ packageName, apiName, forceVersion }: Props) => { 139 const { version } = useContext(DocumentationPageContext); 140 const resolvedVersion = 141 forceVersion || 142 (version === 'unversioned' ? version : version === 'latest' ? LATEST_VERSION : version); 143 return renderAPI(packageName, resolvedVersion, apiName, !!forceVersion); 144}; 145 146export default APISection; 147