import * as ts from 'typescript' ; import * as Lint from 'tslint' ; import { ErrorTolerantWalker } from './utils/ErrorTolerantWalker' ; import { ExtendedMetadata } from './utils/ExtendedMetadata' ; /**
 * Implementation of the id-length rule.
 */ export class Rule extends Lint . Rules . AbstractRule { public static metadata : ExtendedMetadata = { ruleName : 'no-for-each-push' , type : 'maintainability' , description : 'Enforce using Array.prototype.map instead of Array.prototype.forEach and Array.prototype.push.' , options : null , optionsDescription : '' , typescriptOnly : true , issueClass : 'Non-SDL' , issueType : 'Warning' , severity : 'Important' , level : 'Opportunity for Excellence' , group : 'Correctness' , recommendation : 'true,' , commonWeaknessEnumeration : '' , } ; public apply ( sourceFile ) { return this . applyWithWalker ( new NoForeachPushRuleWalker ( sourceFile , this . getOptions ( ) ) ) ; } public static FAILURE_STRING : string = 'Do not use Array.prototype.push inside of Array.prototype.forEach. ' + 'Use Array.prototype.map instead to replace both.' ; } class NoForeachPushRuleWalker extends ErrorTolerantWalker { protected visitPropertyAccessExpression ( node ) { this . checkAndReport ( node ) ; super . visitPropertyAccessExpression ( node ) ; } private checkAndReport ( node ) { const isCallExpression = ts . isCallExpression ( node . parent ) ; const isForEach = node . name . text === 'forEach' ; if ( isCallExpression && isForEach ) { if ( this . doesCallPush ( node ) ) { this . addFailureAtNode ( node . parent , Rule . FAILURE_STRING ) ; } } } private doesCallPush ( node ) { const walker = new PushCallWalker ( ) ; walker . walk ( node . parent ) ; return walker . isFound ; } } class PushCallWalker extends Lint . SyntaxWalker { private foundPush : boolean = false ; private foundIf : boolean = false ; protected visitPropertyAccessExpression ( node ) { const isCallExpression = ts . isCallExpression ( node . parent ) ; const isPush = node . name . text === 'push' ; if ( isCallExpression && isPush ) { this . foundPush = true ; return ; } super . visitPropertyAccessExpression ( node ) ; } protected visitIfStatement ( ) { this . foundIf = true ; return ; } public get isFound ( ) : boolean { return ! this . foundIf && this . foundPush ; } }