@structured-types/react-plugin
Version:
react plugin for structured-types
190 lines (187 loc) • 9.71 kB
JavaScript
import * as ts from 'typescript';
import { getObjectStaticProp, PropKind, getFunctionLike, isSignatureDeclaration, getSymbolDeclaration, isObjectTypeDeclaration, getInitializer, getSymbolType } from '@structured-types/api';
const defaultReactParseOptions = {
collectGenerics: false,
collectParameters: false,
collectInheritance: false,
collectAliasName: false,
};
const getNodeResults = (parser, node) => {
var _a;
if (ts.isCallExpression(node) && node.arguments.length) {
return getNodeResults(parser, node.arguments[0]);
}
const symbol = parser.checker.getSymbolAtLocation(node);
const type = parser.checker.getTypeAtLocation(node);
if (type) {
const typeSymbol = type.aliasSymbol || type.symbol;
const typeDeclaration = ts.isIdentifier(node)
? getSymbolDeclaration(symbol)
: getSymbolDeclaration(typeSymbol);
const results = typesResolve({
parser,
declaration: typeDeclaration,
symbolType: type,
});
if (symbol && results && !((_a = results.prop) === null || _a === void 0 ? void 0 : _a.name)) {
results.prop = {
...results.prop,
kind: PropKind.Component,
name: symbol.getName(),
};
}
return results;
}
return undefined;
};
const typesResolve = ({ symbolType, declaration, expression, parser, }) => {
var _a, _b, _c;
if (declaration) {
if (symbolType.flags &
(ts.TypeFlags.Object | ts.TypeFlags.StructuredType)) {
const { checker } = parser;
const functionCall = expression &&
ts.isCallExpression(expression) &&
expression.arguments.length
? expression.arguments[0]
: undefined;
if (functionCall) {
return getNodeResults(parser, functionCall);
}
const symbol = symbolType.aliasSymbol || symbolType.symbol;
if (symbol &&
[
'MemoExoticComponent',
'NamedExoticComponent',
'ForwardRefExoticComponent',
'ForwardRef',
].includes(symbol.getName())) {
let type;
let initializer;
const props = ts.hasOnlyExpressionInitializer(declaration) &&
declaration.initializer &&
ts.isCallExpression(declaration.initializer) &&
declaration.initializer.arguments.length &&
ts.isFunctionLike(declaration.initializer.arguments[0]) &&
declaration.initializer.arguments[0].parameters.length
? declaration.initializer.arguments[0].parameters[0]
: undefined;
if (props) {
initializer = ts.isParameter(props)
? props.initializer || props.name
: undefined;
type = checker.getTypeAtLocation(props);
}
else {
const typeRef = symbolType;
type = ((_a = typeRef.typeArguments) === null || _a === void 0 ? void 0 : _a.length)
? typeRef.typeArguments[0].isUnionOrIntersection() &&
typeRef.typeArguments[0].types.length
? typeRef.typeArguments[0].types[0]
: typeRef.typeArguments[0]
: undefined;
}
const displayName = getObjectStaticProp(declaration, 'displayName', checker);
const name = displayName && ts.isStringLiteral(displayName)
? displayName.text
: undefined;
const defaultProps = getObjectStaticProp(declaration, 'defaultProps', checker);
return {
type,
declaration,
prop: { kind: PropKind.Component, name },
initializer: defaultProps || initializer,
...defaultReactParseOptions,
};
}
const reactFunction = getFunctionLike(checker, expression || declaration);
if (reactFunction) {
const jsx = checker.getJsxIntrinsicTagNamesAt(reactFunction);
if (jsx.length) {
if (isSignatureDeclaration(reactFunction)) {
const signature = checker.getSignatureFromDeclaration(reactFunction);
if (signature) {
const returnType = checker.getReturnTypeOfSignature(signature);
const returnSymbol = returnType.aliasSymbol || returnType.symbol;
if (!returnSymbol ||
['Element', 'ReactNode'].includes(returnSymbol.getName())) {
let propsType = undefined;
let defaultProps = getObjectStaticProp(reactFunction.parent, 'defaultProps', checker) ||
getObjectStaticProp(reactFunction, 'defaultProps', checker);
const displayName = getObjectStaticProp(reactFunction.parent, 'displayName', checker) ||
((_b = reactFunction.name) === null || _b === void 0 ? void 0 : _b.getText()) ||
(ts.isVariableDeclaration(reactFunction.parent) &&
reactFunction.parent.name.getText());
if (reactFunction.parameters.length) {
const props = reactFunction.parameters[0];
if (!defaultProps && ts.isObjectBindingPattern(props.name)) {
defaultProps = props.name;
}
propsType = checker.getTypeAtLocation(props);
}
else {
propsType = checker.getTypeAtLocation(reactFunction);
}
const name = typeof displayName === 'string'
? displayName
: displayName && ts.isStringLiteral(displayName)
? displayName.text
: undefined;
return {
declaration: reactFunction.parent,
type: propsType,
initializer: defaultProps,
prop: { kind: PropKind.Component, name },
...defaultReactParseOptions,
};
}
}
}
else if (reactFunction.arguments.length) {
return getNodeResults(parser, reactFunction.arguments[0]);
}
}
}
const classDeclaration = getSymbolDeclaration(symbol);
if (classDeclaration && isObjectTypeDeclaration(classDeclaration)) {
const classImplementation = getSymbolDeclaration(checker.getSymbolAtLocation(getInitializer(declaration) || declaration)) || declaration;
const jsx = checker.getJsxIntrinsicTagNamesAt(declaration);
if (jsx.length) {
const signatures = symbolType.getConstructSignatures();
if (signatures.length > 0 && signatures[0].parameters.length) {
const props = signatures[0].parameters[0];
let propsType = getSymbolType(checker, props);
if (!propsType ||
propsType
.intrinsicName === 'any') {
propsType = symbolType;
}
if (propsType.isUnionOrIntersection()) {
propsType = propsType.types[0];
}
const displayName = getObjectStaticProp(classImplementation, 'displayName', checker);
const name = typeof displayName === 'string'
? displayName
: displayName && ts.isStringLiteral(displayName)
? displayName.text
: (_c = declaration.name) === null || _c === void 0 ? void 0 : _c.getText();
const defaultProps = getObjectStaticProp(classImplementation, 'defaultProps', checker) || getInitializer(classImplementation);
return {
type: propsType,
declaration,
prop: { kind: PropKind.Component, name },
initializer: defaultProps,
};
}
}
}
}
}
return undefined;
};
const reactPlugin = {
pluginName: 'react',
filter: (prop) => !(prop.name === 'children'),
typesResolve,
};
export { reactPlugin as default };