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