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