[
  {
    "path": ".gitignore",
    "content": "/node_modules\n.tern-*\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (C) 2013 by Marijn Haverbeke <marijn@haverbeke.berlin> and others\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Getdocs\n\nGetdocs is like JSDoc or documentation.js, running over ES6 code to\nextract information and inline documentation in order to generate\ndocs, but without all the @s. It takes source files and outputs JSON.\n\nFor example, if you have this file, `foo.js`:\n\n```javascript\n// :: (number, number) → number\n// Add two numbers\nexport function plus(a, b = 2) {\n  return a + b\n}\n```\n\nYou can say `getdocs foo.js` to get this JSON:\n\n```json\n{\n  \"plus\": {\n    \"type\": \"Function\",\n    \"params\": [\n      {\n        \"type\": \"number\",\n        \"name\": \"a\"\n      },\n      {\n        \"type\": \"number\",\n        \"default\": \"2\",\n        \"optional\": true,\n        \"name\": \"b\"\n      }\n    ],\n    \"returns\": { \"type\": \"number\" },\n    \"description\": \"Add two numbers\",\n    \"exported\": true\n  }\n}\n```\n\nThe idea is to then feed this into a system (can be a simple set of\ntemplates) that massages it into actual human-readable documention\nfiles.\n\nA getdocs doc comment starts with a double colon, optionally prefixed\nwith a name (`foo::`) and followed by a type. It can be either a block\ncomment or a continuous sequence of line comments. When you don't want\nto specify a type, for example because the type can be inferred from\nthe code (as with a class declaration), you can write a single dash\nafter the colons, instead of a type.\n\nWhen no name is given, such a doc comment applies to the next program\nelement after it. That element should be something with a name, like a\nvariable, function, or class declaration, or an assignment that can be\nstatically resolved.\n\nThe documented items found in the files passed to getdocs will be\nreturned as part of a big JSON object. Nesting is only applied for\nclass and object properties, where the properties are moved under the\n`properties` object of the item they are part of. _A single namespace\nis assumed for the documented identifiers in the group of files._\n\nInside a doc comment, properties of the thing being defined can be\nadded by writing nested, indented doc comments. For example:\n\n```\n// Plugin:: interface\n//\n// Objects conforming to the plugin interface can be plugged into a\n// Foo\n//\n//   mount:: (Foo) → bool\n//   Mount the plugin in this Foo. The return value indicates whether\n//   the mount succeeded.\n//\n//   unmount:: (Foo)\n//   Unmount the plugin from a Foo.\n```\n\nFurther nesting below such a property (by adding more indentation) is\nsupported.\n\n## Type syntax\n\nA type can be:\n\n * A JavaScript identifier, optionally followed by any number of\n   properties, which are a dot character followed by a JavaScript\n   identifier. A type name can be followed by a list of type\n   parameters, between angle brackets, as in `Object<string>` (an\n   object whose properties hold string values).\n\n * An array type, which is a type wrapped in `[` and `]`. `[x]` is\n   equivalent to `Array<x>`.\n\n * A function type, which is written as a parenthesized list of\n   argument types. Each argument type may optionally be prefixed with\n   an argument name, which is an identifier followed by a colon. When\n   an argument is prefixed by the string `...`, it is marked as a\n   `rest` argument. After the closing parenthesis, an optional return\n   type may appear after an arrow, written either `→` or `->`.\n\n * A nullable type, written as a question mark followed by a type.\n\n * An unspecified or “any” type, written as an asterisk `*`.\n\n * An object type, written as a list of properties wrapped in `{` and\n   `}` braces. Each property must start with an identifier, followed\n   by a colon, followed by a type.\n\n * A string literal, enclosed by double quotes, or a number literal.\n\n * A type followed by `extends` followed by another type, to indicate\n   a sub-type.\n\nHere are some examples of types:\n\n * `Math.pow`: `(base: number, exponent: number) → number`\n\n * `Element.insertBefore`: `(newNode: Node, before: ?Node) → Node`\n\n * `console.log`: `(...data: *)`\n\n * A pair of coordinates: `{x: number, y: number}`\n\n * An array of strings: `[string]`\n\n * An array of numbers or a string: `union<[number], string>` (what\n   the name `union` means isn't something getdocs is aware of, but\n   you could use it for union types, and maybe render it as `[number]\n   | string` in your output).\n\n## Tags\n\nIt is possible to add tags to a documented item. These are words\nprefixed with a `#` character, appearing at the start of the comment —\nthat is, immediately after the type.\n\nA tag like `#deprecated`, for example, will result in a `$deprecated:\n\"true\"` property on the given item. The property is named by\nprepending the tag's name with a dollar sign.\n\nYou can give tags an explicit value other than `\"true\"` by writing an\n`=` character followed either by a word (a sequence of characters\nwithout whitespace) or a quoted JavaScript-style string. For example\n`#chapter=selection` or `#added=\"2.1.0\"`.\n\nThe `#static` tag can be used to indicate that a given class member is\nstatic (which is only necessary for doc comments that aren't tied to a\nsyntactic element in the code).\n\n## Output JSON\n\nThe returned object maps item names to item descriptions. The\nfollowing properties can appear in a description for a documented\nitem:\n\n * **description**: The doc comment for the item.\n\n * **loc**: A `{line, column, file}` object pointing at the start of the item.\n\n * **exported**: Set if the item is exported using ES6 module syntax.\n\n * **constructor**: For classes with a documented constructor, this\n   points at the constructor function.\n\n * **extends**: Holds the type of the supertype of a class or other\n     sub-type.\n\n * **staticProperties**: For classes, this holds properties and\n   methods that appear directly on the constructor.\n\nIn addition, they may have these properties, which can also appear on\nnested types:\n\n * **type**: The name of the type. Instances of classes should use the\n   (capitalized) class name. Builtin types will have names like\n   `Array` or `Function`. Getdocs does not prescribe a naming of\n   primitive types, but for consistency I recommend you use `number`,\n   `string`, and `bool`.\n\n * **properties**: An object mapping property names to types.\n\n * **params**: For function types, this holds an array of parameter\n   types. Parameter types can have these additional properties:\n\n     * **name**: The name of the parameter.\n\n     * **rest**: Set when this is a rest parameter.\n\n     * **default**: The default value of the parameter (as a raw\n       source string).\n\n * **returns**: For function types, this holds the type that is\n   returned.\n\n * **typeParams**: For array types or named types with parameters\n   (angle bracket syntax), this holds an array of parameter types.\n\n * **optional**: Set for nullable types.\n\n * **id**: The path to this type. For a top-level variable `foo`\n   this'll be `\"foo\"`, for the type of the property `bar` under `foo`,\n   it'll be `\"foo.bar\"`, and so on.\n\n## Interface\n\nThe module exports the following function:\n\n**`gather`**`: (code: string, options: Object) → Object`\n\nIt takes a code file, extracts the docs, and returns an object\ndescribing the documented items.\n\nOptions can have the following properties:\n\n * **`filename`**`: string` The filename of the given code. Required.\n\n * **`items`**`: ?Object` An existing items object to add the items\n   found in the given code to.\n\n * **`onComment`**`: ?(block: bool, text: string, start: number, end:\n   number, startPos: Object, endPos: Object)` Will be called for each\n   comment in the code, if given.\n\n**`parseType`**`: (input: string, start: number, loc: {file: string, line: number}) → {type: Object, end: number}`\n\nParse a type in getdocs syntax into its object representation. `start`\nindicates where in the string the parsing should start. The returned\nobject tells you where the type ended.\n\nWill throw a `SyntaxError` when the type isn't valid.\n\n**`stripComment`**`: (comment: string) → string`\n\nStrips leading indentation and asterisks (as in the common block\ncomment style where each line gets an asterisk) from a string.\n"
  },
  {
    "path": "bin/getdocs.js",
    "content": "#!/usr/bin/env node\nvar fs = require(\"fs\")\nvar glob = require(\"glob\")\nvar getdocs = require(\"../src\")\n\nvar items = {}\n\nprocess.argv.slice(2).forEach(function(arg) {\n  glob.sync(arg).forEach(function(filename) {\n    getdocs.gather(fs.readFileSync(filename, \"utf8\"), {filename: filename, items: items})\n  })\n})\n\nconsole.log(JSON.stringify(items, null, 2))\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"getdocs\",\n  \"version\": \"0.6.1\",\n  \"description\": \"An extractor for succinct documentation comments in JavaScript code\",\n  \"main\": \"src/index.js\",\n  \"bin\": {\n    \"getdocs\": \"bin/getdocs.js\"\n  },\n  \"scripts\": {\n    \"test\": \"node test/run.js\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/marijnh/getdocs.git\"\n  },\n  \"keywords\": [\n    \"documentation\",\n    \"comment\"\n  ],\n  \"maintainers\": [\n    {\n      \"name\": \"Marijn Haverbeke\",\n      \"email\": \"marijn@haverbeke.berlin\",\n      \"web\": \"http://marijnhaverbeke.nl\"\n    }\n  ],\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/marijnh/getdocs/issues\"\n  },\n  \"dependencies\": {\n    \"acorn\": \"^2.6.0\",\n    \"glob\": \"^6.0.1\"\n  }\n}\n"
  },
  {
    "path": "src/doccomments.js",
    "content": "var acorn = require(\"acorn/dist/acorn\")\nvar walk = require(\"acorn/dist/walk\")\n\nvar parseType = require(\"./parsetype\")\n\nfunction strip(lines) {\n  for (var head, i = 1; i < lines.length; i++) {\n    var line = lines[i], lineHead = line.match(/^[\\s\\*]*/)[0]\n    if (lineHead != line) {\n      if (head == null) {\n        head = lineHead\n      } else {\n        var same = 0\n        while (same < head.length && head.charCodeAt(same) == lineHead.charCodeAt(same)) ++same\n        if (same < head.length) head = head.slice(0, same)\n      }\n    }\n  }\n  if (head != null) {\n    var startIndent = /^\\s*/.exec(lines[0])[0]\n    var trailing = /\\s*$/.exec(head)[0]\n    var extra = trailing.length - startIndent.length\n    if (extra > 0) head = head.slice(0, head.length - extra)\n  }\n\n  outer: for (var i = 0; i < lines.length; i++) {\n    var line = lines[i].replace(/\\s+$/, \"\")\n    if (i == 0 && head != null) {\n      for (var j = 0; j < head.length; j++) {\n        var found = line.indexOf(head.slice(j))\n        if (found == 0) {\n          lines[i] = line.slice(head.length - j)\n          continue outer\n        }\n      }\n    }\n    if (head == null || i == 0)\n      lines[i] = line.replace(/^[\\s\\*]*/, \"\")\n    else if (line.length < head.length)\n      lines[i] = \"\"\n    else\n      lines[i] = line.slice(head.length)\n  }\n\n  while (lines.length && !lines[lines.length - 1]) lines.pop()\n  while (lines.length && !lines[0]) lines.shift()\n  return lines.join(\"\\n\")\n}\n\nexports.stripComment = function(text) { return strip(text.split(\"\\n\")) }\n\nexports.parse = function(text, options) {\n  var current = null, found = [], filename = options.filename\n\n  var ast = acorn.parse(text, {\n    ecmaVersion: 6,\n    locations: true,\n    sourceFile: {text: text, name: filename},\n    sourceType: \"module\",\n    onComment: function(block, text, start, end, startLoc, endLoc) {\n      if (current && !block && current.endLoc.line == startLoc.line - 1) {\n        current.text.push(text)\n        current.end = end\n        current.endLoc = endLoc\n      } else if (/^\\s*[\\w\\.$]*::/.test(text)) {\n        var obj = {text: text.split(\"\\n\"), start: start, end: end, startLoc: startLoc, endLoc: endLoc}\n        found.push(obj)\n        if (!block) current = obj\n      } else {\n        current = null\n      }\n      if (options.onComment) options.onComment(block, text, start, end, startLoc, endLoc)\n    }\n  })\n\n  for (var i = 0; i < found.length; i++) {\n    var comment = found[i], loc = comment.startLoc\n    loc.file = filename\n    comment.parsed = parseNestedComments(strip(comment.text), comment.startLoc)\n  }\n  return {ast: ast, comments: found}\n}\n\nfunction Found() {}\n\nexports.findNodeAfter = function(ast, pos, types) {\n  var stack = []\n  function c(node, _, override) {\n    if (node.end < pos) return\n    if (node.start >= pos && types[node.type]) {\n      stack.push(node)\n      throw new Found\n    }\n    if (!override) stack.push(node)\n    walk.base[override || node.type](node, null, c)\n    if (!override) stack.pop()\n  }\n  try {\n    c(ast)\n  } catch (e) {\n    if (e instanceof Found) return stack\n    throw e\n  }\n}\n\nexports.findNodeAround = function(ast, pos, types) {\n  var stack = [], found\n  function c(node, _, override) {\n    if (node.end <= pos || node.start >= pos) return\n    if (!override) stack.push(node)\n    walk.base[override || node.type](node, null, c)\n    if (types[node.type] && !found) found = stack.slice()\n    if (!override) stack.pop()\n  }\n  c(ast)\n  return found || stack\n}\n\nfunction parseComment(text, loc) {\n  var match = /^\\s*([\\w\\.$]+)?::\\s*(-\\s*)?/.exec(text), data, end = match[0].length, name = match[1]\n  if (match[2]) {\n    data = Object.create(null)\n    data.loc = loc\n  } else {\n    var parsed = parseType(text, match[0].length, loc)\n    data = parsed.type\n    end = parsed.end\n  }\n\n  text = text.slice(end)\n  while (match = /^\\s*#([\\w$]+)(?:=([^\"]\\S*|\"(?:[^\"\\\\]|\\\\.)*\"))?\\s*/.exec(text)) {\n    text = text.slice(match[0].length)\n    var value = match[2] || \"true\"\n    if (value.charAt(0) == '\"') value = JSON.parse(value)\n    data[\"$\" + match[1]] = value\n  }\n\n  if (/\\S/.test(text)) data.description = text\n  return {data: data, name: name, subcomments: []}\n}\n\nfunction dropIndent(text) {\n  var lines = text.split(\"\\n\"), indent = 1e7\n  for (var i = 0; i < lines.length; i++)\n    if (/\\S/.test(lines[i]))\n      indent = Math.min(indent, /^\\s*/.exec(lines[i])[0].length)\n  if (indent == 0) return text\n  for (var i = 0; i < lines.length; i++) lines[i] = lines[i].slice(indent)\n  return lines.join(\"\\n\")\n}\n\nfunction parseNestedComments(text, loc) {\n  var line = 0, context = [], top, nextIndent = /^\\s*/.exec(text)[0].length\n  for (;;) {\n    var next = /\\n( *)[\\w\\.$]*::/.exec(text)\n    var current = next ? text.slice(0, next.index) : text\n    var parsed = parseComment(dropIndent(current), line ? {line: loc.line + line, column: loc.column, file: loc.file} : loc)\n    if (!top) {\n      top = parsed\n    } else {\n      if (!parsed.name)\n        throw new SyntaxError(\"Sub-comment without name at \" + loc.file + \":\" + (loc.line + line))\n      while (context[context.length - 1].indent >= nextIndent) {\n        context.pop()\n        if (!context.length)\n          throw new SyntaxError(\"Invalid indentation for sub-field at \" + loc.file + \":\" + (loc.line + line))\n      }\n      context[context.length - 1].comment.subcomments.push(parsed)\n    }\n    context.push({indent: nextIndent, comment: parsed})\n\n    if (!next) break\n    line += current.split(\"\\n\").length + 1\n    text = text.slice(current.length + 1)\n    nextIndent = next[1].length\n  }\n  return top\n}\n"
  },
  {
    "path": "src/index.js",
    "content": "var docComments = require(\"./doccomments\")\nexports.stripComment = docComments.stripComment\nvar parseType = exports.parseType = require(\"./parsetype\")\n\nexports.gather = function(text, options) {\n  var items = options.items || Object.create(null)\n  var top = {properties: items}\n\n  var found = docComments.parse(text, options)\n\n  found.comments.forEach(function(comment) {\n    var data = comment.parsed.data\n\n    if (comment.parsed.name) {\n      var stack = docComments.findNodeAround(found.ast, comment.end, findPath)\n      path = addNameToPath(comment.parsed.name, getPath(stack), data.$static)\n    } else {\n      var stack = docComments.findNodeAfter(found.ast, comment.end, findPath)\n      var node = stack && stack[stack.length - 1]\n      if (!node || !/^(?:[;{},\\s]|\\/\\/.*|\\/\\*.*?\\*\\/)*$/.test(text.slice(node.end, comment.start)))\n        throw new SyntaxError(\"Misplaced documentation block at \" + options.filename + \":\" + comment.startLoc.line)\n      if (inferForNode.hasOwnProperty(node.type)) data = inferForNode[node.type](node, data, stack)\n      var path = getPath(stack)\n    }\n\n    var stored = addData(top, path, data)\n\n    comment.parsed.subcomments.forEach(function(sub) { applySubComment(stored, sub) })\n  })\n\n  // Mark locals exported with `export {a, b, c}` statements as exported\n  for (var i = 0; i < found.ast.body.length; i++) {\n    var node = found.ast.body[i]\n    if (node.type == \"ExportNamedDeclaration\" && !node.source) {\n      for (var j = 0; j < node.specifiers.length; j++) {\n        var spec = node.specifiers[j]\n        var known = items[spec.local.name]\n        if (known) known.exported = true\n      }\n    }\n  }\n\n  assignIds(top)\n\n  return items\n}\n\nfunction applySubComment(parent, sub) {\n  var target\n  if (parent.type == \"Function\") {\n    if (sub.name == \"return\")\n      target = parent.returns\n    else if (parent.params) for (var i = 0; i < parent.params.length; i++)\n      if (parent.params[i].name == sub.name) target = parent.params[i]\n    if (!target) raise(\"Unknown parameter \" + sub.name, sub.data.loc)\n  } else if (parent.type == \"class\" || parent.type == \"interface\" || parent.type == \"Object\") {\n    var path = splitPath(sub.name), target = parent\n    for (var i = 0; i < path.length; i++) {\n      var isStatic = i == path.length - 1 && sub.data.$static\n      target = deref(deref(target, isStatic ? \"staticProperties\" : \"properties\"), path[i])\n    }\n  } else {\n    raise(\"Can not add sub-fields to named type \" + parent.type, sub.data.loc)\n  }\n  var stored = extend(sub.data, target, [sub.name], true)\n  sub.subcomments.forEach(function(sub) { applySubComment(stored, sub) })\n}\n\nfunction getPath(ancestors) {\n  var top = ancestors[ancestors.length - 1]\n  return top ? findPath[top.type](top, ancestors) : []\n}\n\nvar findPath = {\n  // FIXME destructuring\n  VariableDeclaration: function(node) { return [node.declarations[0].id.name] },\n\n  VariableDeclarator: function(node) { return [node.id.name] },\n\n  FunctionDeclaration: function(node) { return [node.id.name] },\n\n  ClassDeclaration: function(node) { return [node.id.name] },\n\n  AssignmentExpression: function(node, ancestors) {\n    return lvalPath(node.left, ancestors)\n  },\n\n  Property: function(node, ancestors) {\n    var path = parentPath(ancestors)\n    path.push(propName(node, true))\n    return path\n  },\n\n  MethodDefinition: function(node, ancestors) {\n    var path = parentPath(ancestors)\n    if (node.kind == \"constructor\") {\n      path.push(\"#constructor\")\n    } else {\n      if (!node.static) path.push(\"prototype\")\n      path.push(propName(node, true))\n    }\n    return path\n  },\n\n  ExportNamedDeclaration: function(node, ancestors) {\n    return this[node.declaration.type](node.declaration, ancestors)\n  },\n\n  ExportDefaultDeclaration: function() {\n    return [\"default\"]\n  }\n}\n\nfunction addNameToPath(name, path, isStatic) {\n  var parts = splitPath(name)\n  for (var i = 0; i < parts.length; i++) {\n    if (path.length && ctorName(path[path.length - 1]) && (!isStatic || i < parts.length - 1))\n      path.push(\"prototype\")\n    path.push(parts[i])\n  }\n  return path\n}\n\nfunction addData(top, path, data) {\n  var target = top, isCtor = false\n  for (var i = 0; i < path.length; i++) {\n    var cur = path[i], descend = \"properties\"\n    if (cur == \"#constructor\") {\n      target = deref(target, \"constructor\")\n      break\n    }\n    if (isCtor) {\n      if (cur == \"prototype\") {\n        if (i == path.length - 1) raise(\"Can not annotate constructor prototype\", data.loc)\n        cur = path[++i]\n      } else {\n        descend = \"staticProperties\"\n      }\n    }\n    target = deref(deref(target, descend), cur)\n    isCtor = target.type ? target.type == \"class\" : ctorName(cur)\n  }\n  return extend(data, target, path)\n}\n\nvar inferForNode = {\n  VariableDeclaration: function(node, data) {\n    var decl0 = node.declarations[0]\n    return inferExpr(decl0.init, data, decl0.id.name)\n  },\n\n  VariableDeclarator: function(node, data) {\n    return inferExpr(node.init, data, node.id.name)\n  },\n\n  FunctionDeclaration: function(node, data) {\n    return inferFn(node, data, node.id.name)\n  },\n\n  ClassDeclaration: inferClass,\n  ClassExpression: inferClass,\n\n  AssignmentExpression: function(node, data) {\n    return inferExpr(node.right, data, propName(node.left))\n  },\n\n  Property: function(node, data) {\n    return inferExpr(node.value, data, propName(node, true))\n  },\n\n  MethodDefinition: function(node, data) {\n    return inferFn(node.value, data)\n  },\n\n  ExportNamedDeclaration: function(node, data, ancestors) {\n    var inner = this[node.declaration.type](node.declaration, data, ancestors)\n    inner.exported = true\n    return inner\n  },\n\n  ExportDefaultDeclaration: function(node, data, ancestors) {\n    var decl = node.declaration\n    if (this[decl.type])\n      data = this[decl.type](decl, data, ancestors)\n    else\n      data = inferExpr(decl, data)\n    data.exported = true\n    return data\n  }\n}\n\nfunction raise(msg, loc) {\n  throw new SyntaxError(msg + \" at \" + (loc.file || loc.source.name) + \":\" + (loc.start ? loc.start.line : loc.line))\n}\n\nfunction propName(node, force) {\n  var key = node.key || node.property\n  if (!node.computed && key.type == \"Identifier\") return key.name\n  if (key.type == \"Literal\") {\n    if (typeof key.value == \"string\") return key.value\n    if (typeof key.value == \"number\") return String(key.value)\n  }\n  if (node.computed && key.type == \"MemberExpression\" &&\n      !key.computed && key.object.name == \"Symbol\")\n    return \"[Symbol.\" + key.property.name + \"]\"\n  if (force) raise(\"Expected static property\", node.loc)\n}\n\nfunction inferParam(n) {\n  var param = Object.create(null)\n  param.type = \"any\"\n  param.loc = n.loc.start\n  param.loc.file = n.loc.source.name\n  if (n.type == \"RestElement\") {\n    param.rest = true\n    n = n.argument\n  }\n  if (n.type == \"AssignmentPattern\") {\n    if (n.right.end - n.right.start < 20)\n      param.default = n.loc.source.text.slice(n.right.start, n.right.end)\n    n = n.left\n    param.optional = true\n  }\n  if (n.type == \"Identifier\") param.name = n.name\n  return param\n}\n\nfunction ctorName(name) {\n  return name && /^[A-Z]/.test(name)\n}\n\nfunction inferFn(node, data, name) {\n  var inferredParams = node.params.map(inferParam)\n\n  if (!data.type) {\n    data.type = \"Function\"\n    data.params = inferredParams\n  } else if (data.type == \"Function\") {\n    for (var i = 0, e = Math.min(data.params.length, node.params.length); i < e; i++) {\n      var from = inferredParams[i], to = data.params[i]\n      for (var prop in from) if (!(prop in to)) to[prop] = from[prop]\n    }\n  }\n  if (node.generator) data.generator = true\n\n  if (ctorName(name)) {\n    return {constructor: data, type: \"class\", loc: data.loc}\n  } else {\n    return data\n  }\n}\n\nfunction inferClass(node, data) {\n  if (node.superClass && node.superClass.type == \"Identifier\") {\n    var loc = node.superClass.loc\n    loc.start.file = loc.source.name\n    data.extends = parseType(node.superClass.name, 0, loc.start).type\n  }\n  if (!data.type) data.type = \"class\"\n  return data\n}\n\nfunction inferExpr(node, data, name) {\n  if (!node) return data\n  if (node.type == \"ClassExpression\") {\n    inferClass(node, data)\n  } else if (node.type == \"FunctionExpression\" || node.type == \"ArrowFunctionExpression\") {\n    inferFn(node, data, name)\n  } else if (node.type == \"Literal\" && !data.type) {\n    if (typeof node.value == \"number\") data.type = \"number\"\n    else if (typeof node.value == \"boolean\") data.type = \"bool\"\n    else if (typeof node.value == \"string\") data.type = \"string\"\n    else if (node.value instanceof RegExp) data.type = \"RegExp\"\n  } else if (node.type == \"NewExpression\" && !data.type) {\n    if (node.callee.type == \"Identifier\" && ctorName(node.callee.name))\n      data.type = node.callee.name\n  }\n  return data\n}\n\n// Deriving context from ancestor nodes\n\nfunction extend(from, to, path, overrideLoc) {\n  for (var prop in from) {\n    if (!(prop in to) || (prop == \"loc\" && overrideLoc)) {\n      to[prop] = from[prop]\n    } else if (prop == \"properties\" || prop == \"staticProperties\") {\n      extend(from[prop], to[prop], path.concat(prop))\n    } else {\n      var msg = \"Conflicting information for \" + path.join(\".\") + \".\" + prop\n      if (to.loc) msg += \" at \" + to.loc.file + \":\" + to.loc.line\n      if (from.loc) msg += (to.loc ? \" and \" : \" at \") + from.loc.file + \":\" + from.loc.line\n      throw new SyntaxError(msg)\n    }\n  }\n  return to\n}\n\nfunction deref(obj, name) {\n  return obj[name] || (obj[name] = Object.create(null))\n}\n\nfunction assignIds(obj, path) {\n  if (path) obj.id = path\n  if (Object.prototype.hasOwnProperty.call(obj, \"constructor\"))\n    assignIds(obj.constructor, path + \".constructor\")\n  if (obj.properties) for (var prop in obj.properties)\n    assignIds(obj.properties[prop], (path ? path + \".\" : \"\") + prop)\n  if (obj.staticProperties) for (var prop in obj.staticProperties)\n    assignIds(obj.staticProperties[prop], path + \"^\" + prop)\n  if (obj.params) for (var i = 0; i < obj.params.length; i++)\n    if (obj.params[i].name) assignIds(obj.params[i], path + \"^\" + obj.params[i].name)\n  if (obj.returns) assignIds(obj.returns, path + \"^returns\")\n}\n\nfunction lvalPath(lval, ancestors) {\n  var path = []\n  while (lval.type == \"MemberExpression\") {\n    path.unshift(propName(lval))\n    lval = lval.object\n  }\n\n  if (lval.type == \"Identifier\") {\n    path.unshift(lval.name)\n  } else if (lval.type == \"ThisExpression\") {\n    path = selfPath(ancestors.slice(0, ancestors.length - 1)).concat(path)\n  } else {\n    raise(\"Could not derive a target for this assignment\", lval.loc)\n  }\n  return path\n}\n\nfunction assignedPath(ancestors) {\n  var top = ancestors[ancestors.length - 1]\n  if (top.type == \"VariableDeclarator\" && top.id.type == \"Identifier\")\n    return [top.id.name]\n  else if (top.type == \"AssignmentExpression\")\n    return lvalPath(top.left, ancestors)\n  else\n    raise(\"Could not derive a name\", top.loc)\n}\n\nfunction findPrototype(ancestors) {\n  var assign = ancestors[ancestors.length - 1]\n  if (assign.type != \"AssignmentExpression\") return null\n  var lval = assign.left\n  if (lval.type == \"MemberExpression\" && !lval.computed &&\n      lval.object.type == \"MemberExpression\" && !lval.object.computed &&\n      lval.object.property.name == \"prototype\")\n    return lvalPath(lval.object, ancestors)\n}\n\nfunction assignedName(node) {\n  if (node.type == \"VariableDeclarator\" && node.id.type == \"Identifier\")\n    return node.id.name\n  else if (node.type == \"AssignmentExpression\")\n    return propName(node.left)\n}\n\nfunction selfPath(ancestors) {\n  for (var i = ancestors.length - 1; i >= 0; i--) {\n    var ancestor = ancestors[i], found\n    if (ancestor.type == \"ClassDeclaration\" ||\n        (ancestor.type == \"FunctionDeclaration\" && ctorName(ancestor.id.name)))\n      return [ancestor.id.name, \"prototype\"]\n    else if (i && (ancestor.type == \"ClassExpression\" ||\n                   ancestor.type == \"FunctionExpression\" && ctorName(assignedName(ancestors[i - 1]))))\n      return assignedPath(ancestors.slice(0, i)).concat(\"prototype\")\n    else if (i && /Function/.test(ancestor.type) && (found = findPrototype(ancestors.slice(0, i))))\n      return found\n  }\n  raise(\"No context found for 'this'\", ancestors[ancestors.length - 1].loc)\n}\n\nfunction parentPath(ancestors) {\n  for (var i = ancestors.length - 1; i >= 0; i--) {\n    var ancestor = ancestors[i]\n    if (ancestor.type == \"ClassDeclaration\")\n      return [ancestor.id.name]\n    else if (i && (ancestor.type == \"ClassExpression\" || ancestor.type == \"ObjectExpression\"))\n      return assignedPath(ancestors.slice(0, i))\n  }\n}\n\nfunction splitPath(path) {\n  var m, parts = [], rest = path\n  while (rest && (m = /^(\\[.*?\\]|[^\\s\\.#]+)(\\.)?/.exec(rest))) {\n    parts.push(m[1])\n    rest = rest.slice(m[0].length)\n    if (!m[2]) break\n  }\n  if (rest) throw new Error(\"Invalid path: \" + path)\n  return parts\n}\n"
  },
  {
    "path": "src/parsetype.js",
    "content": "module.exports = function(string, start, loc) {\n  var input = new Input(string, start, loc)\n  var result = parse(input)\n  input.skip()\n  return {type: result, end: input.pos}\n}\n\nfunction isSpace(ch) {\n  return (ch < 14 && ch > 8) || ch === 32 || ch === 160;\n}\n\nfunction Input(string, start, loc) {\n  this.str = string\n  this.pos = start\n  this.loc = loc\n  this.skip()\n}\n\nInput.prototype = {\n  skip: function() {\n    while (this.pos < this.str.length && isSpace(this.str.charCodeAt(this.pos))) ++this.pos\n  },\n\n  atEnd: function() {\n    return this.pos == this.str.length\n  },\n\n  eat: function(ch) {\n    if (this.str.charCodeAt(this.pos) == ch.charCodeAt(0)) {\n      this.pos++\n      this.skip()\n      return true\n    }\n  },\n\n  match: function(re) {\n    var match = re.exec(this.str.slice(this.pos))\n    if (match) {\n      this.pos += match[0].length\n      this.skip()\n      return match\n    }\n  },\n\n  error: function(message) {\n    throw new SyntaxError(message + \" for \" + this.loc.file + \":\" + this.loc.line)\n  }\n}\n\nfunction parse(input) {\n  var base = parseBase(input)\n  if (input.match(/^extends\\b/)) base.extends = parse(input)\n  return base\n}\n\nfunction parseBase(input) {\n  if (input.eat(\"?\")) {\n    var inner = parse(input)\n    inner.optional = true\n    return inner\n  }\n  var type = Object.create(null)\n  type.loc = input.loc\n  if (input.eat(\"*\")) {\n    type.type = \"any\"\n  } else if (input.eat(\"(\")) {\n    type.type = \"Function\"\n    type.params = []\n    while (!input.eat(\")\")) {\n      if (type.params.length && !input.eat(\",\")) input.error(\"Missing comma or closing paren\")\n      var rest = input.match(/^\\.\\.\\./)\n      var name = input.match(/^([\\w$]+)(\\??)\\s*:/)\n      var param = parse(input)\n      if (rest) param.rest = true\n      if (name) param.name = name[1]\n      if (name && name[2]) param.optional = true\n      type.params.push(param)\n    }\n    if (input.match(/^(?:→|->)/))\n      type.returns = parse(input)\n  } else if (input.eat(\"[\")) {\n    type.type = \"Array\"\n    type.typeParams = [parse(input)]\n    while (!input.eat(\"]\")) {\n      if (!input.eat(\",\")) input.error(\"Missing comma or closing square bracket\")\n      type.typeParams.push(parse(input))\n    }\n  } else if (input.eat(\"{\")) {\n    type.type = \"Object\"\n    type.properties = Object.create(null)\n    for (var first = true; !input.eat(\"}\"); first = false) {\n      if (!first && !input.eat(\",\")) input.error(\"Missing comma or closing brace\")\n      var name = input.match(/^([\\w$]+)\\s*:/)\n      if (!name) input.error(\"Malformed object type\")\n      type.properties[name[1]] = parse(input)\n    }\n  } else {\n    var name = input.match(/^(?:[\\w$]+(?:\\.[\\w$]+)*|\"(?:[^\"]|\\.)*\")/)\n    if (!name) input.error(\"Unexpected syntax: \" + input.str.slice(input.pos, input.pos + 5))\n    type.type = name[0]\n    if (input.eat(\"<\")) {\n      type.typeParams = []\n      while (!input.eat(\">\")) {\n        if (type.typeParams.length && !input.eat(\",\")) input.error(\"Missing comma or closing angle bracket\")\n        type.typeParams.push(parse(input))\n      }\n    }\n  }\n  return type\n}\n"
  },
  {
    "path": "test/class_addmethod.js",
    "content": "// ::- The Foo class\nclass Foo {}\n\n// :: (number, string) → bool\n// A method\nFoo.prototype.bar = (a, b) => true\n"
  },
  {
    "path": "test/class_addmethod.json",
    "content": "{\n  \"Foo\": {\n    \"id\": \"Foo\",\n    \"description\": \"The Foo class\",\n    \"type\": \"class\",\n    \"loc\": {\n      \"file\": \"test/class_addmethod.js\",\n      \"line\": 1,\n      \"column\": 0\n    },\n    \"properties\": {\n      \"bar\": {\n        \"id\": \"Foo.bar\",\n        \"type\": \"Function\",\n        \"params\": [\n          {\n            \"type\": \"number\",\n            \"name\": \"a\",\n            \"id\": \"Foo.bar^a\",\n            \"loc\": {\n              \"file\": \"test/class_addmethod.js\",\n              \"line\": 4,\n              \"column\": 0\n            }\n          },\n          {\n            \"type\": \"string\",\n            \"name\": \"b\",\n            \"id\": \"Foo.bar^b\",\n            \"loc\": {\n              \"file\": \"test/class_addmethod.js\",\n              \"line\": 4,\n              \"column\": 0\n            }\n          }\n        ],\n        \"returns\": {\n          \"type\": \"bool\",\n          \"id\": \"Foo.bar^returns\",\n          \"loc\": {\n            \"file\": \"test/class_addmethod.js\",\n            \"line\": 4,\n            \"column\": 0\n          }\n        },\n        \"description\": \"A method\",\n        \"loc\": {\n          \"file\": \"test/class_addmethod.js\",\n          \"line\": 4,\n          \"column\": 0\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/class_ctor.js",
    "content": "// :: (number, number) A vector type\nfunction Point(x, y) {\n  // :: number The x coordinate\n  this.x = x\n  // :: number The y coordinate\n  this.y = y\n}\n\n"
  },
  {
    "path": "test/class_ctor.json",
    "content": "{\n  \"Point\": {\n    \"id\": \"Point\",\n    \"constructor\": {\n      \"id\": \"Point.constructor\",\n      \"type\": \"Function\",\n      \"params\": [\n        {\n          \"type\": \"number\",\n          \"name\": \"x\",\n          \"id\": \"Point.constructor^x\",\n          \"loc\": {\n            \"file\": \"test/class_ctor.js\",\n            \"line\": 1,\n            \"column\": 0\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"name\": \"y\",\n          \"id\": \"Point.constructor^y\",\n          \"loc\": {\n            \"file\": \"test/class_ctor.js\",\n            \"line\": 1,\n            \"column\": 0\n          }\n        }\n      ],\n      \"loc\": {\n        \"file\": \"test/class_ctor.js\",\n        \"line\": 1,\n        \"column\": 0\n      },\n      \"description\": \"A vector type\"\n    },\n    \"type\": \"class\",\n    \"loc\": {\n      \"file\": \"test/class_ctor.js\",\n      \"line\": 1,\n      \"column\": 0\n    },\n    \"properties\": {\n      \"x\": {\n        \"id\": \"Point.x\",\n        \"type\": \"number\",\n        \"loc\": {\n          \"file\": \"test/class_ctor.js\",\n          \"line\": 3,\n          \"column\": 2\n        },\n        \"description\": \"The x coordinate\"\n      },\n      \"y\": {\n        \"id\": \"Point.y\",\n        \"type\": \"number\",\n        \"loc\": {\n          \"file\": \"test/class_ctor.js\",\n          \"line\": 5,\n          \"column\": 2\n        },\n        \"description\": \"The y coordinate\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/class_expr.js",
    "content": "// ::- The Foo class\nconst Foo = class extends Bar {\n  // :: (number, string) -> bool\n  m(a, b) { return true }\n}\n"
  },
  {
    "path": "test/class_expr.json",
    "content": "{\n  \"Foo\": {\n    \"id\": \"Foo\",\n    \"description\": \"The Foo class\",\n    \"type\": \"class\",\n    \"loc\": {\n      \"file\": \"test/class_expr.js\",\n      \"line\": 1,\n      \"column\": 0\n    },\n    \"properties\": {\n      \"m\": {\n        \"id\": \"Foo.m\",\n        \"type\": \"Function\",\n        \"params\": [\n          {\n            \"type\": \"number\",\n            \"name\": \"a\",\n            \"id\": \"Foo.m^a\",\n            \"loc\": {\n              \"file\": \"test/class_expr.js\",\n              \"line\": 3,\n              \"column\": 2\n            }\n          },\n          {\n            \"type\": \"string\",\n            \"name\": \"b\",\n            \"id\": \"Foo.m^b\",\n            \"loc\": {\n              \"file\": \"test/class_expr.js\",\n              \"line\": 3,\n              \"column\": 2\n            }\n          }\n        ],\n        \"returns\": {\n          \"type\": \"bool\",\n          \"id\": \"Foo.m^returns\",\n          \"loc\": {\n            \"file\": \"test/class_expr.js\",\n            \"line\": 3,\n            \"column\": 2\n          }        },\n        \"loc\": {\n          \"file\": \"test/class_expr.js\",\n          \"line\": 3,\n          \"column\": 2\n        }\n      }\n    },\n    \"extends\": {\n      \"type\": \"Bar\",\n      \"loc\": {\n        \"line\": 2,\n        \"column\": 26,\n        \"file\": \"test/class_expr.js\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/class_simple.js",
    "content": "// ::- The Foo class\nclass Foo extends Bar {\n  // :: (number, number)\n  constructor(a, b) {\n    this.a = a\n    this.b = b\n  }\n\n  // :: (number, string) -> bool\n  m(a, b) { return true }\n}\n"
  },
  {
    "path": "test/class_simple.json",
    "content": "{\n  \"Foo\": {\n    \"id\": \"Foo\",\n    \"description\": \"The Foo class\",\n    \"loc\": {\n      \"file\": \"test/class_simple.js\",\n      \"line\": 1,\n      \"column\": 0\n    },\n    \"type\": \"class\",\n    \"properties\": {\n      \"m\": {\n        \"id\": \"Foo.m\",\n        \"type\": \"Function\",\n        \"params\": [\n          {\n            \"type\": \"number\",\n            \"name\": \"a\",\n            \"id\": \"Foo.m^a\",\n            \"loc\": {\n              \"file\": \"test/class_simple.js\",\n              \"line\": 9,\n              \"column\": 2\n            }\n          },\n          {\n            \"type\": \"string\",\n            \"name\": \"b\",\n            \"id\": \"Foo.m^b\",\n            \"loc\": {\n              \"file\": \"test/class_simple.js\",\n              \"line\": 9,\n              \"column\": 2\n            }\n          }\n        ],\n        \"returns\": {\n          \"type\": \"bool\",\n          \"id\": \"Foo.m^returns\",\n          \"loc\": {\n            \"file\": \"test/class_simple.js\",\n            \"line\": 9,\n            \"column\": 2\n          }\n        },\n        \"loc\": {\n          \"file\": \"test/class_simple.js\",\n          \"line\": 9,\n          \"column\": 2\n        }\n      }\n    },\n    \"extends\": {\n      \"type\": \"Bar\",\n      \"loc\": {\n        \"line\": 2,\n        \"column\": 18,\n        \"file\": \"test/class_simple.js\"\n      }\n    },\n    \"constructor\": {\n      \"id\": \"Foo.constructor\",\n      \"loc\": {\n        \"file\": \"test/class_simple.js\",\n        \"line\": 3,\n        \"column\": 2\n      },\n      \"type\": \"Function\",\n      \"params\": [\n        {\n          \"type\": \"number\",\n          \"name\": \"a\",\n          \"id\": \"Foo.constructor^a\",\n          \"loc\": {\n            \"file\": \"test/class_simple.js\",\n            \"line\": 3,\n            \"column\": 2\n          }\n        },\n        {\n          \"type\": \"number\",\n          \"name\": \"b\",\n          \"id\": \"Foo.constructor^b\",\n          \"loc\": {\n            \"file\": \"test/class_simple.js\",\n            \"line\": 3,\n            \"column\": 2\n          }\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "test/class_static.js",
    "content": "// ::- The Foo class\nclass Foo extends Bar {\n  // :: (number, string) -> bool\n  static m(a, b) { return true }\n}\n"
  },
  {
    "path": "test/class_static.json",
    "content": "{\n  \"Foo\": {\n    \"id\": \"Foo\",\n    \"description\": \"The Foo class\",\n    \"type\": \"class\",\n    \"loc\": {\n      \"file\": \"test/class_static.js\",\n      \"line\": 1,\n      \"column\": 0\n    },\n    \"extends\": {\n      \"type\": \"Bar\",\n      \"loc\": {\n        \"line\": 2,\n        \"column\": 18,\n        \"file\": \"test/class_static.js\"\n      }\n    },\n    \"staticProperties\": {\n      \"m\": {\n        \"id\": \"Foo^m\",\n        \"type\": \"Function\",\n        \"params\": [\n          {\n            \"type\": \"number\",\n            \"name\": \"a\",\n            \"id\": \"Foo^m^a\",\n            \"loc\": {\n              \"file\": \"test/class_static.js\",\n              \"line\": 3,\n              \"column\": 2\n            }\n          },\n          {\n            \"type\": \"string\",\n            \"name\": \"b\",\n            \"id\": \"Foo^m^b\",\n            \"loc\": {\n              \"file\": \"test/class_static.js\",\n              \"line\": 3,\n              \"column\": 2\n            }\n          }\n        ],\n        \"returns\": {\n          \"type\": \"bool\",\n          \"id\": \"Foo^m^returns\",\n          \"loc\": {\n            \"file\": \"test/class_static.js\",\n            \"line\": 3,\n            \"column\": 2\n          }\n        },\n        \"loc\": {\n          \"file\": \"test/class_static.js\",\n          \"line\": 3,\n          \"column\": 2\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/class_this.js",
    "content": "// ::- A Foo\nclass Foo {\n  // :: (number)\n  constructor(a) {\n    // :: number\n    // The a property\n    this.a = a\n  }\n}\n\n// :: ()\nFoo.prototype.doStuff = function() {\n  // :: bool\n  // The b property\n  this.b = true\n}\n"
  },
  {
    "path": "test/class_this.json",
    "content": "{\n  \"Foo\": {\n    \"id\": \"Foo\",\n    \"loc\": {\n      \"file\": \"test/class_this.js\",\n      \"line\": 1,\n      \"column\": 0\n    },\n    \"description\": \"A Foo\",\n    \"type\": \"class\",\n    \"constructor\": {\n      \"id\": \"Foo.constructor\",\n      \"type\": \"Function\",\n      \"params\": [\n        {\n          \"type\": \"number\",\n          \"name\": \"a\",\n          \"id\": \"Foo.constructor^a\",\n          \"loc\": {\n            \"file\": \"test/class_this.js\",\n            \"line\": 3,\n            \"column\": 2\n          }\n        }\n      ],\n      \"loc\": {\n        \"file\": \"test/class_this.js\",\n        \"line\": 3,\n        \"column\": 2\n      }\n    },\n    \"properties\": {\n      \"a\": {\n        \"id\": \"Foo.a\",\n        \"type\": \"number\",\n        \"loc\": {\n          \"file\": \"test/class_this.js\",\n          \"line\": 5,\n          \"column\": 4\n        },\n        \"description\": \"The a property\"\n      },\n      \"b\": {\n        \"id\": \"Foo.b\",\n        \"type\": \"bool\",\n        \"loc\": {\n          \"file\": \"test/class_this.js\",\n          \"line\": 13,\n          \"column\": 2\n        },\n        \"description\": \"The b property\"\n      },\n      \"doStuff\": {\n        \"id\": \"Foo.doStuff\",\n        \"type\": \"Function\",\n        \"params\": [],\n        \"loc\": {\n          \"file\": \"test/class_this.js\",\n          \"line\": 11,\n          \"column\": 0\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/defaultarg.js",
    "content": "// :: (bool)\nfunction foo(arg = false) {}\n"
  },
  {
    "path": "test/defaultarg.json",
    "content": "{\n  \"foo\": {\n    \"id\": \"foo\",\n    \"type\": \"Function\",\n    \"params\": [\n      {\n        \"type\": \"bool\",\n        \"default\": \"false\",\n        \"optional\": true,\n        \"name\": \"arg\",\n        \"id\": \"foo^arg\",\n        \"loc\": {\n          \"file\": \"test/defaultarg.js\",\n          \"line\": 1,\n          \"column\": 0\n        }\n      }\n    ],\n    \"loc\": {\n      \"file\": \"test/defaultarg.js\",\n      \"line\": 1,\n      \"column\": 0\n    }\n  }\n}\n"
  },
  {
    "path": "test/diffargname.js",
    "content": "// :: (foo: number, bar: string)\nfunction x(a, b) {}\n"
  },
  {
    "path": "test/diffargname.json",
    "content": "{\n  \"x\": {\n    \"id\": \"x\",\n    \"type\": \"Function\",\n    \"params\": [\n      {\n        \"name\": \"foo\",\n        \"id\": \"x^foo\",\n        \"type\": \"number\",\n        \"loc\": {\n          \"file\": \"test/diffargname.js\",\n          \"line\": 1,\n          \"column\": 0\n        }\n      },\n      {\n        \"name\": \"bar\",\n        \"id\": \"x^bar\",\n        \"type\": \"string\",\n        \"loc\": {\n          \"file\": \"test/diffargname.js\",\n          \"line\": 1,\n          \"column\": 0\n        }\n      }\n    ],\n    \"loc\": {\n      \"file\": \"test/diffargname.js\",\n      \"line\": 1,\n      \"column\": 0\n    }\n  }\n}\n"
  },
  {
    "path": "test/exported.js",
    "content": "// :: () → string\nexport function hello() { return \"hello\" }\n\n// :: number\nexport var x = 10\n\n// :: string\nexport default \"hi\"\n"
  },
  {
    "path": "test/exported.json",
    "content": "{\n  \"hello\": {\n    \"id\": \"hello\",\n    \"type\": \"Function\",\n    \"params\": [],\n    \"returns\": {\n      \"type\": \"string\",\n      \"id\": \"hello^returns\",\n      \"loc\": {\n        \"file\": \"test/exported.js\",\n        \"line\": 1,\n        \"column\": 0\n      }\n    },\n    \"loc\": {\n      \"file\": \"test/exported.js\",\n      \"line\": 1,\n      \"column\": 0\n    },\n    \"exported\": true\n  },\n  \"x\": {\n    \"id\": \"x\",\n    \"type\": \"number\",\n    \"loc\": {\n      \"file\": \"test/exported.js\",\n      \"line\": 4,\n      \"column\": 0\n    },\n    \"exported\": true\n  },\n  \"default\": {\n    \"id\": \"default\",\n    \"type\": \"string\",\n    \"loc\": {\n      \"file\": \"test/exported.js\",\n      \"line\": 7,\n      \"column\": 0\n    },\n    \"exported\": true\n  }\n}\n"
  },
  {
    "path": "test/function.js",
    "content": "// :: (number, number) → number\n// Adds two numbers\nfunction add(a, b) {\n  return a + b\n}\n"
  },
  {
    "path": "test/function.json",
    "content": "{\n  \"add\": {\n    \"id\": \"add\",\n    \"type\": \"Function\",\n    \"params\": [\n      {\n        \"type\": \"number\",\n        \"name\": \"a\",\n        \"id\": \"add^a\",\n        \"loc\": {\n          \"file\": \"test/function.js\",\n          \"line\": 1,\n          \"column\": 0\n        }\n      },\n      {\n        \"type\": \"number\",\n        \"name\": \"b\",\n        \"id\": \"add^b\",\n        \"loc\": {\n          \"file\": \"test/function.js\",\n          \"line\": 1,\n          \"column\": 0\n        }\n      }\n    ],\n    \"returns\": {\n      \"type\": \"number\",\n      \"id\": \"add^returns\",\n      \"loc\": {\n        \"file\": \"test/function.js\",\n        \"line\": 1,\n        \"column\": 0\n      }\n    },\n    \"description\": \"Adds two numbers\",\n    \"loc\": {\n      \"file\": \"test/function.js\",\n      \"line\": 1,\n      \"column\": 0\n    }\n  }\n}\n"
  },
  {
    "path": "test/functionsub.js",
    "content": "// :: (number, number) → number\n// A function\n//\n//   a::- Parameter a\n//   return::- The return value is the sum\nfunction foo(a, b) {\n  return a + b\n}\n"
  },
  {
    "path": "test/functionsub.json",
    "content": "{\n  \"foo\": {\n    \"id\": \"foo\",\n    \"loc\": {\n      \"line\": 1,\n      \"column\": 0,\n      \"file\": \"test/functionsub.js\"\n    },\n    \"type\": \"Function\",\n    \"params\": [\n      {\n        \"loc\": {\n          \"line\": 5,\n          \"column\": 0,\n          \"file\": \"test/functionsub.js\"\n        },\n        \"type\": \"number\",\n        \"name\": \"a\",\n        \"id\": \"foo^a\",\n        \"description\": \"Parameter a\"\n      },\n      {\n        \"loc\": {\n          \"line\": 1,\n          \"column\": 0,\n          \"file\": \"test/functionsub.js\"\n        },\n        \"type\": \"number\",\n        \"name\": \"b\",\n        \"id\": \"foo^b\"\n      }\n    ],\n    \"returns\": {\n      \"loc\": {\n        \"line\": 7,\n        \"column\": 0,\n        \"file\": \"test/functionsub.js\"\n      },\n      \"type\": \"number\",\n      \"id\": \"foo^returns\",\n      \"description\": \"The return value is the sum\"\n    },\n    \"description\": \"A function\\n\"\n  }\n}\n"
  },
  {
    "path": "test/infer.js",
    "content": "// ::- It's x\nvar x = 10\n\n// ::- It's y\nvar y = true\n\n// ::- It's z\nvar z = /foo/\n\n// ::- It's obj\nvar obj = new Something\n"
  },
  {
    "path": "test/infer.json",
    "content": "{\n  \"x\": {\n    \"id\": \"x\",\n    \"loc\": {\n      \"line\": 1,\n      \"column\": 0,\n      \"file\": \"test/infer.js\"\n    },\n    \"description\": \"It's x\",\n    \"type\": \"number\"\n  },\n  \"y\": {\n    \"id\": \"y\",\n    \"loc\": {\n      \"line\": 4,\n      \"column\": 0,\n      \"file\": \"test/infer.js\"\n    },\n    \"description\": \"It's y\",\n    \"type\": \"bool\"\n  },\n  \"z\": {\n    \"id\": \"z\",\n    \"loc\": {\n      \"line\": 7,\n      \"column\": 0,\n      \"file\": \"test/infer.js\"\n    },\n    \"description\": \"It's z\",\n    \"type\": \"RegExp\"\n  },\n  \"obj\": {\n    \"id\": \"obj\",\n    \"loc\": {\n      \"line\": 10,\n      \"column\": 0,\n      \"file\": \"test/infer.js\"\n    },\n    \"description\": \"It's obj\",\n    \"type\": \"Something\"\n  }\n}\n"
  },
  {
    "path": "test/literal.js",
    "content": "// :: (union<\"a\",\"b\">, number, number) → number\n// Returns a or b\nfunction add(which, a, b) {\n  return which === \"a\" ? a : b\n}\n"
  },
  {
    "path": "test/literal.json",
    "content": "{\n  \"add\": {\n    \"id\": \"add\",\n    \"loc\": {\n      \"line\": 1,\n      \"column\": 0,\n      \"file\": \"test/literal.js\"\n    },\n    \"type\": \"Function\",\n    \"params\": [\n      {\n        \"loc\": {\n          \"line\": 1,\n          \"column\": 0,\n          \"file\": \"test/literal.js\"\n        },\n        \"type\": \"union\",\n        \"typeParams\": [\n          {\n            \"loc\": {\n              \"line\": 1,\n              \"column\": 0,\n              \"file\": \"test/literal.js\"\n            },\n            \"type\": \"\\\"a\\\"\"\n          },\n          {\n            \"loc\": {\n              \"line\": 1,\n              \"column\": 0,\n              \"file\": \"test/literal.js\"\n            },\n            \"type\": \"\\\"b\\\"\"\n          }\n        ],\n        \"name\": \"which\",\n        \"id\": \"add^which\"\n      },\n      {\n        \"loc\": {\n          \"line\": 1,\n          \"column\": 0,\n          \"file\": \"test/literal.js\"\n        },\n        \"type\": \"number\",\n        \"name\": \"a\",\n        \"id\": \"add^a\"\n      },\n      {\n        \"loc\": {\n          \"line\": 1,\n          \"column\": 0,\n          \"file\": \"test/literal.js\"\n        },\n        \"type\": \"number\",\n        \"name\": \"b\",\n        \"id\": \"add^b\"\n      }\n    ],\n    \"returns\": {\n      \"loc\": {\n        \"line\": 1,\n        \"column\": 0,\n        \"file\": \"test/literal.js\"\n      },\n      \"type\": \"number\",\n      \"id\": \"add^returns\"\n    },\n    \"description\": \"Returns a or b\"\n  }\n}\n"
  },
  {
    "path": "test/namedprop.js",
    "content": "// ::- Boo the class\nclass Boo {\n  // someProp:: (number) Some prop\n}\n\n"
  },
  {
    "path": "test/namedprop.json",
    "content": "{\n  \"Boo\": {\n    \"id\": \"Boo\",\n    \"loc\": {\n      \"line\": 1,\n      \"column\": 0,\n      \"file\": \"test/namedprop.js\"\n    },\n    \"type\": \"class\",\n    \"description\": \"Boo the class\",\n    \"properties\": {\n      \"someProp\": {\n        \"id\": \"Boo.someProp\",\n        \"loc\": {\n          \"line\": 3,\n          \"column\": 2,\n          \"file\": \"test/namedprop.js\"\n        },\n        \"type\": \"Function\",\n        \"params\": [\n          {\n            \"loc\": {\n              \"line\": 3,\n              \"column\": 2,\n              \"file\": \"test/namedprop.js\"\n            },\n            \"type\": \"number\"\n          }\n        ],\n        \"description\": \"Some prop\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/obj.js",
    "content": "// :: {extra: number}\nlet obj = {\n  // :: number A property\n  foo: 10,\n  bar: 20\n}\n\n// ::- An added property\nobj.baz = 30\n"
  },
  {
    "path": "test/obj.json",
    "content": "{\n  \"obj\": {\n    \"id\": \"obj\",\n    \"type\": \"Object\",\n    \"properties\": {\n      \"extra\": {\n        \"id\": \"obj.extra\",\n        \"type\": \"number\",\n        \"loc\": {\n          \"file\": \"test/obj.js\",\n          \"line\": 1,\n          \"column\": 0\n        }\n      },\n      \"foo\": {\n        \"id\": \"obj.foo\",\n        \"description\": \"A property\",\n        \"type\": \"number\",\n        \"loc\": {\n          \"file\": \"test/obj.js\",\n          \"line\": 3,\n          \"column\": 2\n        }\n      },\n      \"baz\": {\n        \"id\": \"obj.baz\",\n        \"description\": \"An added property\",\n        \"type\": \"number\",\n        \"loc\": {\n          \"file\": \"test/obj.js\",\n          \"line\": 8,\n          \"column\": 0\n        }\n      }\n    },\n    \"loc\": {\n      \"file\": \"test/obj.js\",\n      \"line\": 1,\n      \"column\": 0\n    }\n  }\n}\n"
  },
  {
    "path": "test/phantom.js",
    "content": "// Abc:: class\n// The Abc class\n\n// Abc.m1:: (a: number) → number\n// Method #1\n\n// Abc.m2:: () #static\n// Method #2\n\n// myString:: string\n// My string\n"
  },
  {
    "path": "test/phantom.json",
    "content": "{\n  \"Abc\": {\n    \"id\": \"Abc\",\n    \"loc\": {\n      \"line\": 1,\n      \"column\": 0,\n      \"file\": \"test/phantom.js\"\n    },\n    \"description\": \"The Abc class\",\n    \"type\": \"class\",\n    \"properties\": {\n      \"m1\": {\n        \"id\": \"Abc.m1\",\n        \"loc\": {\n          \"line\": 4,\n          \"column\": 0,\n          \"file\": \"test/phantom.js\"\n        },\n        \"type\": \"Function\",\n        \"params\": [\n          {\n            \"loc\": {\n              \"line\": 4,\n              \"column\": 0,\n              \"file\": \"test/phantom.js\"\n            },\n            \"type\": \"number\",\n            \"name\": \"a\",\n            \"id\": \"Abc.m1^a\"\n          }\n        ],\n        \"returns\": {\n          \"loc\": {\n            \"line\": 4,\n            \"column\": 0,\n            \"file\": \"test/phantom.js\"\n          },\n          \"type\": \"number\",\n          \"id\": \"Abc.m1^returns\"\n        },\n        \"description\": \"Method #1\"\n      }\n    },\n    \"staticProperties\": {\n      \"m2\": {\n        \"id\": \"Abc^m2\",\n        \"loc\": {\n          \"line\": 7,\n          \"column\": 0,\n          \"file\": \"test/phantom.js\"\n        },\n        \"type\": \"Function\",\n        \"params\": [],\n        \"$static\": \"true\",\n        \"description\": \"Method #2\"\n      }\n    }\n  },\n  \"myString\": {\n    \"id\": \"myString\",\n    \"loc\": {\n      \"line\": 10,\n      \"column\": 0,\n      \"file\": \"test/phantom.js\"\n    },\n    \"type\": \"string\",\n    \"description\": \"My string\"\n  }\n}\n"
  },
  {
    "path": "test/restarg.js",
    "content": "// :: ([string]) -> bool\nfunction bar(...stuff) {}\n"
  },
  {
    "path": "test/restarg.json",
    "content": "{\n  \"bar\": {\n    \"id\": \"bar\",\n    \"type\": \"Function\",\n    \"params\": [\n      {\n        \"type\": \"Array\",\n        \"typeParams\": [{\n          \"type\": \"string\",\n          \"loc\": {\n            \"file\": \"test/restarg.js\",\n            \"line\": 1,\n            \"column\": 0\n          }\n        }],\n        \"rest\": true,\n        \"name\": \"stuff\",\n        \"id\": \"bar^stuff\",\n        \"loc\": {\n          \"file\": \"test/restarg.js\",\n          \"line\": 1,\n          \"column\": 0\n        }\n      }\n    ],\n    \"returns\": {\n      \"type\": \"bool\",\n      \"id\": \"bar^returns\",\n      \"loc\": {\n        \"file\": \"test/restarg.js\",\n        \"line\": 1,\n        \"column\": 0\n      }\n    },\n    \"loc\": {\n      \"file\": \"test/restarg.js\",\n      \"line\": 1,\n      \"column\": 0\n    }\n  }\n}\n"
  },
  {
    "path": "test/run.js",
    "content": "var fs = require(\"fs\")\n\nvar getdocs = require(\"../src\")\n\nvar filter = process.argv[2]\n\nfs.readdirSync(__dirname).forEach(function(filename) {\n  var isJSON = /^([^\\.]+)\\.json$/.exec(filename)\n  if (!isJSON || (filter && isJSON[1].indexOf(filter) != 0)) return\n\n  var expected = JSON.parse(fs.readFileSync(__dirname + \"/\" + filename, \"utf8\"))\n  var jsfile = \"/\" + isJSON[1] + \".js\"\n  var returned = getdocs.gather(fs.readFileSync(__dirname + jsfile, \"utf8\"), {filename: \"test\" + jsfile})\n  try {\n    compare(returned, expected, \"\")\n  } catch(e) {\n    console.error(isJSON[1] + \": \" + e.message)\n    console.error(\"in \" + JSON.stringify(returned, null, 2))\n  }\n})\n\nfunction hop(obj, prop) {\n  return Object.prototype.hasOwnProperty.call(obj, prop)\n}\n\nfunction compare(a, b, path) {\n  if (typeof a != \"object\" || typeof b != \"object\") {\n    if (a !== b) throw new Error(\"Mismatch at \" + path + \": \" + a + \" vs \" + b)\n  } else {\n    for (var prop in a) if (hop(a, prop)) {\n      if (!(prop in b))\n        throw new Error(\"Unexpected property \" + path + \".\" + prop)\n      else\n        compare(a[prop], b[prop], path + \".\" + prop)\n    }\n    for (var prop in b) if (hop(b, prop)) {\n      if (!(prop in a))\n        throw new Error(\"Missing property \" + path + \".\" + prop)\n    }\n  }\n}\n"
  },
  {
    "path": "test/subcomment.js",
    "content": "// :: Object An object\n//\n//   x:: number The x coordinate\n//\n//   y:: Object The y object\n//\n//     inner:: bool A property of `y`\nvar x = {}\n"
  },
  {
    "path": "test/subcomment.json",
    "content": "{\n  \"x\": {\n    \"id\": \"x\",\n    \"loc\": {\n      \"line\": 1,\n      \"column\": 0,\n      \"file\": \"test/subcomment.js\"\n    },\n    \"type\": \"Object\",\n    \"description\": \"An object\\n\",\n    \"properties\": {\n      \"x\": {\n        \"id\": \"x.x\",\n        \"loc\": {\n          \"line\": 4,\n          \"column\": 0,\n          \"file\": \"test/subcomment.js\"\n        },\n        \"type\": \"number\",\n        \"description\": \"The x coordinate\\n\"\n      },\n      \"y\": {\n        \"id\": \"x.y\",\n        \"loc\": {\n          \"line\": 7,\n          \"column\": 0,\n          \"file\": \"test/subcomment.js\"\n        },\n        \"type\": \"Object\",\n        \"description\": \"The y object\\n\",\n        \"properties\": {\n          \"inner\": {\n            \"id\": \"x.y.inner\",\n            \"loc\": {\n              \"line\": 10,\n              \"column\": 0,\n              \"file\": \"test/subcomment.js\"\n            },\n            \"type\": \"bool\",\n            \"description\": \"A property of `y`\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/tags.js",
    "content": "// :: () #deprecated #exported=false #context=\"a \\\"string\\\"\"\n// Hello!\nfunction foo() {}\n"
  },
  {
    "path": "test/tags.json",
    "content": "{\n  \"foo\": {\n    \"id\": \"foo\",\n    \"loc\": {\n      \"file\": \"test/tags.js\",\n      \"line\": 1,\n      \"column\": 0\n    },\n    \"$deprecated\": \"true\",\n    \"$exported\": \"false\",\n    \"$context\": \"a \\\"string\\\"\",\n    \"description\": \"Hello!\",\n    \"type\": \"Function\",\n    \"params\": []\n  }\n}\n"
  },
  {
    "path": "test/union.js",
    "content": "// :: (union<number, string>, ?union<bool, RegExp, [Error]>) → union<number, bool>\n// That's one complex signature\nfunction blah(x, y) {}\n"
  },
  {
    "path": "test/union.json",
    "content": "{\n  \"blah\": {\n    \"id\": \"blah\",\n    \"type\": \"Function\",\n    \"params\": [\n      {\n        \"type\": \"union\",\n        \"typeParams\": [\n          {\n            \"type\": \"number\",\n            \"loc\": {\n              \"file\": \"test/union.js\",\n              \"line\": 1,\n              \"column\": 0\n            }\n          },\n          {\n            \"type\": \"string\",\n            \"loc\": {\n              \"file\": \"test/union.js\",\n              \"line\": 1,\n              \"column\": 0\n            }\n          }\n        ],\n        \"name\": \"x\",\n        \"id\": \"blah^x\",\n        \"loc\": {\n          \"file\": \"test/union.js\",\n          \"line\": 1,\n          \"column\": 0\n        }\n      },\n      {\n        \"type\": \"union\",\n        \"typeParams\": [\n          {\n            \"type\": \"bool\",\n            \"loc\": {\n              \"file\": \"test/union.js\",\n              \"line\": 1,\n              \"column\": 0\n            }\n          },\n          {\n            \"type\": \"RegExp\",\n            \"loc\": {\n              \"file\": \"test/union.js\",\n              \"line\": 1,\n              \"column\": 0\n            }\n          },\n          {\n            \"type\": \"Array\",\n            \"loc\": {\n              \"file\": \"test/union.js\",\n              \"line\": 1,\n              \"column\": 0\n            },\n            \"typeParams\": [{\n              \"type\": \"Error\",\n              \"loc\": {\n                \"file\": \"test/union.js\",\n                \"line\": 1,\n                \"column\": 0\n              }\n            }]\n          }\n        ],\n        \"optional\": true,\n        \"name\": \"y\",\n        \"id\": \"blah^y\",\n        \"loc\": {\n          \"file\": \"test/union.js\",\n          \"line\": 1,\n          \"column\": 0\n        }\n      }\n    ],\n    \"returns\": {\n      \"type\": \"union\",\n      \"id\": \"blah^returns\",\n      \"typeParams\": [\n        {\n          \"type\": \"number\",\n          \"loc\": {\n            \"file\": \"test/union.js\",\n            \"line\": 1,\n            \"column\": 0\n          }\n        },\n        {\n          \"type\": \"bool\",\n          \"loc\": {\n            \"file\": \"test/union.js\",\n            \"line\": 1,\n            \"column\": 0\n          }\n        }\n      ],\n      \"loc\": {\n        \"file\": \"test/union.js\",\n        \"line\": 1,\n        \"column\": 0\n      }\n    },\n    \"loc\": {\n      \"file\": \"test/union.js\",\n      \"line\": 1,\n      \"column\": 0\n    },\n    \"description\": \"That's one complex signature\"\n  }\n}\n"
  }
]