import React from 'react';
import { P } from '~/components/base/paragraph';
import { ClassDefinitionData, GeneratedData } from '~/components/plugins/api/APIDataTypes';
import APISectionClasses from '~/components/plugins/api/APISectionClasses';
import APISectionComponents from '~/components/plugins/api/APISectionComponents';
import APISectionConstants from '~/components/plugins/api/APISectionConstants';
import APISectionEnums from '~/components/plugins/api/APISectionEnums';
import APISectionInterfaces from '~/components/plugins/api/APISectionInterfaces';
import APISectionMethods from '~/components/plugins/api/APISectionMethods';
import APISectionProps from '~/components/plugins/api/APISectionProps';
import APISectionTypes from '~/components/plugins/api/APISectionTypes';
import { getComponentName, TypeDocKind } from '~/components/plugins/api/APISectionUtils';
import { usePageApiVersion } from '~/providers/page-api-version';
import versions from '~/public/static/constants/versions.json';
const { LATEST_VERSION } = versions;
type Props = {
packageName: string;
apiName?: string;
forceVersion?: string;
strictTypes?: boolean;
testRequire?: any;
};
const filterDataByKind = (
entries: GeneratedData[] = [],
kind: TypeDocKind | TypeDocKind[],
additionalCondition: (entry: GeneratedData) => boolean = () => true
) =>
entries.filter(
(entry: GeneratedData) =>
(Array.isArray(kind) ? kind.includes(entry.kind) : entry.kind === kind) &&
additionalCondition(entry)
);
const isHook = ({ name }: GeneratedData) =>
name.startsWith('use') &&
// note(simek): hardcode this exception until the method will be renamed
name !== 'useSystemBrightnessAsync';
const isListener = ({ name }: GeneratedData) =>
name.endsWith('Listener') || name.endsWith('Listeners');
const isProp = ({ name }: GeneratedData) => name.includes('Props') && name !== 'ErrorRecoveryProps';
const isComponent = ({ type, extendedTypes, signatures }: GeneratedData) => {
if (type?.name && ['React.FC', 'ForwardRefExoticComponent'].includes(type?.name)) {
return true;
} else if (extendedTypes && extendedTypes.length) {
return extendedTypes[0].name === 'Component';
} else if (signatures && signatures.length) {
if (
signatures[0].type.name === 'Element' ||
(signatures[0].parameters && signatures[0].parameters[0].name === 'props')
) {
return true;
}
}
return false;
};
const isConstant = ({ name, type }: GeneratedData) =>
!['default', 'Constants', 'EventEmitter'].includes(name) &&
!(type?.name && ['React.FC', 'ForwardRefExoticComponent'].includes(type?.name));
const renderAPI = (
packageName: string,
version: string = 'unversioned',
apiName?: string,
strictTypes: boolean = false,
testRequire: any = undefined
): JSX.Element => {
try {
const { children: data } = testRequire
? testRequire(`~/public/static/data/${version}/${packageName}.json`)
: require(`~/public/static/data/${version}/${packageName}.json`);
const methods = filterDataByKind(
data,
TypeDocKind.Function,
entry => !isListener(entry) && !isHook(entry) && !isComponent(entry)
);
const eventSubscriptions = filterDataByKind(data, TypeDocKind.Function, isListener);
const types = filterDataByKind(
data,
TypeDocKind.TypeAlias,
entry =>
!isProp(entry) &&
!!(
entry.type.declaration ||
entry.type.types ||
entry.type.type ||
entry.type.typeArguments
) &&
(strictTypes && apiName ? entry.name.startsWith(apiName) : true)
);
const props = filterDataByKind(
data,
TypeDocKind.TypeAlias,
entry => isProp(entry) && !!(entry.type.types || entry.type.declaration?.children)
);
const defaultProps = filterDataByKind(
data
.filter((entry: GeneratedData) => entry.kind === TypeDocKind.Class)
.map((entry: GeneratedData) => entry.children)
.flat(),
TypeDocKind.Property,
entry => entry.name === 'defaultProps'
)[0];
const enums = filterDataByKind(
data,
[TypeDocKind.Enum, TypeDocKind.LegacyEnum],
entry => entry.name !== 'default'
);
const interfaces = filterDataByKind(data, TypeDocKind.Interface);
const constants = filterDataByKind(data, TypeDocKind.Variable, entry => isConstant(entry));
const components = filterDataByKind(
data,
[TypeDocKind.Variable, TypeDocKind.Class, TypeDocKind.Function],
entry => isComponent(entry)
);
const componentsPropNames = components.map(
({ name, children }) => `${getComponentName(name, children)}Props`
);
const componentsProps = filterDataByKind(props, TypeDocKind.TypeAlias, entry =>
componentsPropNames.includes(entry.name)
);
const classes = filterDataByKind(data, TypeDocKind.Class, entry => !isComponent(entry));
const componentsChildren = components
.map((cls: ClassDefinitionData) =>
cls.children?.filter(
child =>
(child?.kind === TypeDocKind.Method || child?.kind === TypeDocKind.Property) &&
child?.flags?.isExternal !== true &&
!child.inheritedFrom &&
child.name !== 'render' &&
// note(simek): hide unannotated "private" methods
!child.name.startsWith('_')
)
)
.flat();
const methodsNames = methods.map(method => method.name);
const staticMethods = componentsChildren.filter(
// note(simek): hide duplicate exports from class components
method =>
method?.kind === TypeDocKind.Method &&
method?.flags?.isStatic === true &&
!methodsNames.includes(method.name) &&
!isHook(method as GeneratedData)
);
const componentMethods = componentsChildren
.filter(
method =>
method?.kind === TypeDocKind.Method &&
method?.flags?.isStatic !== true &&
!method?.overwrites
)
.filter(Boolean);
const hooks = filterDataByKind(
[...data, ...componentsChildren].filter(Boolean),
[TypeDocKind.Function, TypeDocKind.Property],
isHook
);
return (
<>
No API data file found, sorry!
; } }; const APISection = ({ packageName, apiName, forceVersion, strictTypes = false, testRequire = undefined, }: Props) => { const { version } = usePageApiVersion(); const resolvedVersion = forceVersion || (version === 'unversioned' ? version : version === 'latest' ? LATEST_VERSION : version); return renderAPI(packageName, resolvedVersion, apiName, strictTypes, testRequire); }; export default APISection;