1import React, { useContext } from 'react'; 2 3import DocumentationPageContext from '~/components/DocumentationPageContext'; 4import { P } from '~/components/base/paragraph'; 5import { ClassDefinitionData, 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, signatures }: GeneratedData) => 46 (type?.name && ['React.FC', 'ForwardRefExoticComponent'].includes(type?.name)) || 47 (extendedTypes && extendedTypes.length ? extendedTypes[0].name === 'Component' : false) || 48 (signatures && signatures[0] 49 ? signatures[0].type.name === 'Element' || 50 (signatures[0].parameters && signatures[0].parameters[0].name === 'props') 51 : false); 52 53const isConstant = ({ name, type }: GeneratedData) => 54 !['default', 'Constants', 'EventEmitter'].includes(name) && 55 !(type?.name && ['React.FC', 'ForwardRefExoticComponent'].includes(type?.name)); 56 57const renderAPI = ( 58 packageName: string, 59 version: string = 'unversioned', 60 apiName?: string, 61 isTestMode: boolean = false 62): JSX.Element => { 63 try { 64 // note(simek): When the path prefix is interpolated Next or Webpack fails to locate the file 65 const { children: data } = isTestMode 66 ? require(`../../public/static/data/${version}/${packageName}.json`) 67 : require(`~/public/static/data/${version}/${packageName}.json`); 68 69 const methods = filterDataByKind( 70 data, 71 TypeDocKind.Function, 72 entry => !isListener(entry) && !isHook(entry) && !isComponent(entry) 73 ); 74 const hooks = filterDataByKind(data, TypeDocKind.Function, isHook); 75 const eventSubscriptions = filterDataByKind(data, TypeDocKind.Function, isListener); 76 77 const types = filterDataByKind( 78 data, 79 TypeDocKind.TypeAlias, 80 entry => 81 !isProp(entry) && 82 !!( 83 entry.type.declaration || 84 entry.type.types || 85 entry.type.type || 86 entry.type.typeArguments 87 ) 88 ); 89 90 const props = filterDataByKind( 91 data, 92 TypeDocKind.TypeAlias, 93 entry => isProp(entry) && !!(entry.type.types || entry.type.declaration?.children) 94 ); 95 const defaultProps = filterDataByKind( 96 data 97 .filter((entry: GeneratedData) => entry.kind === TypeDocKind.Class) 98 .map((entry: GeneratedData) => entry.children) 99 .flat(), 100 TypeDocKind.Property, 101 entry => entry.name === 'defaultProps' 102 )[0]; 103 104 const enums = filterDataByKind(data, [TypeDocKind.Enum, TypeDocKind.LegacyEnum]); 105 const interfaces = filterDataByKind(data, TypeDocKind.Interface); 106 const constants = filterDataByKind(data, TypeDocKind.Variable, entry => isConstant(entry)); 107 108 const components = filterDataByKind( 109 data, 110 [TypeDocKind.Variable, TypeDocKind.Class, TypeDocKind.Function], 111 entry => isComponent(entry) 112 ); 113 const componentsPropNames = components.map(component => `${component.name}Props`); 114 const componentsProps = filterDataByKind(props, TypeDocKind.TypeAlias, entry => 115 componentsPropNames.includes(entry.name) 116 ); 117 118 const classes = filterDataByKind( 119 data, 120 TypeDocKind.Class, 121 entry => !isComponent(entry) && (apiName ? !entry.name.includes(apiName) : true) 122 ); 123 124 const componentsChildren = components 125 .map((cls: ClassDefinitionData) => 126 cls.children?.filter( 127 child => 128 child.kind === TypeDocKind.Method && 129 child?.flags?.isExternal !== true && 130 child.name !== 'render' && 131 // note(simek): hide unannotated "private" methods 132 !child.name.startsWith('_') 133 ) 134 ) 135 .flat(); 136 137 const methodsNames = methods.map(method => method.name); 138 const staticMethods = componentsChildren.filter( 139 // note(simek): hide duplicate exports for Camera API 140 method => method?.flags?.isStatic === true && !methodsNames.includes(method.name) 141 ); 142 const componentMethods = componentsChildren 143 .filter(method => method?.flags?.isStatic !== true && !method?.overwrites) 144 .filter(Boolean); 145 146 return ( 147 <> 148 <APISectionComponents data={components} componentsProps={componentsProps} /> 149 <APISectionMethods data={staticMethods} header="Static Methods" /> 150 <APISectionMethods data={componentMethods} header="Component Methods" /> 151 <APISectionConstants data={constants} apiName={apiName} /> 152 <APISectionMethods data={hooks} header="Hooks" /> 153 <APISectionClasses data={classes} /> 154 {props && !componentsProps.length ? ( 155 <APISectionProps data={props} defaultProps={defaultProps} /> 156 ) : null} 157 <APISectionMethods data={methods} apiName={apiName} /> 158 <APISectionMethods 159 data={eventSubscriptions} 160 apiName={apiName} 161 header="Event Subscriptions" 162 /> 163 <APISectionTypes data={types} /> 164 <APISectionInterfaces data={interfaces} /> 165 <APISectionEnums data={enums} /> 166 </> 167 ); 168 } catch (error) { 169 return <P>No API data file found, sorry!</P>; 170 } 171}; 172 173const APISection = ({ packageName, apiName, forceVersion }: Props) => { 174 const { version } = useContext(DocumentationPageContext); 175 const resolvedVersion = 176 forceVersion || 177 (version === 'unversioned' ? version : version === 'latest' ? LATEST_VERSION : version); 178 return renderAPI(packageName, resolvedVersion, apiName, !!forceVersion); 179}; 180 181export default APISection; 182