import * as ts from 'typescript' ; import * as Lint from 'tslint' ; import { ErrorTolerantWalker } from './utils/ErrorTolerantWalker' ; import { ExtendedMetadata } from './utils/ExtendedMetadata' ; /**
 * Implementation of the no-map-without-usage rule.
 */ export class Rule extends Lint . Rules . AbstractRule { public static metadata : ExtendedMetadata = { ruleName : 'no-map-without-usage' , type : 'maintainability' , description : 'Prevent Array.prototype.map from being called and results not used.' , options : null , optionsDescription : '' , optionExamples : [ ] , typescriptOnly : false , issueClass : 'Non-SDL' , issueType : 'Warning' , severity : 'Low' , level : 'Opportunity for Excellence' , group : 'Correctness' , commonWeaknessEnumeration : '' , } ; public static FAILURE_STRING : string = 'Return value from Array.prototype.map should be assigned to a variable. ' + 'Consider using Array.prototype.forEach instead.' ; public apply ( sourceFile ) { return this . applyWithWalker ( new NoMapWithoutAssignmentRuleWalker ( sourceFile , this . getOptions ( ) ) ) ; } } class NoMapWithoutAssignmentRuleWalker extends ErrorTolerantWalker { protected visitPropertyAccessExpression ( node ) { this . checkAndReport ( node ) ; super . visitPropertyAccessExpression ( node ) ; } private checkAndReport ( node ) { if ( this . isMapCall ( node ) && ! this . isAssignment ( node ) && ! this . isUsed ( node ) ) { this . addFailureAtNode ( node , Rule . FAILURE_STRING ) ; } } private isMapCall ( node ) { const isCallExpression = ts . isCallExpression ( node . parent ) ; const isMap = node . name . text === 'map' ; return isCallExpression && isMap ; } private isAssignment ( node ) { const { parent : parent1 } = node ; if ( parent1 && ts . isCallExpression ( parent1 ) ) { const { parent : parent2 } = parent1 ; const parentIsAssignment = ts . isPropertyAssignment ( parent2 ) || ts . isVariableDeclaration ( parent2 ) || ( ts . isBinaryExpression ( parent2 ) && parent2 . operatorToken . kind === ts . SyntaxKind . FirstAssignment ) ; if ( parentIsAssignment ) { return true ; } } return false ; } private isUsed ( node ) { const { parent : parent1 } = node ; if ( parent1 && ts . isCallExpression ( parent1 ) ) { const { parent : parent2 } = parent1 ; if ( this . parentUsesNode ( parent2 ) ) { return true ; } } return false ; } private parentUsesNode ( parent ? ) { return ( parent && ( ts . isPropertyAccessExpression ( parent ) || ts . isPropertyDeclaration ( parent ) || ts . isReturnStatement ( parent ) || ts . isCallOrNewExpression ( parent ) || ts . isSpreadElement ( parent ) || ts . isJsxExpression ( parent ) || ts . isConditionalExpression ( parent ) || ts . isArrayLiteralExpression ( parent ) || ts . isBinaryExpression ( parent ) || ts . isArrowFunction ( parent ) ) ) ; } }