twada/espower

View on GitHub
lib/sig-matcher.js

Summary

Maintainability
A
45 mins
Test Coverage
'use strict';

const isIdentifier = (node) => node && node.type === 'Identifier';
const isCallExpression = (node) => node && node.type === 'CallExpression';
const hasIdentName = (node, name) => isIdentifier(node) && node.name === name;
const isCalleeOfParent = (currentNode, parentNode) => parentNode &&
          currentNode &&
          parentNode.type === 'CallExpression' &&
          parentNode.callee === currentNode;

class SigMatcher {
  constructor ({ callee, args }) {
    this.callee = callee;
    this.args = args;
    this.numMaxArgs = args.length;
    this.numMinArgs = args.filter((a) => a.optional === false).length;
  }

  test (currentNode) {
    if (this.isCalleeMatched(currentNode)) {
      const numArgs = currentNode.arguments.length;
      return this.numMinArgs <= numArgs && numArgs <= this.numMaxArgs;
    }
    return false;
  }

  matchArgument (currentNode, parentNode) {
    /* istanbul ignore next */
    if (isCalleeOfParent(currentNode, parentNode)) {
      return null;
    }
    if (this.test(parentNode)) {
      const indexOfCurrentArg = parentNode.arguments.indexOf(currentNode);
      let numOptional = parentNode.arguments.length - this.numMinArgs;
      const matchedSignatures = this.argumentSignatures().reduce((accum, argSig) => {
        if (argSig.kind === 'mandatory') {
          accum.push(argSig);
        }
        if (argSig.kind === 'optional' && numOptional > 0) {
          numOptional -= 1;
          accum.push(argSig);
        }
        return accum;
      }, []);
      return matchedSignatures[indexOfCurrentArg];
    }
    return null;
  }

  argumentSignatures () {
    return this.args.map(toArgumentSignature);
  }

  isCalleeMatched (currentNode) {
    if (!isCallExpression(currentNode)) {
      return false;
    }
    const callee = currentNode.callee;
    switch (this.callee.type) {
      case 'Identifier':
        return hasIdentName(callee, this.callee.name);
      case 'MemberExpression':
        return hasIdentName(callee.object, this.callee.object) && hasIdentName(callee.property, this.callee.member);
      /* istanbul ignore next */
      default:
        return false;
    }
  }
}
const toArgumentSignature = (arg, idx) => {
  return {
    index: idx,
    name: arg.name,
    kind: arg.optional ? 'optional' : 'mandatory'
  };
};

module.exports = SigMatcher;