Skip to content
Parser module for JsDoc or Closure Compiler format type-annotation.
Branch: master
Clone or download

README.md

jsdoctypeparser

Build Status NPM version

The parser can parse:

Live demo

The live demo is available.

Usage (Programmatic)

Parsing

const {parse} = require('jsdoctypeparser');

const ast = parse('Array<MyClass>');

The ast becomes:

{
  "type": "GENERIC",
  "subject": {
    "type": "NAME",
    "name": "Array"
  },
  "objects": [
    {
      "type": "NAME",
      "name": "MyClass"
    }
  ],
  "meta": {
    "syntax": "ANGLE_BRACKET"
  }
}

See the AST specifications.

Publishing

We can stringify the AST nodes by using publish.

const {publish} = require('jsdoctypeparser');

const ast = {
  type: 'GENERIC',
  subject: {
    type: 'NAME',
    name: 'Array'
  },
  objects: [
    {
      type: 'NAME',
      name: 'MyClass'
    }
  ]
};

const string = publish(ast);

The string becomes:

"Array<MyClass>"

Custom publishing

We can change the stringification strategy by using the 2nd parameter of publish(node, publisher). The publisher MUST have handlers for all node types (see lib/NodeType.js).

And we can override default behavior by using createDefaultPublisher.

const {publish, createDefaultPublisher} = require('jsdoctypeparser');

const ast = {
  type: 'NAME',
  name: 'MyClass',
};

const customPublisher = createDefaultPublisher();
customPublisher.NAME = (node, pub) =>
  `<a href="./types/${node.name}.html">${node.name}</a>`;

const string = publish(ast, customPublisher);

The string becomes:

<a href="./types/MyClass.html">MyClass</a>

Traversing

We can traverse the AST by using traverse. This function takes 3 parameters (a node and an onEnter handler, an onLeave handler). The handlers take a visiting node.

const {parse, traverse} = require('jsdoctypeparser');
const ast = parse('Array<{ key1: function(), key2: A.B.C }>');

function onEnter(node, parentName, parentNode) {
  console.log('enter', node.type, parentName, parentNode.type);
}

function onLeave(node, parentName, parentNode) {
  console.log('leave', node.type, parentName, parentNode.type);
}

traverse(ast, onEnter, onLeave);

The output will be:

enter GENERIC null null
enter NAME subject GENERIC
leave NAME subject GENERIC
enter RECORD objects GENERIC
enter RECORD_ENTRY entries RECORD
enter FUNCTION value RECORD_ENTRY
leave FUNCTION value RECORD_ENTRY
leave RECORD_ENTRY entries RECORD
enter RECORD_ENTRY entries RECORD
enter MEMBER value RECORD_ENTRY
enter MEMBER owner MEMBER
enter NAME owner MEMBER
leave NAME owner MEMBER
leave MEMBER owner MEMBER
leave MEMBER value RECORD_ENTRY
leave RECORD_ENTRY entries RECORD
leave RECORD objects GENERIC
leave GENERIC null null

AST Specifications

NAME

Example:

/**
 * @type {name}
 */

Structure:

{
  "type": "NAME",
  "name": string
}

MEMBER

Example:

/**
 * @type {owner.name}
 * @type {superOwner.owner.name}
 */

Structure:

{
  "type": "MEMBER",
  "name": string,
  "quoteStyle": "none",
  "owner": node,
  "hasEventPrefix": boolean
}

INNER_MEMBER

Example:

/**
 * @type {owner~name}
 */

Structure:

{
  "type": "INNER_MEMBER",
  "name": string,
  "quoteStyle": "none",
  "owner": node,
  "hasEventPrefix": boolean
}

INSTANCE_MEMBER

Example:

/**
 * @type {owner#name}
 */

Structure:

{
  "type": "INSTANCE_MEMBER",
  "name": string,
  "quoteStyle": "none",
  "owner": node,
  "hasEventPrefix": boolean
}

UNION

Example:

/**
 * @type {left|right}
 * @type {(left|right)}
 * @type {left/right}
 */

Structure:

{
  "type": "UNION",
  "left": node,
  "right": node,
  "meta": {
    "syntax": ("PIPE" or "SLASH")
  }
}

RECORD

Example:

/**
 * @type {{}}
 * @type {{ key: value }}
 * @type {{ key: value, anyKey }}
 */

Structure:

{
  "type": "RECORD",
  "entries": [
    recordEntryNode,
    recordEntryNode,
    ...
  ]
}

RECORD_ENTRY

Structure:

{
  "type": "RECORD_ENTRY",
  "key": string,
  "value": node (or null)
}

GENERIC

Example:

/**
 * @type {Subject<Object, Object>}
 * @type {Object[]}
 */

Structure:

{
  "type": "GENERIC",
  "subject": node,
  "objects": [
    node,
    node,
    ...
  ],
  "meta": {
    "syntax": ("ANGLE_BRACKET" or "ANGLE_BRACKET_WITH_DOT" or "SQUARE_BRACKET")
  }
}

FUNCTION

Example:

/**
 * @type {function()}
 * @type {function(param, param): return}
 * @type {function(this: Context)}
 * @type {function(new: Class)}
 */

Structure:

{
  "type": "FUNCTION",
  "params": [
    node,
    node,
    ...
  ],
  "returns": node (or null),
  "new": node (or null),
  "this": node (or null)
}

OPTIONAL

Example:

/**
 * @type {Optional=}
 */

Structure:

{
  "type": "OPTIONAL",
  "value": node,
  "meta": {
    "syntax": ("PREFIX_EQUALS_SIGN" or "SUFFIX_EQUALS_SIGN")
  }
}

NULLABLE

Example:

/**
 * @type {?Nullable}
 */

Structure:

{
  "type": "NULLABLE",
  "value": node,
  "meta": {
    "syntax": ("PREFIX_QUESTION_MARK" or "SUFFIX_QUESTION_MARK")
  }
}

NOT_NULLABLE

Example:

/**
 * @type {!NotNullable}
 */

Structure:

{
  "type": "NOT_NULLABLE",
  "value": node,
  "meta": {
    "syntax": ("PREFIX_BANG" or "SUFFIX_BANG")
  }
}

VARIADIC

Example:

/**
 * @type {...Variadic}
 * @type {Variadic...}
 * @type {...}
 */

Structure:

{
  "type": "VARIADIC",
  "value": node (or null),
  "meta": {
    "syntax": ("PREFIX_DOTS" or "SUFFIX_DOTS" or "ONLY_DOTS")
  }
}

MODULE

Example:

/**
 * @type {module:path/to/file.Module}
 */

Structure:

{
  "type": "MODULE",
  "value": node
}

FILE_PATH

Example:

/**
 * @type {module:path/to/file.Module}
 *               ^^^^^^^^^^^^
 */

Structure:

{
  "type": "FILE_PATH",
  "path": string
}

EXTERNAL

Example:

/**
 * @type {external:External}
 */

Structure:

{
  "type": "EXTERNAL",
  "value": node
}

STRING_VALUE

Example:

/**
 * @type {"abc"}
 * @type {"can\"escape"}
 */

Structure:

{
  "type": "STRING_VALUE",
  "quoteStyle": "double",
  "string": string
}

NUMBER_VALUE

Example:

/**
 * @type {123}
 * @type {0b11}
 * @type {0o77}
 * @type {0xff}
 */

Structure:

{
  "type": "NUMBER_VALUE",
  "number": string
}

ANY

Example:

/**
 * @type {*}
 */

Structure:

{
  "type": "ANY"
}

UNKNOWN

Example:

/**
 * @type {?}
 */

Structure:

{
  "type": "UNKNOWN"
}

PARENTHESIS

Example:

/**
 * @type {(Foo)}
 */

Structure:

{
  "type": "PARENTHESIS",
  "value": node
}

Others

We can use a parenthesis to change operator orders.

/**
 * @type {(module:path/to/file.js).foo}
 */

Usage (CLI)

To parse a type into a JSON structure, you may pass a string argument containing the structure to parse (with the JSON results equivalent to the parsing example above):

jsdoctypeparser 'Array<MyClass>'

Note: There is no need to prefix the path to the jsdoctypeparser binary, e.g., with ./node_modules/.bin/ when you are running within one of the package.json scripts or if you have installed the package globally.

License

This script is licensed under the MIT.

You can’t perform that action at this time.