Code coverage report for lib/hierarchy.js

Statements: 100% (40 / 40)      Branches: 100% (21 / 21)      Functions: 100% (6 / 6)      Lines: 100% (40 / 40)      Ignored: none     

All files » lib/ » hierarchy.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              26 69               69 173   173   34     41       173 6         173         173   173 214       214 171                 214     173                                       26 411   411   411 171   171 342         171 173   173 173 346     173 29     173 7 21   7   7         7   166         411     69    
'use strict';
 
/**
 * @param {Array<Object>} comments an array of parsed comments
 * @returns {Array<Object>} nested comments, with only root comments
 * at the top level.
 */
module.exports = function (comments) {
  var id = 0,
    root = {
      members: {
        instance: {},
        static: {}
      }
    };
 
  comments.forEach(function (comment) {
    var path = [];
 
    if (comment.memberof) {
      // TODO: full namepath parsing
      path = comment.memberof
        .split('.')
        .map(function (segment) {
          return ['static', segment];
        });
    }
 
    if (!comment.name) {
      comment.errors.push({
        message: 'could not determine @name for hierarchy'
      });
    }
 
    path.push([
      comment.scope || 'static',
      comment.name || ('unknown_' + id++)
    ]);
 
    var node = root;
 
    while (path.length) {
      var segment = path.shift(),
        scope = segment[0],
        name = segment[1];
 
      if (!node.members[scope].hasOwnProperty(name)) {
        node.members[scope][name] = {
          comments: [],
          members: {
            instance: {},
            static: {}
          }
        };
      }
 
      node = node.members[scope][name];
    }
 
    node.comments.push(comment);
  });
 
  /*
   * Massage the hierarchy into a format more suitable for downstream consumers:
   *
   * * Individual top-level scopes are collapsed to a single array
   * * Members at intermediate nodes are copied over to the corresponding comments,
   *   with multisignature comments allowed.
   * * Intermediate nodes without corresponding comments indicate an undefined
   *   @memberof reference. Emit an error, and reparent the offending comment to
   *   the root.
   * * Add paths to each comment, making it possible to generate permalinks
   *   that differentiate between instance functions with the same name but
   *   different `@memberof` values.
   *
   *     Person#say  // the instance method named "say."
   *     Person.say  // the static method named "say."
   *     Person~say  // the inner method named "say."
   */
  function toComments(nodes, root, hasUndefinedParent, path) {
    var result = [], scope;
 
    path = path || [];
 
    for (var name in nodes) {
      var node = nodes[name];
 
      for (scope in node.members) {
        node.members[scope] = toComments(node.members[scope], root || result,
          !node.comments.length,
          node.comments.length ? path.concat(node.comments[0]) : []);
      }
 
      for (var i = 0; i < node.comments.length; i++) {
        var comment = node.comments[i];
 
        comment.members = {};
        for (scope in node.members) {
          comment.members[scope] = node.members[scope];
        }
 
        comment.path = path.map(function (n) {
          return n.name;
        }).concat(comment.name);
 
        if (hasUndefinedParent) {
          var memberOfTag = comment.tags.filter(function (tag) {
            return tag.title === 'memberof';
          })[0];
          var memberOfTagLineNumber = (memberOfTag && memberOfTag.lineNumber) || 0;
 
          comment.errors.push({
            message: '@memberof reference to ' + comment.memberof + ' not found',
            commentLineNumber: memberOfTagLineNumber
          });
 
          root.push(comment);
        } else {
          result.push(comment);
        }
      }
    }
 
    return result;
  }
 
  return toComments(root.members.static);
};