All files readonly-shared.ts

100% Statements 44/44
93.1% Branches 27/29
100% Functions 8/8
100% Lines 42/42
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 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160            1x     1x 1x                               1x       197x       1x 27x   27x   16x   8x 8x     27x     1x         27x     2174x                     20x 20x           20x   20x     2154x   2154x       1x         2174x 197x               1x               20x     20x 12x 12x 12x         20x 7x 7x 7x       20x     1x           315x 144x               135x         135x         56x       259x    
/**
 * This file has code that is shared for all the readonly rules.
 * It supports the options for ignore-local and ignore-prefix which all readonly rules have.
 * The rules ony need to provide a checker function.
 */
 
import * as ts from "typescript";
import * as Lint from "tslint";
 
const OPTION_IGNORE_LOCAL = "ignore-local";
const OPTION_IGNORE_PREFIX = "ignore-prefix";
 
export interface CheckNodeFunction {
  (node: ts.Node, ctx: Lint.WalkContext<Options>): ReadonlyArray<InvalidNode>;
}
 
export interface Options {
  readonly ignoreLocal: boolean;
  readonly ignorePrefix: string | undefined;
}
 
export interface InvalidNode {
  readonly node: ts.Node;
  readonly replacement: Lint.Replacement | undefined;
}
 
export function createInvalidNode(
  node: ts.Node,
  replacement?: Lint.Replacement
): InvalidNode {
  return { node, replacement };
}
 
//tslint:disable-next-line
export function parseOptions(options: any[]): Options {
  const ignoreLocal = options.indexOf(OPTION_IGNORE_LOCAL) !== -1;
  let ignorePrefix: string | undefined;
  for (const o of options) {
    //tslint:disable-next-line
    if (typeof o === "object" && o[OPTION_IGNORE_PREFIX] !== null) {
      //tslint:disable-line
      ignorePrefix = o[OPTION_IGNORE_PREFIX];
      break;
    }
  }
  return { ignoreLocal, ignorePrefix };
}
 
export function walk(
  ctx: Lint.WalkContext<Options>,
  checkNode: CheckNodeFunction,
  failureString: string
): void {
  return ts.forEachChild(ctx.sourceFile, cb);
  function cb(node: ts.Node): void {
    // Skip checking in functions if ignore-local is set
    if (
      ctx.options.ignoreLocal &&
      (node.kind === ts.SyntaxKind.FunctionDeclaration ||
        node.kind === ts.SyntaxKind.ArrowFunction ||
        node.kind === ts.SyntaxKind.FunctionExpression ||
        node.kind === ts.SyntaxKind.MethodDeclaration)
    ) {
      // We still need to check the parameters and return type
      const functionNode:
        | ts.FunctionDeclaration
        | ts.ArrowFunction
        | ts.MethodDeclaration = node as any; //tslint:disable-line
      const invalidNodes = checkIgnoreLocalFunctionNode(
        functionNode,
        ctx,
        checkNode
      );
      // invalidNodes.forEach((n) => reportInvalidNodes(n, ctx, failureString));
      reportInvalidNodes(invalidNodes, ctx, failureString);
      // Now skip this whole branch
      return;
    }
    // Check the node
    reportInvalidNodes(checkNode(node, ctx), ctx, failureString);
    // Use return becuase performance hints docs say it optimizes the function using tail-call recursion
    return ts.forEachChild(node, cb);
  }
}
 
export function reportInvalidNodes(
  invalidNodes: ReadonlyArray<InvalidNode>,
  ctx: Lint.WalkContext<Options>,
  failureString: string
): void {
  invalidNodes.forEach(invalidNode =>
    ctx.addFailureAtNode(
      invalidNode.node,
      failureString,
      invalidNode.replacement
    )
  );
}
 
export function checkIgnoreLocalFunctionNode(
  functionNode:
    | ts.FunctionDeclaration
    | ts.ArrowFunction
    | ts.MethodDeclaration,
  ctx: Lint.WalkContext<Options>,
  checkNode: CheckNodeFunction
): ReadonlyArray<InvalidNode> {
  let invalidNodes: Array<InvalidNode> = [];
 
  // Check either the parameter's explicit type if it has one, or itself for implict type
  for (const n of functionNode.parameters.map(p => (p.type ? p.type : p))) {
    const invalidCheckNodes = checkNode(n, ctx);
    Eif (invalidCheckNodes) {
      invalidNodes = invalidNodes.concat(...invalidCheckNodes);
    }
  }
 
  // Check the return type
  if (functionNode.type) {
    const invalidCheckNodes = checkNode(functionNode.type, ctx);
    Eif (invalidCheckNodes) {
      invalidNodes = invalidNodes.concat(...invalidCheckNodes);
    }
  }
 
  return invalidNodes;
}
 
export function shouldIgnorePrefix(
  node: ts.Node,
  options: Options,
  sourceFile: ts.SourceFile
): boolean {
  // Check ignore-prefix for VariableDeclaration, PropertySignature, TypeAliasDeclaration, Parameter
  if (options.ignorePrefix) {
    if (
      node &&
      (node.kind === ts.SyntaxKind.VariableDeclaration ||
        node.kind === ts.SyntaxKind.Parameter ||
        node.kind === ts.SyntaxKind.PropertySignature ||
        node.kind === ts.SyntaxKind.PropertyDeclaration ||
        node.kind === ts.SyntaxKind.TypeAliasDeclaration)
    ) {
      const variableDeclarationNode = node as
        | ts.VariableDeclaration
        | ts.PropertySignature
        | ts.TypeAliasDeclaration
        | ts.ParameterDeclaration;
      if (
        variableDeclarationNode.name
          .getText(sourceFile)
          .substr(0, options.ignorePrefix.length) === options.ignorePrefix
      ) {
        return true;
      }
    }
  }
  return false;
}