src/utils/getMemberValuePath.ts (67 lines of code) (raw):
import { namedTypes as t } from 'ast-types';
import getClassMemberValuePath from './getClassMemberValuePath';
import getMemberExpressionValuePath from './getMemberExpressionValuePath';
import getPropertyValuePath from './getPropertyValuePath';
import resolveFunctionDefinitionToReturnValue from '../utils/resolveFunctionDefinitionToReturnValue';
import type { Importer } from '../parse';
import type { NodePath } from 'ast-types/lib/node-path';
const SYNONYMS = {
getDefaultProps: 'defaultProps',
defaultProps: 'getDefaultProps',
};
const postprocessPropTypes = (path, importer) =>
t.Function.check(path.node)
? resolveFunctionDefinitionToReturnValue(path, importer)
: path;
const POSTPROCESS_MEMBERS = new Map([['propTypes', postprocessPropTypes]]);
const LOOKUP_METHOD = new Map([
// @ts-ignore
[t.ArrowFunctionExpression.name, getMemberExpressionValuePath],
// @ts-ignore
[t.CallExpression.name, getMemberExpressionValuePath],
// @ts-ignore
[t.FunctionExpression.name, getMemberExpressionValuePath],
// @ts-ignore
[t.FunctionDeclaration.name, getMemberExpressionValuePath],
// @ts-ignore
[t.VariableDeclaration.name, getMemberExpressionValuePath],
// @ts-ignore
[t.ObjectExpression.name, getPropertyValuePath],
// @ts-ignore
[t.ClassDeclaration.name, getClassMemberValuePath],
// @ts-ignore
[t.ClassExpression.name, getClassMemberValuePath],
]);
export function isSupportedDefinitionType({ node }: NodePath): boolean {
return (
t.ObjectExpression.check(node) ||
t.ClassDeclaration.check(node) ||
t.ClassExpression.check(node) ||
/**
* Adds support for libraries such as
* [styled components]{@link https://github.com/styled-components} that use
* TaggedTemplateExpression's to generate components.
*
* While react-docgen's built-in resolvers do not support resolving
* TaggedTemplateExpression definitions, third-party resolvers (such as
* https://github.com/Jmeyering/react-docgen-annotation-resolver) could be
* used to add these definitions.
*/
t.TaggedTemplateExpression.check(node) ||
// potential stateless function component
t.VariableDeclaration.check(node) ||
t.ArrowFunctionExpression.check(node) ||
t.FunctionDeclaration.check(node) ||
t.FunctionExpression.check(node) ||
/**
* Adds support for libraries such as
* [system-components]{@link https://jxnblk.com/styled-system/system-components} that use
* CallExpressions to generate components.
*
* While react-docgen's built-in resolvers do not support resolving
* CallExpressions definitions, third-party resolvers (such as
* https://github.com/Jmeyering/react-docgen-annotation-resolver) could be
* used to add these definitions.
*/
t.CallExpression.check(node)
);
}
/**
* This is a helper method for handlers to make it easier to work either with
* an ObjectExpression from `React.createClass` class or with a class
* definition.
*
* Given a path and a name, this function will either return the path of the
* property value if the path is an ObjectExpression, or the value of the
* ClassProperty/MethodDefinition if it is a class definition (declaration or
* expression).
*
* It also normalizes the names so that e.g. `defaultProps` and
* `getDefaultProps` can be used interchangeably.
*/
export default function getMemberValuePath(
componentDefinition: NodePath,
memberName: string,
importer: Importer,
): NodePath | null {
if (!isSupportedDefinitionType(componentDefinition)) {
throw new TypeError(
'Got unsupported definition type. Definition must be one of ' +
'ObjectExpression, ClassDeclaration, ClassExpression,' +
'VariableDeclaration, ArrowFunctionExpression, FunctionExpression, ' +
'TaggedTemplateExpression, FunctionDeclaration or CallExpression. Got "' +
componentDefinition.node.type +
'" instead.',
);
}
const lookupMethod =
LOOKUP_METHOD.get(componentDefinition.node.type) ||
getMemberExpressionValuePath;
let result = lookupMethod(componentDefinition, memberName, importer);
if (!result && SYNONYMS[memberName]) {
result = lookupMethod(componentDefinition, SYNONYMS[memberName], importer);
}
const postprocessMethod = POSTPROCESS_MEMBERS.get(memberName);
if (result && postprocessMethod) {
result = postprocessMethod(result, importer);
}
return result;
}