import path from 'path' ; import * as babel from '@babel/core' ; import { NodePath , Visitor } from '@babel/traverse' ; type FunctionDeclaration = babel . types . FunctionDeclaration ; const KEY_REACT_FUNCTIONS = new Set ( [ 'createElement' , 'cloneElement' ] ) ; const resolveCalleeName = ( path ) => { const callee = path . get ( 'callee' ) ; if ( callee . isIdentifier ( ) ) { return callee . node . name ; } if ( callee . isMemberExpression ( ) ) { const property = callee . get ( 'property' ) ; return property . isIdentifier ( ) ? property . node . name : '' ; } return '' ; } ; const isFunctionBodyComponentLike = ( path ) : boolean => { let matched = false ; const visitor = { JSXElement ( path ) { matched = true ; path . stop ( ) ; } , CallExpression ( path ) { const calleeName = resolveCalleeName ( path ) ; if ( KEY_REACT_FUNCTIONS . has ( calleeName ) ) { matched = true ; path . stop ( ) ; } } , } ; path . traverse ( visitor ) ; return matched ; } ; export const isComponentDeclaration = ( path , strict ? ) => { const functionName = path . node . id ?. name ?? '' ; const args = path . node . params ; return args . length <= 1 && /^[A-Z]/ . test ( functionName ) && ( ! strict || isFunctionBodyComponentLike ( path ) ) ; } ; export const resolveComponentName = ( declaration , filename ) => { const functionName = declaration . node . id ?. name ; if ( functionName ) { return functionName ; } if ( ! filename ) { return 'Unknown' ; } const file = path . basename ( filename , path . extname ( filename ) ) ; return file === 'index' ? path . dirname ( filename ) . split ( path . sep ) . pop ( ) : file ; } ;