import { AstNode , Expression , IfStatement , Statement } from 'ast-types' ; import { NodePath } from 'ast-types' ; import { Collection , JsCodeShift } from 'jscodeshift' ; import { CodeModExports } from '../codeModTypes' ; import { getSingleStatement } from '../utils/astHelpers' ; function getParentIf ( j , path ) { if ( path . parent && j . IfStatement . check ( path . parent . node ) ) { return path . parent as NodePath < IfStatement > ; } if ( path . parent . parent && j . IfStatement . check ( path . parent . parent . node ) ) { return path . parent . parent as NodePath < IfStatement > ; } return null ; } function isMergableChildIf ( j , n ) { return Boolean ( n && j . IfStatement . check ( n ) && ! n . alternate ) ; } function getValidParentIf ( j , path ) { const parentIf = getParentIf ( j , path ) ; const validParent = parentIf && isMergableChildIf ( j , parentIf . node ) && getSingleStatement ( j , parentIf . node . consequent ) === path . node ; return validParent ? parentIf : null ; } const codeMod = ( ( fileInfo , api , options ) => { const j = api . jscodeshift ; const ast = fileInfo . ast ; const target = options . target ; const path = target . firstPath < IfStatement > ( ) ! ; let topIf = path ; let ifArr = [ ] ; let currentPath = path ; while ( currentPath ) { let parent = getValidParentIf ( j , currentPath ) ; if ( parent ) { topIf = parent ; ifArr . unshift ( parent . node ) ; } currentPath = parent ; } let currentNode = path . node ; while ( isMergableChildIf ( j , currentNode ) ) { ifArr . push ( currentNode ) ; currentNode = getSingleStatement ( j , currentNode . consequent ) ; } const testExpression = ifArr . map ( ( s ) => s . test ) . reduce ( ( prev , current ) => ( prev ? j . logicalExpression ( '&&' , prev , current ) : current ) , null as Expression | null ) ! ; const consequentStatement = ifArr [ ifArr . length - 1 ] . consequent ; const newIfStatement = j . ifStatement ( testExpression , consequentStatement ) ; newIfStatement . comments = topIf . node . comments ; topIf . replace ( newIfStatement ) ; const resultText = ast . toSource ( ) ; return resultText ; } ) as CodeModExports ; codeMod . canRun = ( fileInfo , api , options ) => { const j = api . jscodeshift ; const ast = fileInfo . ast ; const target = options . target ; const path = target . firstPath ( ) ; if ( ! path ) { return false ; } if ( isMergableChildIf ( j , path . node ) ) { const childConsequent = getSingleStatement ( j , path . node . consequent ) ; if ( isMergableChildIf ( j , childConsequent ) ) { return true ; } return Boolean ( getValidParentIf ( j , path ) ) ; } return false ; } ; codeMod . scope = 'cursor' ; codeMod . title = 'Merge into one if-statement' ; codeMod . description = '' ; codeMod . detail = '' ; module . exports = codeMod ;