👟 Tiny and fast ESTree-compliant AST walker and modifier.
👟 A tiny and fast ESTree-compliant AST walker and modifier.
Install with the Node Package Manager:
npm install astravel
Alternatively, checkout this repository and install the development dependencies to build the module file:
git clone https://github.com/davidbonnet/astravel.gitcd astravelnpm install
The astravel module exports the following items:
defaultTraveler⬅️
traveler⚠️ Deprecated in favor of ES6 class notation.
This object describes a basic AST traveler. It contains the following methods:
go(node, state): Travels through the provided AST node with a given state (an object that can be of any type) by recursively calling this method.find(predicate, node, state) ➞ { node, state }?: Returns { node, state } for which predicate(node, state) returns truthy, starting at the specified AST node and with the provided state. Otherwise, returns undefined.[NodeType](node, state): Method handler for a specific NodeType.makeChild(properties) ➞ traveler: Returns a custom AST traveler that inherits from this traveler with its own provided properties and the property super that points to this traveler.makeTraveler()➡️
(properties)
⬅️traveler⚠️ Deprecated in favor of ES6 class notation.
This function is similar to astravel.defaultTraveler.makeChild: it returns a traveler that inherits from the defaultTraveler with its own provided properties and the property super that points to the defaultTraveler object. These properties should redefine the traveler’s behavior by implementing the go(node, state) method and/or any node handler.
When redefining the go method, make sure its basic functionality is kept by calling the parent’s go method to keep traveling through the AST:
const customTraveler = makeTraveler({go: function (node, state) {// Code before entering the nodeconsole.log('Entering ' + node.type)// Call the parent's `go` methodthis.super.go.call(this, node, state)// Code after leaving the nodeconsole.log('Leaving ' + node.type)},})
To skip specific node types, the most effective way is to replace the corresponding node handlers with a function that does nothing:
import { makeTraveler } from 'astravel'const ignore = Function.prototypeconst customTraveler = makeTraveler({FunctionDeclaration: ignore,FunctionExpression: ignore,ArrowFunctionExpression: ignore,})
attachComments()➡️
(ast, comments)
⬅️ast
This function attaches a list of comments to the corresponding nodes of a provided ast and returns that same ast. The ast is modified in-place and only the nodes getting comments are augmented with a comments and/or a trailingComments array property.
Each comment should be an object with the following properties:
type: "Line" or "Block"value: Comment string valuestart: Comment starting character offset numberend: Comment ending character offset numberloc: Location object with start and end properties containing one-based line number and zero-based column number properties.The following examples show how to obtain a proper list of comments of a given source code and how to attach them on the generated ast:
import { parse } from 'meriyah'import { attachComments } from 'astravel'const comments = []const ast = parse(code, {// Comments are stored in this arrayonComment: comments,})// Attach comments on the ASTattachComments(ast, comments)
import { parse } from 'acorn'import { attachComments } from 'astravel'const comments = []const ast = parse(code, {// This ensures that the `loc` property is present on comment objectslocations: true,onComment: comments,})attachComments(ast, comments)
The algorithm assumes that comments are not put in exotic places, such as in-between function arguments, and proceeds as follows:
comments property.trailingComments to that code block.In this example, the comments tell to which statement they are attached:
// Attached to the variable declaration just belowconst point = {// Attached to the property definition just belowx: 0,y: 0, // Attached to the property definition on its left}/*Attached to the function declaration just below.*/function add(a, b) {/*Attached to the function body because it is the first comment block.*/return a + b // Attached to the return statement on its left// Trailing comment attached as such to the function body}// Trailing comment attached as such to the program body