• Stars
    star
    171
  • Rank 214,302 (Top 5 %)
  • Language
    JavaScript
  • Created over 11 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Recursively walk and transform EcmaScript AST

Rocambole Build Status

rocambole

Recursively walk and add extra information/helpers to Esprima / Mozilla SpiderMonkey Parser API compatible AST.

The main difference between other tools is that it also keeps information about tokens and white spaces and it is meant to be used to transform the tokens and not the string values itself.

This library is specially useful for non-destructive AST manipulation.

Inspiration

This module was heavily inspired by node-falafel and node-burrito but I needed more information than what is currently available on falafel (specially about tokens, empty lines and white spaces) and also needed to do the node traversing on the opposite order (start from leaf nodes). The amount of changes required to introduce the new features and the differences on the concept behind the tool justified a new project.

It was created mainly to be used on esformatter.

Extra Tokens

Besides all the regular tokens returned by esprima we also add a few more that are important for non-destructive transformations:

  • WhiteSpace
    • Can store multiple white spaces (tabs are considered white space, line breaks not). Important if you want to do non-destructive replacements that are white-space sensitive.
    • Multiple subsequent white spaces are treated as a single token.
  • LineBreak
  • LineComment
  • BlockComment

It's way easier to rebuild the JS string if the tokens already have line breaks and comments. It's also easier to identify if previous/next/current token is a LineBreak or Comment (sometimes needed for non-destructive transformations).

Rocambole structure might change in the future to keep the extraneous tokens outside the tokens array and also add an option to toggle the behavior. (issue #7)

Extra Properties

Each Node have the following extra properties/methods:

  • parent : Node|undefined
  • toString() : string
  • next : Node|undefined
  • prev : Node|undefined
  • depth : Number
  • startToken : Token
  • endToken : Token

Each token also have:

  • prev : Token|undefined
  • next : Token|undefined

BlockComment also have:

  • originalIndent: String|undefined

To get a better idea of the generated AST structure try out rocambole-visualize.

Linked List

You should treat the tokens as a linked list instead of reading the ast.tokens array (inserting/removing items from a linked list is very cheap and won't break the loop). You should grab a reference to the node.startToken and get token.next until you find the desired token or reach the end of the program. To loop between all tokens inside a node you can do like this:

var token = node.startToken;
while (token !== node.endToken.next) {
    doStuffWithToken(token);
    token = token.next;
}

The method toString loops through all tokens between node.startToken and node.endToken grabbing the token.raw (used by comments) or token.value properties. To implement a method similar to falafel update() you can do this:

function update(node, str){
    var newToken = {
        type : 'Custom', // can be anything (not used internally)
        value : str
    };
    // update linked list references
    if ( node.startToken.prev ) {
        node.startToken.prev.next = newToken;
        newToken.prev = node.startToken.prev;
    }
    if ( node.endToken.next ) {
        node.endToken.next.prev = newToken;
        newToken.next = node.endToken.next;
    }
    node.startToken = node.endToken = newToken;
}

Helpers

I plan to create helpers as separate projects when possible.

API

rocambole.parse(source, [opts])

Parses a string and instrument the AST with extra properties/methods.

var rocambole = require('rocambole');
var ast = rocambole.parse(string);
console.log( ast.startToken );
// to get a string representation of all tokens call toString()
console.log( ast.toString() );

You can pass custom options as the second argument:

rocambole.parse(source, {
    loc: true,
    // custom options are forwarded to the rocambole.parseFn
    ecmaFeatures: {
        arrowFunctions: true
    }
});

IMPORTANT: rocambole needs the range, tokens and comment info to build the token linked list, so these options will always be set to true.

rocambole.parseFn:Function

Allows you to override the function used to parse the program. Defaults to esprima.parse.

// espree is compatible with esprima AST so things should work as expected
var espree = require('espree');
rocambole.parseFn = espree.parse;
rocambole.parseContext = espree;

rocambole.parseContext:Object

Sets the context (this value) of the parseFn. Defaults to esprima.

rocambole.parseOptions:Object

Sets the default options passed to parseFn.

// default values
rocambole.parseOptions = {
    // we need range/tokens/comment info to build the tokens linked list!
    range: true,
    tokens: true,
    comment: true
};

rocambole.moonwalk(ast, callback)

The moonwalk() starts at the leaf nodes and go down the tree until it reaches the root node (Program). Each node will be traversed only once.

rocambole.moonwalk(ast, function(node){
    if (node.type == 'ArrayExpression'){
        console.log( node.depth +': '+ node.toString() );
    }
});

Traverse order:

 Program [#18]
 `-FunctionDeclaration [#16]
   |-BlockStatement [#14]
   | |-IfStatement [#12]
   | | |-BynaryExpression [#9]
   | | | |-Identifier [#4]
   | | | `-Literal [#5]
   | | `-BlockStatement [#10]
   | |   `-ExpressionStatement [#6]
   | |     `-AssignmentExpression [#3]
   | |       |-Identifier [#1 walk starts here]
   | |       `-Literal [#2]
   | `-VariableDeclaration [#13]
   |   `-VariableDeclarator [#11]
   |     |-Identifier [#7]
   |     `-Literal [#8]
   `-ReturnStatement [#17]
     `-Identifier [#15]

This behavior is very different from node-falafel and node-burrito.

rocambole.walk(ast, callback)

It loops through all nodes on the AST starting from the root node (Program), similar to node-falafel.

rocambole.walk(ast, function(node){
    console.log(node.type);
});

Popular Alternatives

Unit Tests

Besides the regular unit tests we also use istanbul to generate code coverage reports, tests should have at least 95% code coverage for statements, branches and lines and 100% code coverage for functions or travis build will fail.

We do not run the coverage test at each call since it slows down the performnace of the tests and it also makes it harder to see the test results. To execute tests and generate coverage report call npm test --coverage, for regular tests just do npm test.

Coverage reports are not committed to the repository since they will change at each npm test --coverage call.

License

MIT

More Repositories

1

js-signals

Custom Event/Messaging system for JavaScript inspired by AS3-Signals
JavaScript
1,959
star
2

crossroads.js

JavaScript Routes
JavaScript
1,445
star
3

esformatter

ECMAScript code beautifier/formatter
JavaScript
970
star
4

requirejs-plugins

RequireJS Plugins
JavaScript
932
star
5

Hasher

Browser history manager for rich media websites
JavaScript
492
star
6

mdoc

node.js markdown powered documentation generator
CSS
202
star
7

amd-utils

modular JavaScript utilities written in the AMD format
JavaScript
143
star
8

vim-statline

Add useful informations to Vim statusline
Vim Script
83
star
9

gh-markdown-cli

Node.js command-line tool to batch convert Markdown files into HTML
JavaScript
81
star
10

requirejs-hogan-plugin

RequireJS Hogan / Mustache Plugin
JavaScript
77
star
11

nodefy

convert AMD modules into a node.js compatible format
JavaScript
64
star
12

disparity

colorized string diff (char or unified) ideal for text/code that spans through multiple lines
JavaScript
56
star
13

vim-esformatter

run esformatter from inside vim (automatically formats JavaScript source code)
Vim Script
34
star
14

basis.css

A base CSS stylesheet for rapid development
31
star
15

CompoundSignal

Special Signal type used to listen for multiple Signals dispatches
JavaScript
31
star
16

node-ant

Experimental Apache Ant adapter for node.js
Shell
27
star
17

esformatter-quotes

esformatter plugin: enforces coding style that string literals are delimited with single or double quotes
JavaScript
26
star
18

domr

fast and async DOM updates for modern browsers
JavaScript
21
star
19

swffit

swffit repository
JavaScript
17
star
20

SVGParser

AS3 SVG parser to FIVe3D and HTML5 canvas
ActionScript
16
star
21

MM_js_lib

My Modular JavaScript Library
JavaScript
16
star
22

trainwreck.js

JavaScript method chaining made easy
JavaScript
15
star
23

MM_boilerplate

My Basic Project Boilerplate
JavaScript
13
star
24

CanvasContext2DWrapper

enable Canvas method chaining without overwritting any native objects/prototypes
JavaScript
13
star
25

esindent

[deprecated] ECMAScript code indenter based on Esprima AST and Rocambole
JavaScript
6
star
26

rocambole-token

Helpers for rocambole AST token manipulation
JavaScript
5
star
27

prototype-week-view

prototype for gaia calendar v2.1
JavaScript
5
star
28

Hasher_AS3_helper

Actionscript 3 helper classes to use together with Hasher.js
JavaScript
4
star
29

rtf.js

JavaScript RTF document generator
4
star
30

rocambole-node

Helpers for rocambole AST node manipulation
JavaScript
4
star
31

esformatter-semicolon-first

esformatter plugin to add semicolon before `[` and `(` if they are the first things on the line
JavaScript
3
star
32

esformatter-flow

esformatter plugin: enforces coding style for Flow type annotations
JavaScript
3
star
33

MM_php_lib

My Modular PHP5 Library
PHP
3
star
34

MM_as3_lib

My Modular AS3 Library
ActionScript
3
star
35

eclipse_monkey_scripts

Scripts for extending Eclipse and Aptana functionalities through the Eclipse Monkey plugin.
JavaScript
3
star
36

SceneGraph.js

[abandoned] JavaScript implementation of a Scene Graph (a.k.a. display list) initially targeting HTML5 canvas but with plans to support other rendering engines in the future.
3
star
37

esformatter-parser

JavaScript parser used by esformatter (babylon+rocambole)
JavaScript
1
star
38

rocambole-linebreak

helpers to manipulate rocambole AST line break tokens
JavaScript
1
star
39

rocambole-whitespace

helpers for rocambole AST whitespace manipulation
JavaScript
1
star
40

YUI_to_Jasmine

Simple tool to convert YUI Test assertions into Jasmine expect() calls
JavaScript
1
star
41

rocambole-indent

helpers for rocambole AST indentation
JavaScript
1
star