UNPKG

@structured-types/react-plugin

Version:
190 lines (187 loc) 9.71 kB
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 };