nodes
Nodes is a dom-like library for spidermonkey-type ast objects, generated by Esprima or any compatible ast generator. Its purpose is to make it easy to work with ast nodes, by providing a dom-like environment with references to parentNodes, query-selector searches and type validations.
The idea is that you provide an ast json object, and you get back a document-like object. This package does not require esprima, nor does it enforce any specific esprima version.
It supports the full es6 specification.
The file in the root of this project, spec.json
, has been manually generated by using estree as a reference. The json specification is used programmatically to set up the fake multiple inheritance for the nodes classes.
overview
Document creation:
var nodes = require('nodes');
var parse = require('esprima').parse;
var ast = parse(javascriptString);
var program = nodes.build(ast);
creating new nodes
It is also possible to create new ast nodes using the classses provided by nodes directly:
var types = require('nodes').types;
var identifier = new types.Identifier({ name: })
var declaration = new types.VariableDeclaration({ kind: "var" });
var declarator = new types.VariableDeclarator;
declaration.declarations.push(declarator)
syntax
Nodes also exports a syntax object, which holds every type as a string.
var syntax = require('nodes').syntax;
syntax.Identifier === "Identifier";
parentNodes
Whenever you update any node or list, which also happens at creation, parentNodes references are saved to child nodes:
var expression = program.body[0];
expression.parentNode === program.body;
expression.expression.parentNode === expression;
var declaration = nodes.build(parse('var x = 0;').body[0]);
parogram.body.push(declaration);
declaration.parentNode === program.body;
validation
Each node property is validated against rules defined by the Mozilla Parser API:
declaration.declarations[0].id = 10;
Same goes for lists:
program.body.push(nodes.build({type: "Identifier", name: "asd"}));
queries
nodes implements css-like queries for any ast nodes.
Say you want to get all the Identifiers in a program:
var identifiers = program.search('#Identifier');
console.log(identifiers);
Or maybe you are interested in the names only?
var identifiers = program.search('#Identifier > name');
console.log(identifiers);
An ID selector in this instance is equivalent to [type=id].
Direct children:
var id = program.find('#FunctionDeclaration > id');
console.log(id);
note: find is like search, but ends the traversal when it finds the first result.
Any level:
var id = program.search('#FunctionDeclaration id');
console.log(id);
It also supports generic types like #Function, #Statement, #Expression or #Pattern, in case you want to filter by the base type.
For instance, #FunctionExpression, #FunctionDeclaration and #ArrowFunctionExpression will all react to #Function.
parent combinators:
var functionDeclaration = id.find('< #FunctionDeclaration');
console.log(functionDeclaration);
parent method, for direct parents. this works like matchesSelector in dom, and also supports expression sequences:
var functionDeclaration = id.parent('#FunctionDeclaration');
console.log(functionDeclaration);
parents query, for any parents, same as parent() but keeps traversing:
var functions = id.parents('#Function');
console.log(functions);
:declaration pseudo class to find any declaration
program.search('#Identifier:declaration > name');
program.search('#Identifier:declaration(someVarName)')
:reference pseudo class to find any reference
program.search('#Identifier:reference');
program.search('#Identifier:reference(someName)')
scope, scopes methods, works like parent / parents, but only cares about scopes.
id.scope();
:scope pseudo class
program.search(':scope');
You can also use attribute selectors or classNames in the queries to check if nodes have / match specific properties. Works pretty much like in the dom.
search / parents / scopes return a BaseList instance, which is an Array-Like object. Just like lists (e.g. program.body) you can run sub queries off of them.
var functions = program.search('#Function');
functions.search('id');
Multiple queries are also supported:
var functions = program.search('#FunctionExpression, #FunctionDeclaration');
Every list gets decomposed to its nodes:
var bodyElements = program.search('body');
serialization
Document serialization is automatic, and no special steps are needed:
var generate = require('escodegen').generate;
generate(program);
If you need to you can use toJSON to convert the document back to json format:
var object = program.toJSON();
There is a toString() method to generate json, which is just a shortcut to JSON.stringify.
var jsonString = program.toString()