Code coverage report for lib/infer/params.js

Statements: 100% (69 / 69)      Branches: 97.92% (47 / 48)      Functions: 100% (13 / 13)      Lines: 100% (69 / 69)      Ignored: none     

All files » lib/infer/ » params.js
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 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196    25       25   134   25 134 126   134                         25 8   8       8 2 2     6           6 1           6     25 17 9 8     6 2 2       25 4                   25 2                   25 7               7 3   7       134 8     126 4     122 2     120 7     113             113 10     113                   25 64 139   139 22         117 92 92     117 117   117   109     126   48 19     48 78       2 1     126           117 72 68       117      
'use strict';
 
var shouldSkipInference = require('./should_skip_inference'),
  finders = require('./finders'),
  flowDoctrine = require('../flow_doctrine');
 
function paramToDoc(param, comment, i, prefix) {
 
  prefix = prefix || '';
 
  function addPrefix(doc) {
    if (!Array.isArray(doc)) {
      doc.name = prefix + doc.name;
    }
    return doc;
  }
 
  /**
   * Given a parameter like
   *
   *     function a(b = 1)
   *
   * Format it as an optional parameter in JSDoc land
   *
   * @param {Object} param ESTree node
   * @returns {Object} JSDoc param
   */
  function paramWithDefaultToDoc(param) {
    var newParam = paramToDoc(param.left, comment, i);
 
    var defaultValue = comment.context.code.substring(
        param.right.start, param.right.end);
 
    // this is a destructuring parameter with defaults
    if (Array.isArray(newParam)) {
      newParam[0].default = defaultValue;
      return newParam;
    }
 
    var optionalParam = {
      title: 'param',
      name: newParam.name,
      'default': defaultValue
    };
 
    if (newParam.type) {
      optionalParam.type = {
        type: 'OptionalType',
        expression: newParam.type
      };
    }
 
    return optionalParam;
  }
 
  function destructuringPropertyToDoc(property) {
    if (property.type === 'Property') {
      return paramToDoc(property.value, comment, i, prefix + '$' + i + '.');
    } else if (property.type === 'Identifier') {
      // if the destructuring type is an array, the elements
      // in it are identifiers
      return paramToDoc(property, comment, i, prefix + '$' + i + '.');
    } else Eif (property.type === 'SpreadProperty') {
      return paramToDoc(property, comment, i, prefix + '$' + i + '.');
    }
  }
 
  function destructuringObjectParamToDoc(param) {
    return [{
      title: 'param',
      name: '$' + i,
      type: flowDoctrine(param) || {
        type: 'NameExpression',
        name: 'Object'
      }
    }].concat(param.properties.map(destructuringPropertyToDoc));
  }
 
  function destructuringArrayParamToDoc(param) {
    return [{
      title: 'param',
      name: '$' + i,
      type: flowDoctrine(param) || {
        type: 'NameExpression',
        name: 'Array'
      }
    }].concat(param.elements.map(destructuringPropertyToDoc));
  }
 
  function restParamToDoc(param) {
    var newParam = {
      title: 'param',
      name: param.argument.name,
      lineNumber: param.loc.start.line,
      type: {
        type: 'RestType'
      }
    };
    if (param.typeAnnotation) {
      newParam.type.expression = flowDoctrine(param.typeAnnotation.typeAnnotation);
    }
    return newParam;
  }
 
  // ES6 default
  if (param.type === 'AssignmentPattern') {
    return addPrefix(paramWithDefaultToDoc(param));
  }
 
  if (param.type === 'ObjectPattern') {
    return addPrefix(destructuringObjectParamToDoc(param));
  }
 
  if (param.type === 'ArrayPattern') {
    return addPrefix(destructuringArrayParamToDoc(param));
  }
 
  if (param.type === 'SpreadProperty' || param.type === 'RestElement') {
    return addPrefix(restParamToDoc(param));
  }
 
  var newParam = {
    title: 'param',
    name: param.name,
    lineNumber: param.loc.start.line
  };
 
  // Flow/TS annotations
  if (param.typeAnnotation && param.typeAnnotation.typeAnnotation) {
    newParam.type = flowDoctrine(param.typeAnnotation.typeAnnotation);
  }
 
  return addPrefix(newParam);
}
 
/**
 * Infers param tags by reading function parameter names
 *
 * @name inferParams
 * @param {Object} comment parsed comment
 * @returns {Object} comment with parameters
 */
module.exports = function () {
  return shouldSkipInference(function inferParams(comment) {
    var node = finders.findType(comment.context.ast.value, 'Function');
 
    if (!node) {
      return comment;
    }
 
    // Ensure that explicitly specified parameters are not overridden
    // by inferred parameters
    var existingParams = (comment.params || []).reduce(function (memo, param) {
      memo[param.name] = param;
      return memo;
    }, {});
 
    var paramOrder = {};
    var i = 0;
 
    node.params
      .reduce(function (params, param, i) {
        return params.concat(paramToDoc(param, comment, i));
      }, [])
      .forEach(function (doc) {
        if (!existingParams.hasOwnProperty(doc.name)) {
          // This type is not explicitly documented
          if (!comment.params) {
            comment.params = [];
          }
 
          comment.params = comment.params.concat(doc);
        } else if (!existingParams[doc.name].type) {
          // This param has a description, but potentially it can
          // be have an inferred type. Infer its type without
          // dropping the description.
          if (doc.type) {
            existingParams[doc.name].type = doc.type;
          }
        }
        paramOrder[doc.name] = i++;
      });
 
    // Ensure that if params are specified partially or in
    // the wrong order, they'll be output in the order
    // they actually appear in code
    if (comment.params) {
      comment.params.sort(function (a, b) {
        return paramOrder[a.name] - paramOrder[b.name];
      });
    }
 
    return comment;
  });
};