UNPKG

@supernovaio/cli

Version:

Supernova.io Command Line Interface

213 lines (211 loc) 7.61 kB
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="17cefb0a-c2ec-5931-9323-ed1d6e9c7b90")}catch(e){}}(); import * as ts from "typescript"; const validReactTypes = [ "FC", "FunctionComponent", "Component", "Element", "ReactElement", "ForwardRefExoticComponent", "ReactNode", ]; const validReactComponentTypeAnnotations = new Set([ "ComponentType", "FC", "ForwardRefExoticComponent", "FunctionComponent", "MemoExoticComponent", ]); function isPascalCaseSymbolName(name) { return name === "default" || /^[A-Z]/.test(name); } function hasReactRenderSyntax(node) { let containsJsx = false; let hasReactCreateElementCall = false; const visit = (current) => { if (containsJsx && hasReactCreateElementCall) { return; } if (ts.isJsxElement(current) || ts.isJsxSelfClosingElement(current) || ts.isJsxFragment(current)) { containsJsx = true; } if (ts.isCallExpression(current) && ts.isPropertyAccessExpression(current.expression) && current.expression.expression.getText() === "React" && current.expression.name.getText() === "createElement") { hasReactCreateElementCall = true; } ts.forEachChild(current, visit); }; visit(node); return containsJsx || hasReactCreateElementCall; } function isFunctionLikeWithReactOutput(node) { if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node)) { return hasReactRenderSyntax(node); } return false; } function getTypeAnnotationName(node) { if (!ts.isTypeReferenceNode(node)) { return null; } const { typeName } = node; if (ts.isIdentifier(typeName)) { return typeName.text; } if (ts.isQualifiedName(typeName) && typeName.left.getText() === "React") { return typeName.right.text; } return null; } function isReactComponentTypeAnnotation(node) { const typeName = getTypeAnnotationName(node); return typeName ? validReactComponentTypeAnnotations.has(typeName) : false; } function hasRenderMethodWithJsx(classNode) { for (const member of classNode.members) { if (!ts.isMethodDeclaration(member)) { continue; } const methodName = member.name?.getText(); if (methodName !== "render") { continue; } if (hasReactRenderSyntax(member)) { return true; } } return false; } function unwrapExpression(node) { let current = node; while (ts.isParenthesizedExpression(current) || ts.isAsExpression(current) || ts.isSatisfiesExpression(current) || ts.isTypeAssertionExpression(current) || ts.isNonNullExpression(current)) { current = current.expression; } return current; } function resolveAliasedSymbol(symbol, checker) { return symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol; } function reactWrapperName(node) { const expression = unwrapExpression(node); if (ts.isIdentifier(expression)) { return expression.text; } if (ts.isPropertyAccessExpression(expression) && ts.isIdentifier(expression.expression)) { return `${expression.expression.text}.${expression.name.text}`; } return null; } function isForwardRefOrMemoCall(node, checker, seen) { const expressionText = reactWrapperName(node.expression); if (expressionText !== "forwardRef" && expressionText !== "memo" && expressionText !== "React.forwardRef" && expressionText !== "React.memo") { return false; } const firstArg = node.arguments[0]; if (!firstArg) { return false; } return isReactComponentExpression(firstArg, checker, seen); } function isReactComponentCall(node, checker, seen) { return isForwardRefOrMemoCall(node, checker, seen); } function isReactComponentExpression(node, checker, seen) { const expression = unwrapExpression(node); if (isFunctionLikeWithReactOutput(expression)) { return true; } if (ts.isCallExpression(expression)) { return isReactComponentCall(expression, checker, seen); } if (ts.isIdentifier(expression)) { const symbol = checker.getSymbolAtLocation(expression); if (!symbol) { return false; } return isReactComponentBySyntax(resolveAliasedSymbol(symbol, checker), checker, seen, false); } return false; } function isReactComponentBySyntax(symbol, checker, seen, requirePascalCase = true) { if (seen.has(symbol)) { return false; } seen.add(symbol); if (requirePascalCase && !isPascalCaseSymbolName(symbol.getName())) { return false; } for (const declaration of symbol.declarations ?? []) { if (isFunctionLikeWithReactOutput(declaration)) { return true; } if (ts.isVariableDeclaration(declaration) && declaration.initializer) { if (declaration.type && isReactComponentTypeAnnotation(declaration.type)) { return true; } const initializer = unwrapExpression(declaration.initializer); if (isFunctionLikeWithReactOutput(initializer)) { return true; } if (ts.isCallExpression(initializer) && isReactComponentCall(initializer, checker, seen)) { return true; } } if (ts.isClassDeclaration(declaration) && hasRenderMethodWithJsx(declaration)) { return true; } if (ts.isExportAssignment(declaration)) { const expression = unwrapExpression(declaration.expression); if (isFunctionLikeWithReactOutput(expression)) { return true; } if (ts.isCallExpression(expression) && isReactComponentCall(expression, checker, seen)) { return true; } } } return false; } export function isTypeFromReact(type, checker) { if (type.isUnion()) { return type.types.some(t => isTypeFromReact(t, checker)); } if (type.symbol && type.symbol.declarations && type.symbol.declarations.length > 0) { const declaration = type.symbol.declarations[0]; const sourceFile = declaration.getSourceFile(); const { fileName } = sourceFile; const typeString = checker.typeToString(type); const isReactType = Boolean(typeString.match(new RegExp(`^(React[.])?(?:${validReactTypes.join("|")}).*`))?.length); return isReactType && fileName.includes("@types/react"); } return false; } export function isReactComponent(symbol, _sourceFile, checker) { const actualSymbol = resolveAliasedSymbol(symbol, checker); const type = checker.getTypeOfSymbol(actualSymbol); if (isTypeFromReact(type, checker)) { return true; } const [signature] = type.getCallSignatures(); if (signature) { const returnType = checker.getReturnTypeOfSignature(signature); if (isTypeFromReact(returnType, checker)) { return true; } } return isReactComponentBySyntax(actualSymbol, checker, new Set()); } //# sourceMappingURL=is-react-component.js.map //# debugId=17cefb0a-c2ec-5931-9323-ed1d6e9c7b90