All files noObjectMutationRule.ts

100% Statements 38/38
100% Branches 38/38
100% Functions 9/9
100% Lines 38/38
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 1241x   1x           1x         1x         1x         1x                               1x                 300x     300x 23x 23x 43x 113x             19x         300x 4x 4x 7x           3x         300x 6x 6x 12x 9x           3x         300x 5x 5x 10x 6x           4x       300x       20x 20x 47x 1x   46x   19x    
import * as ts from "typescript";
import * as Lint from "tslint";
import {
  createInvalidNode,
  CheckNodeResult,
  createCheckNodeRule,
  InvalidNode
} from "./shared/check-node";
import * as Ignore from "./shared/ignore";
 
type Options = Ignore.IgnorePrefixOption;
 
// tslint:disable-next-line:variable-name
export const Rule = createCheckNodeRule(
  checkNode,
  "Modifying properties of existing object not allowed."
);
 
const objPropAccessors: ReadonlyArray<ts.SyntaxKind> = [
  ts.SyntaxKind.ElementAccessExpression,
  ts.SyntaxKind.PropertyAccessExpression
];
 
const forbidObjPropOnLeftSideOf: ReadonlyArray<ts.SyntaxKind> = [
  ts.SyntaxKind.EqualsToken,
  ts.SyntaxKind.PlusEqualsToken,
  ts.SyntaxKind.MinusEqualsToken,
  ts.SyntaxKind.AsteriskEqualsToken,
  ts.SyntaxKind.AsteriskAsteriskEqualsToken,
  ts.SyntaxKind.SlashEqualsToken,
  ts.SyntaxKind.PercentEqualsToken,
  ts.SyntaxKind.LessThanLessThanEqualsToken,
  ts.SyntaxKind.GreaterThanGreaterThanEqualsToken,
  ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken,
  ts.SyntaxKind.AmpersandEqualsToken,
  ts.SyntaxKind.BarEqualsToken,
  ts.SyntaxKind.CaretEqualsToken
];
 
const forbidUnaryOps: ReadonlyArray<ts.SyntaxKind> = [
  ts.SyntaxKind.PlusPlusToken,
  ts.SyntaxKind.MinusMinusToken
];
 
function checkNode(
  node: ts.Node,
  ctx: Lint.WalkContext<Options>
): CheckNodeResult {
  let invalidNodes: Array<InvalidNode> = [];
 
  // No assignment with object.property on the left
  if (node && node.kind === ts.SyntaxKind.BinaryExpression) {
    const binExp = node as ts.BinaryExpression;
    if (
      objPropAccessors.some(k => k === binExp.left.kind) &&
      forbidObjPropOnLeftSideOf.some(k => k === binExp.operatorToken.kind) &&
      !Ignore.isIgnoredPrefix(
        binExp.getText(node.getSourceFile()),
        ctx.options.ignorePrefix
      ) &&
      !inConstructor(node)
    ) {
      invalidNodes = [...invalidNodes, createInvalidNode(node, [])];
    }
  }
 
  // No deleting object properties
  if (node && node.kind === ts.SyntaxKind.DeleteExpression) {
    const delExp = node as ts.DeleteExpression;
    if (
      objPropAccessors.some(k => k === delExp.expression.kind) &&
      !Ignore.isIgnoredPrefix(
        delExp.expression.getText(node.getSourceFile()),
        ctx.options.ignorePrefix
      )
    ) {
      invalidNodes = [...invalidNodes, createInvalidNode(node, [])];
    }
  }
 
  // No prefix inc/dec
  if (node && node.kind === ts.SyntaxKind.PrefixUnaryExpression) {
    const preExp = node as ts.PrefixUnaryExpression;
    if (
      objPropAccessors.some(k => k === preExp.operand.kind) &&
      forbidUnaryOps.some(o => o === preExp.operator) &&
      !Ignore.isIgnoredPrefix(
        preExp.operand.getText(node.getSourceFile()),
        ctx.options.ignorePrefix
      )
    ) {
      invalidNodes = [...invalidNodes, createInvalidNode(node, [])];
    }
  }
 
  // No postfix inc/dec
  if (node && node.kind === ts.SyntaxKind.PostfixUnaryExpression) {
    const postExp = node as ts.PostfixUnaryExpression;
    if (
      objPropAccessors.some(k => k === postExp.operand.kind) &&
      forbidUnaryOps.some(o => o === postExp.operator) &&
      !Ignore.isIgnoredPrefix(
        postExp.getText(node.getSourceFile()),
        ctx.options.ignorePrefix
      )
    ) {
      invalidNodes = [...invalidNodes, createInvalidNode(node, [])];
    }
  }
 
  return { invalidNodes };
}
 
function inConstructor(nodeIn: ts.Node): boolean {
  let node = nodeIn.parent;
  while (node) {
    if (node.kind === ts.SyntaxKind.Constructor) {
      return true;
    }
    node = node.parent;
  }
  return false;
}