Security News
JSR Working Group Kicks Off with Ambitious Roadmap and Plans for Open Governance
At its inaugural meeting, the JSR Working Group outlined plans for an open governance model and a roadmap to enhance JavaScript package management.
jsdoctypeparser
Advanced tools
The jsdoctypeparser npm package is a tool for parsing and manipulating JSDoc type expressions. It allows developers to parse JSDoc type strings into an abstract syntax tree (AST) and then manipulate or analyze these types programmatically.
Parsing JSDoc Type Strings
This feature allows you to parse a JSDoc type string into an abstract syntax tree (AST). The code sample demonstrates how to parse the type string 'Array.<string>' and output the resulting AST.
const { parse } = require('jsdoctypeparser');
const typeString = 'Array.<string>';
const ast = parse(typeString);
console.log(JSON.stringify(ast, null, 2));
Stringifying AST Back to JSDoc Type String
This feature allows you to convert an AST back into a JSDoc type string. The code sample shows how to parse a type string into an AST and then convert it back to a string.
const { parse, stringify } = require('jsdoctypeparser');
const typeString = 'Array.<string>';
const ast = parse(typeString);
const newTypeString = stringify(ast);
console.log(newTypeString);
Handling Complex Type Expressions
This feature demonstrates the ability to handle complex JSDoc type expressions. The code sample parses a complex type string representing an object with properties of different types.
const { parse } = require('jsdoctypeparser');
const complexTypeString = '{a: number, b: string|boolean}';
const ast = parse(complexTypeString);
console.log(JSON.stringify(ast, null, 2));
Doctrine is a popular library for parsing JSDoc comments, including type expressions. It provides a more comprehensive solution for parsing entire JSDoc comments, not just type strings. Compared to jsdoctypeparser, Doctrine offers broader functionality but may be more complex to use if you only need to parse type expressions.
Comment-parser is a library for parsing JavaScript comments, including JSDoc comments. It focuses on extracting and parsing comments from source code, including type annotations. While it provides similar functionality for parsing JSDoc types, it is more focused on the overall comment structure rather than just type expressions.
The parser can parse:
foo.bar
, String[]
Array<string>
, function(arg1, arg2): ret
(x: number) => string
, typeof x
, import("./some-module")
Array<Array<string>>
, function(function(Function))
The live demo is available.
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.
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>"
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>
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
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)}
*/
Structure:
{
"type": "UNION",
"left": node,
"right": node
}
INTERSECTION
Example:
/**
* @type {left&right}
* @type {(left&right)}
*/
Structure:
{
"type": "INTERSECTION",
"left": node,
"right": node
}
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
}
We can use a parenthesis to change operator orders.
/**
* @type {(module:path/to/file.js).foo}
*/
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.
FAQs
Strict JsDoc type expression parser.
We found that jsdoctypeparser demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
At its inaugural meeting, the JSR Working Group outlined plans for an open governance model and a roadmap to enhance JavaScript package management.
Security News
Research
An advanced npm supply chain attack is leveraging Ethereum smart contracts for decentralized, persistent malware control, evading traditional defenses.
Security News
Research
Attackers are impersonating Sindre Sorhus on npm with a fake 'chalk-node' package containing a malicious backdoor to compromise developers' projects.