@supernovaio/cli
Version:
Supernova.io Command Line Interface
213 lines (211 loc) • 7.61 kB
JavaScript
!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